在android服务中,我创建了用于执行某些后台任务的线程。
我遇到一种情况,线程需要在主线程的消息队列上发布某些任务,例如Runnable。一种获取主线程的Handler并从其他线程发布Message / Runnable的方法?

评论

您还可以使用自定义广播接收器。...在此处尝试我的回答,[内部广播接收器] [1] [1]:stackoverflow.com/a/22541324/1881527

有很多方法。除了David的回答和dzeikei在他的回答中的评论之外,(3)您可以使用广播接收器,或者(4)在用于启动服务的Intent的额外内容中传递处理程序,然后使用getIntent()在服务内部检索主线程的处理程序。 ).getExtras()。

@ sazzad-hissain-khan,为什么要用kotlin标记在2012年以来用Java的大部分答案标记这个问题?

#1 楼

注意:此答案已引起广泛关注,我需要对其进行更新。自从原始答案发布以来,@ dzeikei的评论几乎和原始答案一样受到关注。因此,这里有两种可能的解决方案:

1。如果您的后台线程引用了Context对象:

请确保您的后台工作线程可以访问Context对象(可以是Application上下文或Service上下文)。然后只需在后台工作线程中执行此操作:

// Get a handler that can be used to post to the main thread
Handler mainHandler = new Handler(context.getMainLooper());

Runnable myRunnable = new Runnable() {
    @Override 
    public void run() {....} // This is your code
};
mainHandler.post(myRunnable);


2。如果您的后台线程没有(或不需要)Context对象

(@dzeikei建议):

// Get a handler that can be used to post to the main thread
Handler mainHandler = new Handler(Looper.getMainLooper());

Runnable myRunnable = new Runnable() {
    @Override 
    public void run() {....} // This is your code
};
mainHandler.post(myRunnable);


评论


谢谢David,它为我解决了问题,如果您可以帮助我,还可以做一件事,如果我扩展Handler和impl handleMessage()会阻止主线程处理其消息吗?这只是出于好奇。

–艾哈迈德(Ahmed)
2012年6月20日19:03

不能。如果您将Handler子类化(或使用Handler.Callback接口),则将仅为使用处理程序发布的消息调用handleMessage()方法。主线程使用其他处理程序来发布/处理消息,因此没有冲突。

– David Wasser
2012年6月20日19:22

我相信,如果您使用Handler mainHandler = new Handler(Looper.getMainLooper()),您甚至不需要上下文。

– dzeikei
13年5月5日,下午3:57

小点;您的代码不在当前的位置。它应该是新的Runnable(){@ Override public void run(){....}};

–Richard Tingle
2014年7月31日上午10:19

@SagarDevanga这不是提出其他问题的正确位置。请发布一个新问题,而不是对无关答案的评论。这样您将获得更好,更快的响应。

– David Wasser
2015年1月21日,9:10

#2 楼

正如下面的评论者正确指出的那样,这不是服务的通用解决方案,仅适用于从您的活动启动的线程(服务可以是这样的线程,但并非所有都是这样的)。
关于服务的复杂主题活动通信,请阅读正式文档的整个服务部分-这很复杂,因此需要了解以下基本知识:
http://developer.android.com/guide/components/services.html#Notifications
以下方法可能在最简单的情况下起作用:
如果我正确理解了您的信息,则需要在应用程序的GUI线程中执行一些代码(不能考虑其他称为“主”线程的代码)。
为此,在Activity上有一种方法:
someActivity.runOnUiThread(new Runnable() {
        @Override
        public void run() {
           //Your code to run in GUI thread here
        }//public void run() {
});

文档:http://developer.android.com/reference/android/app/Activity.html#runOnUiThread%28java.lang。 Runnable%29
希望这是您想要的。

评论


OP表示他正在Service中运行线程。您不能在服务中使用runOnUiThread()。这个答案有误导性,没有解决所问的问题。

– David Wasser
13年11月27日在9:25

#3 楼

Kotlin版本
进行活动时,请使用
runOnUiThread {
    //code that runs in main
}

