在一个 android 服务中,我创建了线程来执行一些后台任务。
我有一个线程需要在主线程的消息队列上发布某些任务的情况,例如 Runnable
。
有没有办法获取主线程的 Handler
并将 Message
/Runnable
从我的另一个线程发布到它?
注意:这个答案引起了如此多的关注,我需要更新它。自从发布了原始答案以来,@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);
正如下面的评论者正确指出的那样,这不是服务的通用解决方案,仅适用于从您的活动启动的线程(服务可以是这样的线程,但并非所有这些都是)。关于服务活动通信的复杂主题,请阅读官方文档的整个服务部分 - 它很复杂,因此了解基础知识是值得的: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() {
});
希望这是您正在寻找的。
Service
中运行线程。您不能在 Service
中使用 runOnUiThread()
。这个答案具有误导性,并没有解决所提出的问题。
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(){...}
如果您无权访问 Context,还有另一种简单的方法。
1)。从主循环器创建一个处理程序:
Handler uiHandler = new Handler(Looper.getMainLooper());
2)。实现一个 Runnable 接口:
Runnable runnable = new Runnable() { // your code here }
3)。将您的 Runnable 发布到 uiHandler:
uiHandler.post(runnable);
就是这样 ;-) 享受线程的乐趣,但不要忘记同步它们。
如果您在线程中运行代码,例如延迟某些操作,那么您需要从上下文中调用 runOnUiThread
。例如,如果您的代码在 MainActivity
类中,则使用以下代码:
MainActivity.this.runOnUiThread(new Runnable() {
@Override
public void run() {
myAction();
}
});
如果您的方法可以从主(UI 线程)或其他线程调用,则需要进行如下检查:
public void myMethod() {
if( Looper.myLooper() == Looper.getMainLooper() ) {
myAction();
}
else {
}
Service
中运行线程。您不能在 Service
中使用 runOnUiThread()
。
Activity
文档的链接所指出的,runOnUiThread()
是 Activity
的一种方法。它不是 Service
的方法,因此您不能使用它。还有什么比这更清楚的呢?
一个精简的代码块如下:
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
})
我知道这是一个老问题,但我遇到了一个我在 Kotlin 和 Java 中都使用的主线程单线器。这可能不是服务的最佳解决方案,但对于调用将更改片段内的 UI 的东西来说,这是非常简单和明显的。
爪哇 (8):
getActivity().runOnUiThread(()->{
//your main thread code
});
科特林:
this.runOnUiThread {
//your main thread code
}
HandlerThread
是 Android 中普通 java 线程的更好选择。
创建一个 HandlerThread 并启动它 从 HandlerThread 创建一个带有 Looper 的 Handler :requestHandler 在 requestHandler 上发布一个 Runnable 任务
与 HandlerThread
的 UI 线程通信
为主线程创建一个带有 Looper 的 Handler:responseHandler 并覆盖 handleMessage 方法在其他线程的 Runnable 任务中(在本例中为 HandlerThread),在 responseHandler 上调用 sendMessage 这个 sendMessage 导致在 responseHandler 中调用 handleMessage。从消息中获取属性并处理它,更新 UI
示例:使用从网络服务接收到的数据更新 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
最简单的方法,特别是如果你没有上下文,如果你使用 RxAndroid,你可以这样做:
AndroidSchedulers.mainThread().scheduleDirect {
runCodeHere()
}
使用 handler 更精确的 Kotlin 代码:
Handler(Looper.getMainLooper()).post {
// your codes here run on main Thread
}
我能想到的一种方法是:
1) 让 UI 绑定到服务。
2) 通过注册您的 Handler
的 Binder
公开如下方法:
public void registerHandler(Handler handler) {
mHandler = handler;
}
3)在UI线程中,绑定服务后调用上述方法:
mBinder.registerHandler(new Handler());
4)使用服务线程中的处理程序发布您的任务:
mHandler.post(runnable);
所以最方便的是做这样的事情:
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)}
}
ContextCompat.getMainExecutor(context).execute {
// do something
}
按照这个方法。使用这种方式,您可以简单地从后台线程更新 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);
}
对于 Kotlin,您可以使用 Anko corountines:
更新
doAsync {
...
}
已弃用
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
}
});
}
这也可以在没有问题的服务类中工作。
使用 Kotlin,在任何函数中都是这样的:
runOnUiThread {
// Do work..
}
Activity
上时。
Handler
子类化(或使用Handler.Callback
接口),您的handleMessage()
方法只会被调用用于使用您的处理程序发布的消息。主线程使用不同的处理程序来发布/处理消息,因此没有冲突。Handler mainHandler = new Handler(Looper.getMainLooper());
,我相信您甚至不需要上下文new Runnable(){@Override public void run() {....}};