Reputation: 15029
In an Android service, I have created thread(s) for doing some background task.
I have a situation where a thread needs to post a certain task on the main thread's message queue, for example a Runnable
.
Is there a way to get the Handler
of the main thread and post a Message
/Runnable
to it from my other thread?
Upvotes: 405
Views: 378159
Reputation: 71
Old thread, but thought I'd, add. These days, if you're using coroutines, a common way would be
scope.launch(Dispatchers.Main) {
/* code to be executed on main thread */
}
Upvotes: 0
Reputation: 40126
When you are on an activity, then use
runOnUiThread {
//code that runs in main
}
When you have activity context, mContext then use
mContext.runOnUiThread {
//code that runs in main
}
When you are in somewhere outside activity where no context available, then use
Handler(Looper.getMainLooper()).post {
//code that runs in main
}
Upvotes: 111
Reputation: 938
ContextCompat.getMainExecutor(context).execute {
// do something
}
Upvotes: 7
Reputation: 2435
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
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).
For this there is a method on Activity
:
someActivity.runOnUiThread(new Runnable() {
@Override
public void run() {
//Your code to run in GUI thread here
}//public void run() {
});
Hope this is what you are looking for.
Upvotes: 167
Reputation: 4798
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)}
}
Upvotes: 5
Reputation: 787
I know this is an old question, but I came across a main thread one-liner that I use in both Kotlin and 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.
Java (8):
getActivity().runOnUiThread(()->{
//your main thread code
});
Kotlin:
this.runOnUiThread {
//your main thread code
}
Upvotes: 9
Reputation: 2929
More precise Kotlin code using handler :
Handler(Looper.getMainLooper()).post {
// your codes here run on main Thread
}
Upvotes: 6
Reputation: 2145
The simplest way especially if you don't have a context, if you're using RxAndroid you can do:
AndroidSchedulers.mainThread().scheduleDirect {
runCodeHere()
}
Upvotes: 7
Reputation: 7104
for Kotlin, you can use Anko corountines:
update
doAsync {
...
}
deprecated
async(UI) {
// Code run on UI thread
// Use ref() instead of this@MyActivity
}
Upvotes: 2
Reputation: 11
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.
Upvotes: 1
Reputation: 2647
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:
Handler(Looper.getMainLooper()).post(Runnable {
// things to do on the main thread
})
Upvotes: 32
Reputation: 2518
Follow this method. Using this way you can simply update the UI from a background thread. runOnUiThread work on the main(UI) thread . 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
handler = new Handler();
then use it like this
private void runOnUiThread(Runnable runnable) {
handler.post(runnable);
}
Upvotes: 3
Reputation: 38910
HandlerThread
is better option to normal java Threads in Android .
requestHandler
post
a Runnable
task on requestHandler
Communication with UI Thread from HandlerThread
Handler
with Looper
for main thread : responseHandler
and override handleMessage
methodRunnable
task of other Thread ( HandlerThread
in this case), call sendMessage
on responseHandler
sendMessage
result invocation of handleMessage
in responseHandler
.Message
and process it, update UIExample: Update TextView
with data received from a web service. Since web service should be invoked on non-UI thread, created HandlerThread
for Network Operation. 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.
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
android-looper-handler-handlerthread-i
Upvotes: 8
Reputation: 95543
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. So here are 2 possible solutions:
1. If your background thread has a reference to a Context
object:
Make sure that your background worker threads have access to a Context object (can be the Application context or the Service context). 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
(suggested by @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);
Upvotes: 701
Reputation: 463
There is another simple way, if you don't have an access to the Context.
1). Create a handler from the main looper:
Handler uiHandler = new Handler(Looper.getMainLooper());
2). Implement a Runnable interface:
Runnable runnable = new Runnable() { // your code here }
3). Post your Runnable to the uiHandler:
uiHandler.post(runnable);
That's all ;-) Have fun with threads, but don't forget to synchronize them.
Upvotes: 34
Reputation: 8372
If you run code in a thread, e.g. do delaying some action, then you need to invoke runOnUiThread
from the context. For example, if your code is inside MainActivity
class then use this:
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:
public void myMethod() {
if( Looper.myLooper() == Looper.getMainLooper() ) {
myAction();
}
else {
}
Upvotes: 30
Reputation: 28687
One method I can think of is this:
1) Let the UI bind to the service.
2) Expose a method like the one below by the Binder
that registers your Handler
:
public void registerHandler(Handler handler) {
mHandler = handler;
}
3) In the UI thread, call the above method after binding to the service:
mBinder.registerHandler(new Handler());
4) Use the handler in the Service's thread to post your task:
mHandler.post(runnable);
Upvotes: 5