有活动上下文时,请使用mContext然后使用
mContext.runOnUiThread {
    //code that runs in main
}

当您处于无上下文的地方时可用,然后使用
Handler(Looper.getMainLooper()).post {  
    //code that runs in main
}


评论


不知道这是什么版本的Kotlin,但是我必须添加括号:runOnUiThread(){...}

– Wex
20-05-14在9:20

在Mac上使用Android Studio 4.1.1对我来说非常有效。

–金色拇指
20 Dec 12'在0:30



#4 楼

如果您无权访问Context,则还有另一种简单的方法。

1)。在主循环程序中创建处理程序:

Handler uiHandler = new Handler(Looper.getMainLooper());


2)。实现可运行的接口:

Runnable runnable = new Runnable() { // your code here }


3)。将您的Runnable发布到uiHandler:

uiHandler.post(runnable);


就这些;-)玩弄线程,但不要忘记同步它们。

#5 楼

如果您在线程中运行代码,例如确实延迟了某些操作,那么您需要从上下文中调用runOnUiThread。例如,如果您的代码在MainActivity类中,则使用此代码:

MainActivity.this.runOnUiThread(new Runnable() {
    @Override
    public void run() {
        myAction();
    }
});


如果可以从主(UI线程)或其他线程中调用您的方法,则需要像这样的支票:

public void myMethod() {
   if( Looper.myLooper() == Looper.getMainLooper() ) {
       myAction();
   }
   else {

}


评论


OP表示他正在Service中运行线程。您不能在服务中使用runOnUiThread()。

– David Wasser
13年11月27日在9:27

@DavidWasser在任何地方都有记录吗?方法文档未提及任何内容。 developer.android.com/reference/android/app/…

–格雷格·布朗(Greg Brown)
15年8月26日在12:49

@GregBrown正如到Activity文档的链接所指出的那样,runOnUiThread()是Activity的一种方法。它不是服务方法,因此您不能使用它。还有什么比这更清楚的了?

– David Wasser
16年8月4日在16:41

@DavidWasser足够公平。我什至不记得我为什么现在问这个问题(该问题已在大约一年前发布)。

–格雷格·布朗(Greg Brown)
16年8月7日在20:42

#6 楼

压缩代码块如下:

   new Handler(Looper.getMainLooper()).post(new Runnable() {
       @Override
       public void run() {
           // things to do on the main thread
       }
   });


这不涉及传递活动引用或应用程序引用。

科特琳等效项:

    Handler(Looper.getMainLooper()).post(Runnable {
        // things to do on the main thread
    })


#7 楼

最简单的方法,尤其是在没有上下文的情况下,如果您使用的是RxAndroid,则可以执行以下操作:

AndroidSchedulers.mainThread().scheduleDirect {
    runCodeHere()
}


#8 楼

使用处理程序的更精确的Kotlin代码:

Handler(Looper.getMainLooper()).post {  
 // your codes here run on main Thread
 }


#9 楼

我可以想到的一种方法是:

1)让UI绑定到服务。
2)通过注册您的BinderHandler公开一种类似于下面的方法:

public void registerHandler(Handler handler) {
    mHandler = handler;
}


3)在UI线程中,绑定到服务后调用上述方法:

mBinder.registerHandler(new Handler());


4 )使用服务线程中的处理程序来发布您的任务:

mHandler.post(runnable);


#10 楼

HandlerThread是Android中普通Java线程的更好选择。


创建HandlerThread并启动它
从HandlerThread使用Looper创建处理程序:requestHandler


post上的Runnable任务


与来自requestHandler的UI线程通信


为主线程创建带有HandlerThreadHandlerLooper并重写responseHandler方法
在其他线程的handleMessage任务中(在本例中为Runnable),在HandlerThread上调用sendMessage

responseHandler结果调用sendMessage中的handleMessage
responseHandler并对其进行处理,更新UI

示例:使用从Web服务接收的数据更新Message。由于应该在非UI线程上调用Web服务,因此请为网络操作创建TextView。从Web服务获取内容后,将消息发送到主线程(UI线程)处理程序,然后HandlerThread将处理该消息并更新UI。

