368

在一个 android 服务中,我创建了线程来执行一些后台任务。

我有一个线程需要在主线程的消息队列上发布某些任务的情况,例如Runnable.

有没有办法从我的另一个线程获取Handler主线程并发布Message/到它?Runnable

4

17 回答 17

681

注意:这个答案引起了如此多的关注,我需要更新它。自从发布了原始答案以来,@dzeikei 的评论几乎与原始答案一样受到关注。所以这里有两种可能的解决方案:

1.如果你的后台线程有一个Context对象的引用:

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

// 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);
于 2012-06-20T17:49:22.887 回答
154

正如下面的评论者正确指出的那样,这不是服务的通用解决方案,仅适用于从您的活动启动的线程(服务可以是这样的线程,但并非所有这些都是)。关于服务活动通信的复杂主题,请阅读官方文档的整个服务部分 - 它很复杂,因此了解基础知识是值得的:http: //developer.android.com/guide/components/services.html #通知

下面的方法可能适用于最简单的情况:

如果我理解正确,您需要在应用程序的 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

希望这是您正在寻找的。

于 2012-06-20T22:19:47.087 回答
63

Kotlin 版本

您进行活动时,请使用

runOnUiThread {
    //code that runs in main
}

您有活动上下文时, mContext 然后使用

mContext.runOnUiThread {
    //code that runs in main
}

当您在没有可用上下文的地方时,请使用

Handler(Looper.getMainLooper()).post {  
    //code that runs in main
}
于 2019-07-02T12:14:03.843 回答
32

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

1)。从主循环器创建一个处理程序:

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

2)。实现一个 Runnable 接口:

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

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

uiHandler.post(runnable);

就是这样 ;-) 享受线程的乐趣,但不要忘记同步它们。

于 2015-07-03T07:15:47.033 回答
29

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

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

如果您的方法可以从主(UI 线程)或其他线程调用,则需要进行如下检查:

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

}
于 2013-09-27T09:27:29.960 回答
27

一个精简的代码块如下:

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

这不涉及传递 Activity 引用或 Application 引用。

Kotlin 等价物:

    Handler(Looper.getMainLooper()).post(Runnable {
        // things to do on the main thread
    })
于 2018-02-20T17:46:10.150 回答
5

最简单的方法,特别是如果你没有上下文,如果你使用 RxAndroid,你可以这样做:

AndroidSchedulers.mainThread().scheduleDirect {
    runCodeHere()
}
于 2018-10-30T03:54:01.370 回答
5

使用 handler 更精确的 Kotlin 代码:

Handler(Looper.getMainLooper()).post {  
 // your codes here run on main Thread
 }
于 2019-01-14T11:04:17.300 回答
4

我能想到的一种方法是:

1) 让 UI 绑定到服务。2)通过注册您
的公开如下方法:BinderHandler

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

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

mBinder.registerHandler(new Handler());

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

mHandler.post(runnable);
于 2012-06-20T16:41:01.240 回答
4

所以最方便的是做这样的事情:

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)}
}
于 2019-10-21T09:51:40.663 回答
4

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

爪哇 (8):

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

科特林:

this.runOnUiThread {
     //your main thread code
}
于 2018-06-14T22:05:26.857 回答
4

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

  1. 创建一个HandlerThread并启动它
  2. 从 HandlerThread创建一个带有Looper的处理程序:requestHandler
  3. post一个Runnable任务requestHandler

与 UI 线程的通信来自HandlerThread

  1. 为主线程创建一个Handlerwith Looper: responseHandler并覆盖handleMessage方法
  2. 其他线程的内部Runnable任务(在HandlerThread这种情况下),调用sendMessageresponseHandler
  3. sendMessage导致handleMessagein 的调用responseHandler
  4. 从中获取属性Message并对其进行处理,更新 UI

示例TextView使用从 Web 服务接收到的数据进行更新。由于应在非 UI 线程上调用 Web 服务,HandlerThread为网络操作创建。从 Web 服务获取内容后,将消息发送到主线程(UI 线程)处理程序,该处理程序Handler将处理消息并更新 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);

有用的文章:

handlerthreads-and-why-you-should-be-using-them-in-your-android-apps

android-looper-handler-handlerthread-i

于 2017-05-09T12:40:10.967 回答
2
ContextCompat.getMainExecutor(context).execute {
  // do something
}
于 2021-09-13T08:18:25.740 回答
1

对于 Kotlin,您可以使用Anko corountines

更新

doAsync {
   ...
}

已弃用

async(UI) {
    // Code run on UI thread
    // Use ref() instead of this@MyActivity
}
于 2018-09-17T08:00:49.903 回答
1

按照这个方法。使用这种方式,您可以简单地从后台线程更新 UI。runOnUiThread 在主(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);
    }
于 2018-02-19T13:42:53.833 回答
0
public void mainWork() {
    new Handler(Looper.getMainLooper()).post(new Runnable() {
        @Override
        public void run() {
            //Add Your Code Here
        }
    });
}

这也可以在没有问题的服务类中工作。

于 2018-08-23T12:24:21.853 回答
-1

使用 Kotlin,在任何函数中都是这样的:

runOnUiThread {
   // Do work..
}
于 2019-09-25T09:40:09.827 回答