简体   繁体   English

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

[英]Running code in main thread from another thread

In an android service I have created thread(s) for doing some background task.在 android 服务中,我创建了线程来执行一些后台任务。

I have a situation where a thread needs to post certain task on main thread's message queue, for example a Runnable .我有一个线程需要在主线程的消息队列上发布某些任务的情况,例如Runnable

Is there a way to get Handler of the main thread and post Message / Runnable to it from my other thread?有没有办法获取主线程的Handler并从我的另一个线程向它发布Message / Runnable

NOTE: This answer has gotten so much attention, that I need to update it.注意:这个答案受到了如此多的关注,我需要更新它。 Since the original answer was posted, the comment from @dzeikei has gotten almost as much attention as the original answer.自从发布原始答案以来,@dzeikei 的评论几乎与原始答案一样受到关注。 So here are 2 possible solutions:所以这里有两种可能的解决方案:

1. If your background thread has a reference to a Context object: 1. 如果你的后台线程有一个Context对象的引用:

Make sure that your background worker threads have access to a Context object (can be the Application context or the Service context).确保您的后台工作线程可以访问 Context 对象(可以是 Application 上下文或 Service 上下文)。 Then just do this in the background worker thread:然后在后台工作线程中执行此操作:

// 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. If your background thread does not have (or need) a Context object 2. 如果你的后台线程没有(或需要)一个Context对象

(suggested by @dzeikei): (由@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);

As a commenter below pointed correctly, this is not a general solution for services, only for threads launched from your activity (a service can be such a thread, but not all of those are).正如下面的评论者正确指出的那样,这不是服务的通用解决方案,仅适用于从您的活动启动的线程(服务可以是这样的线程,但并非所有这些都是)。 On the complicated topic of service-activity communication please read the whole Services section of the official doc - it is complex, so it would pay to understand the basics: http://developer.android.com/guide/components/services.html#Notifications关于服务-活动通信的复杂主题,请阅读官方文档的整个服务部分 - 它很复杂,因此了解基础知识是值得的: http : //developer.android.com/guide/components/services.html #通知

The method below may work in the simplest cases:下面的方法可能适用于最简单的情况:

If I understand you correctly you need some code to be executed in the GUI thread of the application (cannot think about anything else called "main" thread).如果我理解正确,您需要在应用程序的 GUI 线程中执行一些代码(无法考虑任何其他称为“主”线程的内容)。 For this there is a method on Activity :为此, Activity有一个方法:

someActivity.runOnUiThread(new Runnable() {
        @Override
        public void run() {
           //Your code to run in GUI thread here
        }//public void run() {
});

Doc: http://developer.android.com/reference/android/app/Activity.html#runOnUiThread%28java.lang.Runnable%29文档: http : //developer.android.com/reference/android/app/Activity.html#runOnUiThread%28java.lang.Runnable%29

Hope this is what you are looking for.希望这是你正在寻找的。

Kotlin versions Kotlin 版本

When you are on an activity , then use您进行活动时,请使用

runOnUiThread {
    //code that runs in main
}

When you have activity context , mContext then use您有活动上下文时, mContext 然后使用

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

When you are in somewhere where no context available , then use当您处于没有可用上下文的地方时,使用

Handler(Looper.getMainLooper()).post {  
    //code that runs in main
}

There is another simple way, if you don't have an access to the Context.如果您无权访问上下文,还有另一种简单的方法。

1). 1)。 Create a handler from the main looper:从主循环器创建一个处理程序:

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

2). 2)。 Implement a Runnable interface:实现 Runnable 接口:

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

3). 3)。 Post your Runnable to the uiHandler:将您的 Runnable 发布到 uiHandler:

uiHandler.post(runnable);

That's all ;-) Have fun with threads, but don't forget to synchronize them.这就是全部;-) 享受线程的乐趣,但不要忘记同步它们。

If you run code in a thread, eg do delaying some action, then you need to invoke runOnUiThread from the context.如果您在线程中运行代码,例如延迟某些操作,那么您需要从上下文调用runOnUiThread For example, if your code is inside MainActivity class then use this:例如,如果您的代码在MainActivity类中,则使用以下代码:

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

If your method can be invoked either from main (UI thread) or from other threads you need a check like:如果您的方法可以从主(UI 线程)或其他线程调用,则需要进行以下检查:

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

}

A condensed code block is as follows:一个精简的代码块如下:

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

This does not involve passing down the Activity reference or the Application reference.这不涉及向下传递活动引用或应用程序引用。