示例代码:

HandlerThread handlerThread = new HandlerThread("NetworkOperation");
handlerThread.start();
Handler requestHandler = new Handler(handlerThread.getLooper());

final Handler responseHandler = new Handler(Looper.getMainLooper()) {
    @Override
    public void handleMessage(Message msg) {
        txtView.setText((String) msg.obj);
    }
};

Runnable myRunnable = new Runnable() {
    @Override
    public void run() {
        try {
            Log.d("Runnable", "Before IO call");
            URL page = new URL("http://www.your_web_site.com/fetchData.jsp");
            StringBuffer text = new StringBuffer();
            HttpURLConnection conn = (HttpURLConnection) page.openConnection();
            conn.connect();
            InputStreamReader in = new InputStreamReader((InputStream) conn.getContent());
            BufferedReader buff = new BufferedReader(in);
            String line;
            while ((line = buff.readLine()) != null) {
                text.append(line + "\n");
            }
            Log.d("Runnable", "After IO call:"+ text.toString());
            Message msg = new Message();
            msg.obj = text.toString();
            responseHandler.sendMessage(msg);


        } catch (Exception err) {
            err.printStackTrace();
        }
    }
};
requestHandler.post(myRunnable);


有用的文章:

处理线程以及为什么要在您的Android应用程序中使用它们

android-looper-handler-handlerthread-i

#11 楼

我知道这是一个老问题,但是我遇到了在Kotlin和Java中都使用过的一个主线程单层。这可能不是服务的最佳解决方案,但是对于调用将改变片段内部的UI的东西而言,这非常简单和明显。

Java(8):

 getActivity().runOnUiThread(()->{
      //your main thread code
 });


科特琳:

this.runOnUiThread {
     //your main thread code
}


#12 楼

因此,最方便的方法是执行以下操作:

import android.os.AsyncTask
import android.os.Handler
import android.os.Looper

object Dispatch {
    fun asyncOnBackground(call: ()->Unit) {
        AsyncTask.execute {
            call()
        }
    }

    fun asyncOnMain(call: ()->Unit) {
        Handler(Looper.getMainLooper()).post {
            call()
        }
    }
}


之后:

Dispatch.asyncOnBackground {
    val value = ...// super processing
    Dispatch.asyncOnMain { completion(value)}
}


#13 楼

请遵循此方法。使用这种方法,您可以简单地从后台线程更新UI。 runOnUiThread在main(UI)线程上工作。我认为此代码段不太复杂,也不容易,特别是对于初学者。

AsyncTask.execute(new Runnable() {
            @Override
            public void run() {

            //code you want to run on the background
            someCode();

           //the code you want to run on main thread
 MainActivity.this.runOnUiThread(new Runnable() {

                    public void run() {

/*the code you want to run after the background operation otherwise they will executed earlier and give you an error*/
                        executeAfterOperation();

                   }
                });
            }
        });


对于服务而言,

在其中创建处理程序oncreate

 handler = new Handler();


然后像这样使用它

 private void runOnUiThread(Runnable runnable) {
        handler.post(runnable);
    }


#14 楼

对于Kotlin,可以使用Anko corountines:

更新

doAsync {
   ...
}


已弃用的

async(UI) {
    // Code run on UI thread
    // Use ref() instead of this@MyActivity
}


#15 楼

public void mainWork() {
    new Handler(Looper.getMainLooper()).post(new Runnable() {
        @Override
        public void run() {
            //Add Your Code Here
        }
    });
}


这也可以在服务类中正常工作。

评论


尽管此代码可以回答问题,但您仍然可以考虑添加一些解释性的句子,因为这可以为其他用户增加答案的价值!

– MBT
18年8月23日在12:42

#16 楼

使用Kotlin,在任何函数中都就像这样:

runOnUiThread {
   // Do work..
}


评论


仅当您处于Activity上时。

– Farsheel
20年1月6日在6:04