从另一个线程在主线程中运行代码

Posted

技术标签:

【中文标题】从另一个线程在主线程中运行代码【英文标题】:Running code in main thread from another thread 【发布时间】:2012-06-22 19:54:35 【问题描述】:

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

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

有没有办法获取主线程的Handler 并从我的其他线程向其发布Message/Runnable

【问题讨论】:

您也可以使用自定义广播接收器....在这里尝试我的答案,[内部广播接收器][1] [1]:***.com/a/22541324/1881527 方法有很多。除了大卫的回答和 dzeikei 在他的回答中的评论外,(3)您可以使用广播接收器,或者(4)在用于启动服务的 Intent 的额外内容中传递处理程序,然后使用 getIntent( ).getExtras() . @sazzad-hissain-khan,为什么从 2012 年开始用 kotlin 标记标记这个问题? 【参考方案1】:
ContextCompat.getMainExecutor(context).execute 
  // do something

【讨论】:

【参考方案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

希望这是您正在寻找的。​​p>

【讨论】:

OP 说他正在Service 中运行线程。您不能在 Service 中使用 runOnUiThread()。此答案具有误导性,并未解决所提出的问题。【参考方案3】:

所以最方便的方法是:

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)

【讨论】:

AsyncTask 似乎被贬低了【参考方案4】:

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

runOnUiThread 
   // Do work..

【讨论】:

这仅适用于您在Activity 上。【参考方案5】:

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()... 在 Mac 上使用 Android Studio 4.1.1 非常适合我。【参考方案6】:

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

Java (8):

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

科特林:

this.runOnUiThread 
     //your main thread code

【讨论】:

【参考方案7】:

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

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

【讨论】:

【参考方案8】:

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

AndroidSchedulers.mainThread().scheduleDirect 
    runCodeHere()

【讨论】:

【参考方案9】:

对于 Kotlin,您可以使用 Anko corountines:

更新

doAsync 
   ...

已弃用

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

【讨论】:

【参考方案10】:
public void mainWork() 
    new Handler(Looper.getMainLooper()).post(new Runnable() 
        @Override
        public void run() 
            //Add Your Code Here
        
    );

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

【讨论】:

虽然此代码可能会回答问题,但您仍然可以考虑添加一些解释性句子,因为这会增加您的答案对其他用户的价值!【参考方案11】:

一个精简的代码块如下:

   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
    )

【讨论】:

【参考方案12】:

按照这个方法。使用这种方式,您可以简单地从后台线程更新 UI。 runOnUiThread 在主(UI)线程上工作。我认为这段代码 sn-p 不那么复杂和简单,尤其是对于初学者。

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);
    

【讨论】:

【参考方案13】:

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

    创建一个HandlerThread 并启动它 使用来自 HandlerThread 的Looper 创建一个Handler:requestHandler postRunnablerequestHandler 上的任务

与来自HandlerThread 的 UI 线程通信

    Looper 为主线程创建一个HandlerresponseHandler 并覆盖handleMessage 方法 在其他线程的Runnable任务内(在这种情况下为HandlerThread),在responseHandler上调用sendMessagesendMessage 导致在responseHandler 中调用handleMessage。 从Message获取属性并处理它,更新UI

示例:使用从 Web 服务接收到的数据更新 TextView。由于应该在非 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

【讨论】:

【参考方案14】:

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

1.如果您的后台线程引用了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);

【讨论】:

谢谢大卫,它为我解决了,如果你能帮助我,还有一件事,如果我扩展 Handler 和 impl handleMessage() 会阻止主线程处理它的消息吗?这只是一个好奇的问题.. 没有。如果您将Handler 子类化(或使用Handler.Callback 接口),则您的handleMessage() 方法将仅用于已使用您的处理程序发布的消息。主线程使用不同的处理程序来发布/处理消息,因此没有冲突。 我相信如果你使用Handler mainHandler = new Handler(Looper.getMainLooper());,你甚至不需要上下文 小点;您的代码没有到达 ... 当前所在的位置。应该是new Runnable()@Override public void run() ....; @SagarDevanga 这不是提出不同问题的正确地方。请发布一个新问题,而不是对无关答案的评论。这样你会得到更好更快的响应。【参考方案15】:

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

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

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

2)。实现一个 Runnable 接口:

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

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

uiHandler.post(runnable);

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

【讨论】:

【参考方案16】:

如果您在线程中运行代码,例如做延迟一些动作,那么你需要从上下文中调用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 中运行线程。您不能在 Service 中使用 runOnUiThread() @DavidWasser 是否记录在任何地方?方法文档没有提到任何关于它的内容。 developer.android.com/reference/android/app/… @GregBrown 正如您通过指向Activity 文档的链接所指出的那样,runOnUiThread()Activity 的一种方法。它不是Service 的方法,因此您不能使用它。还有什么比这更清楚的呢? @DavidWasser 够公平的。我什至不记得为什么我现在问这个问题(它是在差不多一年前发布的)。【参考方案17】:

我能想到的一种方法是这样的:

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

public void registerHandler(Handler handler) 
    mHandler = handler;

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

mBinder.registerHandler(new Handler());

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

mHandler.post(runnable);

【讨论】:

以上是关于从另一个线程在主线程中运行代码的主要内容,如果未能解决你的问题,请参考以下文章

从 boost 线程在主线程上运行一个函数并将参数传递给该函数

在主线程开始运行之前捕获新的进程事件

swift 在主线程上运行代码

Xamarin - 在主线程以外的线程上加载数据

在主窗体关闭并且不等待线程完成后让线程运行

在后台线程执行硬任务,在主线程返回结果