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


326

在android服务中,我创建了用于执行某些后台任务的线程。

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

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

谢谢,


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

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

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

Answers:


617

注意:此答案已引起广泛关注,我需要对其进行更新。自从原始答案发布以来,@ 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);

1
谢谢David,它为我解决了问题,如果您可以帮助我,还有另一件事,如果我扩展Handler和impl handleMessage()会阻止主线程处理其消息吗?这只是出于好奇而已。.–
艾哈迈德(Ahmed)

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

205
我相信,如果您使用的话,甚至都不需要上下文Handler mainHandler = new Handler(Looper.getMainLooper());
dzeikei

6
小点;您的代码不在当前的位置。应该是new Runnable(){@Override public void run() {....}};
Richard Tingle 2014年

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

138

正如下面的评论者正确指出的那样,这不是服务的通用解决方案,仅是针对从您的活动启动的线程(服务可以是这样的线程,但并非所有都是这样)。关于服务活动通信的复杂主题,请阅读官方文档的整个“服务”部分-它很复杂,因此有必要了解基本知识: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() {
});

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

希望这是您想要的。


20
OP说他正在A中运行线程Service。您不能runOnUiThread()在中使用Service。这个答案有误导性,没有解决所问的问题。
David Wasser

31

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

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

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

2)。实现一个Runnable接口:

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

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

uiHandler.post(runnable);

仅此而已;-)玩弄线程,但不要忘记同步它们。


29

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

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

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

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

}

7
OP说他正在A中运行线程Service。您不能runOnUiThread()在中使用Service
David Wasser

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

1
@GregBrown正如您在指向Activity文档的链接中所指出的,runOnUiThread()是一种方法Activity。它不是的方法Service,因此您不能使用它。还有什么比这更清楚的了?
大卫·瓦瑟

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

24

压缩代码块如下:

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

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

Kotlin等效:

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

20

Kotlin版本

您进行某项活动时,请使用

runOnUiThread {
    //code that runs in main
}

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

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

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

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

不知道这科特林版本的,但我不得不加括号:runOnUiThread(){...}
WEX

5

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

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


4

我能想到的一种方法是:

1)让UI绑定到服务。
2)公开一种Binder注册您的方法,如下所示Handler

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

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

mBinder.registerHandler(new Handler());

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

mHandler.post(runnable);

3

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

  1. 创建一个HandlerThread并启动它
  2. 从HandlerThread 用Looper创建一个HandlerrequestHandler
  3. post一项Runnable任务requestHandler

与UI线程通信 HandlerThread

  1. 为主线程创建一个Handlerwith LooperresponseHandler和override handleMessage方法
  2. 里面Runnable其他线程(任务HandlerThread在这种情况下),调用sendMessageresponseHandler
  3. 这个sendMessage结果调用handleMessageresponseHandler
  4. 从中获取属性Message并进行处理,更新UI

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

android-looper-handler-handlerthread-i


2

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

Java(8):

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

科特林:

this.runOnUiThread {
     //your main thread code
}

1

请遵循此方法。使用这种方法,您可以简单地从后台线程更新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);
    }

感谢您提供此代码段,它可能会提供一些有限的即时帮助。一个适当的解释,将大大提高其长期的价值通过展示为什么这是一个很好的解决了这个问题,并会使其与其他类似的问题,更有助于未来的读者。请编辑您的答案以添加一些解释,包括您所做的假设。
iBug

1

对于Kotlin,您可以使用Anko corountines

更新

doAsync {
   ...
}

不推荐使用

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

1

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

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

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

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


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

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.