在android服务中,我创建了用于执行某些后台任务的线程。
我遇到一种情况,线程需要在主线程的消息队列上发布某些任务,例如a Runnable
。
有没有办法从我的其他线程获取Handler
主线程并将其发布Message
/ 发布Runnable
到该主线程?
谢谢,
在android服务中,我创建了用于执行某些后台任务的线程。
我遇到一种情况,线程需要在主线程的消息队列上发布某些任务,例如a Runnable
。
有没有办法从我的其他线程获取Handler
主线程并将其发布Message
/ 发布Runnable
到该主线程?
谢谢,
Answers:
注意:此答案已引起广泛关注,我需要对其进行更新。自从原始答案发布以来,@ 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);
Handler
(或使用Handler.Callback
接口)handleMessage()
,则仅对使用处理程序发布的消息调用方法。主线程使用其他处理程序来发布/处理消息,因此没有冲突。
Handler mainHandler = new Handler(Looper.getMainLooper());
new Runnable(){@Override public void run() {....}};
正如下面的评论者正确指出的那样,这不是服务的通用解决方案,仅是针对从您的活动启动的线程(服务可以是这样的线程,但并非所有都是这样)。关于服务活动通信的复杂主题,请阅读官方文档的整个“服务”部分-它很复杂,因此有必要了解基本知识: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
希望这是您想要的。
Service
。您不能runOnUiThread()
在中使用Service
。这个答案有误导性,没有解决所问的问题。
如果您在线程中运行代码(例如延迟某些操作),则需要runOnUiThread
从上下文中调用。例如,如果您的代码在MainActivity
类内部,则使用以下代码:
MainActivity.this.runOnUiThread(new Runnable() {
@Override
public void run() {
myAction();
}
});
如果您的方法可以从主线程(UI线程)或从其他线程调用,则需要进行如下检查:
public void myMethod() {
if( Looper.myLooper() == Looper.getMainLooper() ) {
myAction();
}
else {
}
Service
。您不能runOnUiThread()
在中使用Service
。
Activity
文档的链接中所指出的,runOnUiThread()
是一种方法Activity
。它不是的方法Service
,因此您不能使用它。还有什么比这更清楚的了?
当您进行某项活动时,请使用
runOnUiThread {
//code that runs in main
}
当您有活动上下文时,然后使用mContext
mContext.runOnUiThread {
//code that runs in main
}
当您在没有上下文可用的地方时,请使用
Handler(Looper.getMainLooper()).post {
//code that runs in main
}
runOnUiThread(){...}
最简单的方法,尤其是在没有上下文的情况下,如果您使用的是RxAndroid,则可以执行以下操作:
AndroidSchedulers.mainThread().scheduleDirect {
runCodeHere()
}
我能想到的一种方法是:
1)让UI绑定到服务。
2)公开一种Binder
注册您的方法,如下所示Handler
:
public void registerHandler(Handler handler) {
mHandler = handler;
}
3)在UI线程中,绑定到服务后调用上述方法:
mBinder.registerHandler(new Handler());
4)使用服务线程中的处理程序来发布您的任务:
mHandler.post(runnable);
HandlerThread
是Android中普通java线程的更好选择。
requestHandler
post
一项Runnable
任务requestHandler
与UI线程通信 HandlerThread
Handler
with Looper
: responseHandler
和override handleMessage
方法Runnable
其他线程(任务HandlerThread
在这种情况下),调用sendMessage
上responseHandler
sendMessage
结果调用handleMessage
在responseHandler
。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);
有用的文章:
请遵循此方法。使用这种方法,您可以简单地从后台线程更新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);
}
对于Kotlin,您可以使用Anko corountines:
更新
doAsync {
...
}
不推荐使用
async(UI) {
// Code run on UI thread
// Use ref() instead of this@MyActivity
}
因此,最方便的是执行以下操作:
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)}
}