Run Callback On Main Thread - android

I have some code that interacts with the Android Facebook SDK, Asynchronously. Unfortunately this means when it returns it is in a background thread.
Cocos-2dx prefers me to interact with it in the Main Thread, especially when doing things like telling the Director to switch scenes (As it involves Open GL)
Is there any way to get some code to run on the Main thread ?

As long as you have a Context, you can do something like this:
Handler mainHandler = new Handler(context.getMainLooper());
And to run code on UI thread:
mainHandler.post(new Runnable() {
#Override
public void run() {
// run code
}
});
As suggested by kaka:
You could also use the static Looper.getMainLooper() which
Returns the application's main looper, which lives in the main thread of the application.

runOnUiThread(new Runnable() {
#Override
public void run() {
//execute code on main thread
}
});

In C++:
Director::getInstance()->getScheduler()->performFunctionInCocosThread([]{
// execute code on main thread
});

You can run code in the main thread in this 2 ways: (with Java 8's lambdas)
If you have an activity instance:
activity.runOnUiThread(() -> {
// do your work on main thread
});
Otherwise use an Handler object and post a Runnable.
You can use the postDelayed version if you need some delay before executing the code.
Handler handler = new Handler(Looper.getMainLooper());
handler.post(() -> {
// do your work on main thread
});

Related

What is the difference between these methods for running code on UI Thread?

There are different methods posted on the web on how to run code on the UI thread. They all accomplish the same task, however, I really want to know the difference between these methods.
Method 1:
new Handler(Looper.getMainLooper()).post(new Runnable() {
#Override
public void run() {
// Code here will run in UI thread
}
});
Method 2:
new Handler().post(new Runnable() {
#Override
public void run() {
// Code here will run in UI thread
}
});
Method 3:
runOnUiThread(new Runnable() {
#Override
public void run() {
// Code here will run in UI thread
}
});
In Android, a Thread might have one Looper or MessageQueue. Handler is used to send Message or post Runnable to MessageQueue of a Thread, and it must always be associated with a Looper or a MessageQueue of a Thread.
Method 1
new Handler(Looper.getMainLooper()).post(new Runnable() {
#Override
public void run() {
// Code here will run in UI thread
}
});
When open an app, Android create a new thread (called main thread or UI thread) with a Looper and MessageQueue, this thread is used to render UI and process input events from users.
The above code is create a Handler and associated with Looper of UI thread, so the runnable will be queued to MessageQueue of UI thread, and will be executed later.
Method 2
new Handler().post(new Runnable() {
#Override
public void run() {
// Code here will run in UI thread
}
});
Creating a Handler and associated with Looper of current thread, there are 3 cases:
If this code is executed on UI thread, then the runnable will be queued to MessageQueue of UI thread and will be executed later.
If this code is executed on a background thread, if this thread has a Looper, then the runnable will be queued to MessageQueue of background thread and will be executed later.
If this code is executed on a background thread and this thread has no Looper, then an exception will be thrown.
Method 3
runOnUiThread(new Runnable() {
#Override
public void run() {
// Code here will run in UI thread
}
});
runOnUiThread is just a utility method of Activity, it used when you want to execute some code on UI thread. The logic behind this method is if current thread is UI thread, then execute it immediately, otherwise used Handler to post a message to MessageQueue of UI thread (like method 1).
Method 1 will always work.
Method 2 will only work if you're already on the UI thread- the new Handler without a Looper parameter creates a Handler to the current thread (and fails if there is no Looper on the current thread).
Method 3 needs to be done in an Activity or called on an Activity object, as runOnUiThread is a function of Activity. But under the hood it will do the same as 1 (although probably keeps a single Handler around to be more efficient, rather than always new-ing one).
All methods works like this:
Method 1 looping handler if loop exist
Method 2 handler can works in all activities if not private or wanted
Method 3 handler can work only in current activity

Looper.Loop() exception inside invoked method

I am invoking a method:
method = (MessageController.getInstance()).getClass().getMethod(data.getString("action") + "Action", cArg);
method.invoke(MessageController.getInstance(), "param1");
and the method:
public static void errorAction(String data){
ProgressDialog dialog = new ProgressDialog(context);
dialog.setTitle("hi");
dialog.setMessage("there");
dialog.show();
}
However i get the following exception:
Caused by: java.lang.RuntimeException: Can't create handler inside thread that has not called Looper.prepare()
on the dialog.show() part.
Is this because of invoking actually happens on a new thread?
If yes, how to make it run on UI thread? How to just show the dialog?
Thanks!
I'm not exactly sure why you're using reflection to do this, but yes. The reason is you're not on a Looper when invoking the show() method. More likely, you'll get another error if it isn't on the main looper thread (UI thread).
Handlers and Loopers go hand-in-hand. A Looper keeps a thread alive and running and a Handler executes Runnables and posts Messages on that thread.
So, to post to a main thread, you can create a new Handler yourself and pass in the main Looper which will ensure it gets executed on the main thread:
new Handler(Looper.getMainLooper()).post(new Runnable() {
#Override
public void run() {
// Code to execute on the main thread.
}
}
Doing it this way doesn't require an Activity or View. It will always post on the UI thread and not another Looper thread that you created. Note that this asynchronous and won't execute until the next draw pass.
Or you can run it in the UI thread, like so:
getActivity().runOnUiThread(new Runnable() {
#Override
public void run() {
method = (MessageController.getInstance()).getClass().getMethod(data.getString("action") + "Action", cArg);
method.invoke(MessageController.getInstance(), "param1");
}
});

How can I run something on the UI thread from my Application class

I need to run something inside my extends Application class on the UI thread
Handler h = new Handler();
h.postDelayed(new Runnable() {
#Override
public void run() {
//I need to run code here on the main thread
}
}, 1000);
do I need to pass the activity? It will be difficult since I am calling it from multiple activities
Methods called by Android in the Application class already run on the UI thread (a.k.a 'main' thread).
When in doubt you can use Thread.currentThread() to find out.
Remember to avoid doing costly operations on the main thread.
You need to call runOnUiThread
MainActivity.this.runOnUiThread(new Runnable() {
public void run() {
Log.d("UI thread", "I am the UI thread");
}
});

When to use handler.post() & when to new Thread()

I'm wondering when should I use handler.post(runnable); and when should I use
new Thread(runnable).start();
It is mentioned in developers documentation for Handler:
Causes the Runnable r to be added to the message queue. The runnable
will be run on the thread to which this handler is attached.
Does this mean if I write in the onCreate() of Activity class:
Handler handler = new Handler();
handler.post(runnable);
then runnable will be called in a separate thread or in the Activity's thread?
You should use Handler.post() whenever you want to do operations on the UI thread.
So let's say you want to change a TextView's text in the callback. Because the callback is not running on the UI thread, you should use Handler.post().
In Android, as in many other UI frameworks, UI elements (widgets) can be only modified from UI thread.
Also note that the terms "UI thread" and "main thread" are often used interchangeably.
Edit: an example of the long-running task:
mHandler = new Handler();
new Thread(new Runnable() {
#Override
public void run () {
// Perform long-running task here
// (like audio buffering).
// You may want to update a progress
// bar every second, so use a handler:
mHandler.post(new Runnable() {
#Override
public void run () {
// make operation on the UI - for example
// on a progress bar.
}
});
}
}).start();
Of course, if the task you want to perform is really long and there is a risk that user might switch to some another app in the meantime, you should consider using a Service.
To answer you specific question:
Does this mean if in the onCreate of Activity class I write:
Handler handler = new Handler() hanlder.post(runnable); then, runnable
will be called in a separate thread or on the Activity's thread?
No it won't be. The Runnable will be called on the Main Thread itself.
Handler is simply used for posting a message to the thread to which it is attached (where its is created).
It does not create a thread on its own.
In your example, you created a Handler in the main Thread (that where Activity.OnCreate() is called) and hence any message posted on such a Handler will be run on the Main Thread only.
Example is jacked:
mHandler = new Handler();
new Thread(new Runnable(){
#Override
public void run () {
mHandler.post(new Runnable() {
#Override
public void run () {
mUiView.setX(x);
}
});
}
}).start();
Alternatively you can skip the handler and use the post method on the view directly:
new Thread(new Runnable(){
#Override
public void run () {
mUiView.post(new Runnable() {
#Override
public void run () {
mUiView.setX(x);
}
});
}
}).start();
This is a good post that outlines the difference: What exactly does the post method do?
use handler.post() when you want to post the code (usually from background thread) to the main thread. Yea, POST,just like you, post a letter to someone. With the help of handler the code will be executed ASAP i.e. almost immediately.

Threading error "Can't create handler inside thread that has not called Looper.Prepare" with Android

I am currently developing an Android app where I need to perform a method inside a thread. I have the following code in order to create the thread and perform the
new Thread(new Runnable() {
#Override
public void run() {
new DownloadSync(getApplicationContext());
}
}).start();
When I try to run this code it displays the error message in the log cat:
java.lang.RuntimeException: Can't create handler inside thread that has not called Looper.Prepare().
How can I fix this problem?
Use AsyncTask. Your situation is the reason for its existence. Also, read up on Painless Threading (which is mostly using AsyncTask).
The issue with your code is that something is trying to use a Handler internally in your new Thread, but nothing has told the Looper to prepare.
I had an earlier answer that was not very helpful - I've removed it and kept the relevant bit:
Make sure your DownLoadSync object only communicates with the main UI thread via a handler (if you want to avoid using an AsyncTask)
Declare a runnable and a handler as shown:
final Runnable updateMainUIThread = new Runnable() {
public void run() {
Toast.makeText(getBaseContext(), "Communicating with the main thread!!", Toast.LENGTH_LONG).show();
}
};
private final Handler handler = new Handler();`
And use it like this:
handler.post(updateMainUIThread);

Categories

Resources