Kotlin Equivalent: Kotlin 等效项:

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

The simplest way especially if you don't have a context, if you're using RxAndroid you can do:最简单的方法,尤其是在没有上下文的情况下,如果您使用的是 RxAndroid,则可以执行以下操作:

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

More precise Kotlin code using handler :使用 handler 更精确的 Kotlin 代码:

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

One method I can think of is this:我能想到的一种方法是:

1) Let the UI bind to the service. 1) 让 UI 绑定到服务。
2) Expose a method like the one below by the Binder that registers your Handler : 2)通过注册您的HandlerBinder公开一个类似于下面的方法:

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

3) In the UI thread, call the above method after binding to the service: 3)在UI线程中,绑定服务后调用上述方法:

mBinder.registerHandler(new Handler());

4) Use the handler in the Service's thread to post your task: 4)使用服务线程中的处理程序发布您的任务:

mHandler.post(runnable);

HandlerThread is better option to normal java Threads in Android . HandlerThread是 Android 中普通 java 线程的更好选择。

  1. Create a HandlerThread and start it创建一个HandlerThread并启动它
  2. Create aHandler with Looper from HandlerThread : requestHandler从 HandlerThread 使用Looper创建一个处理程序requestHandler
  3. post a Runnable task on requestHandlerrequestHandlerpost一个Runnable任务

Communication with UI Thread from HandlerThreadHandlerThread与 UI 线程通信

  1. Create a Handler with Looper for main thread : responseHandler and override handleMessage method为主线程创建一个带有LooperHandlerresponseHandler并覆盖handleMessage方法
  2. Inside Runnable task of other Thread ( HandlerThread in this case), call sendMessage on responseHandler在其他线程(本例中为HandlerThread )的Runnable任务内部,在responseHandler上调用sendMessage
  3. This sendMessage result invocation of handleMessage in responseHandler .sendMessage的结果调用handleMessageresponseHandler
  4. Get attributes from the Message and process it, update UIMessage获取属性并进行处理,更新 UI

Example : Update TextView with data received from a web service.示例:使用从 Web 服务接收到的数据更新TextView Since web service should be invoked on non-UI thread, created HandlerThread for Network Operation.由于应该在非 UI 线程上调用 Web 服务,因此为网络操作创建了HandlerThread Once you get the content from the web service, send message to your main thread (UI Thread) handler and that Handler will handle the message and update UI.从 Web 服务获取内容后,将消息发送到主线程(UI 线程)处理程序,该Handler程序将处理消息并更新 UI。

Sample code:示例代码:

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

Useful articles:有用的文章:

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

android-looper-handler-handlerthread-i android-looper-handler-handlerthread-i

I know this is an old question, but I came across a main thread one-liner that I use in both Kotlin and Java.我知道这是一个老问题,但我遇到了一个在 Kotlin 和 Java 中都使用的主线程单行。 This may not be the best solution for a service, but for calling something that will change the UI inside of a fragment this is extremely simple and obvious.这可能不是服务的最佳解决方案,但对于调用会改变片段内 UI 的东西,这是非常简单和明显的。

Java (8):爪哇(8):

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

Kotlin:科特林:

this.runOnUiThread {
     //your main thread code
}

So most handy is to do sort of:所以最方便的是做一些事情:

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()
        }
    }
}

And after:之后:

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

Follow this method.按照这个方法。 Using this way you can simply update the UI from a background thread.使用这种方式,您可以简单地从后台线程更新 UI。 runOnUiThread work on the main(UI) thread . runOnUiThread 在主(UI)线程上工作。 I think this code snippet is less complex and easy, especially for beginners.我认为这个代码片段不那么复杂和简单,特别是对于初学者来说。

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

                   }
                });
            }
        });

in the case of a service在服务的情况下

create a handler in the oncreate在 oncreate 中创建一个处理程序

 handler = new Handler();

then use it like this然后像这样使用它

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

for Kotlin, you can use Anko corountines :对于 Kotlin,您可以使用Anko 协程

update更新

doAsync {
   ...
}

deprecated已弃用

async(UI) {
    // Code run on UI thread
    // Use ref() instead of this@MyActivity
}
public void mainWork() {
    new Handler(Looper.getMainLooper()).post(new Runnable() {
        @Override
        public void run() {
            //Add Your Code Here
        }
    });
}

This can also work in a service class with no issue.这也可以在服务类中正常工作。

With Kotlin, it is just like this inside any function:使用 Kotlin,在任何函数中都是这样的:

runOnUiThread {
   // Do work..
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM