Android context thread checking - android

As far as I know the UI thread is the main thread.
getMainLooper according to the documentation is the UI thread.
The Application context thread shouldn't be the UI thread as well?
Also, the Fragment's Looper thread shouldn't be the UI thread as well?
Why the following fails (both)?
if (getActivity().getApplicationContext().getMainLooper().getThread() == Looper.getMainLooper().getThread()) {
throw new Exception("Context is not the application's thread");
}
//Inside a Fragment
if (new Handler().getLooper().getThread() == Looper.getMainLooper().getThread()) {
throw new Exception("Handler is not for the main thread");
}

i do not understand your question but i think my answer makes it clear:
getActivity().getApplicationContext().getMainLooper().getThread() returns the thread associated to UI thread or equivalently main thread.
Looper.getMainLooper().getThread() also returns above thread. so they both actually one object so the if condition is true that means:
"Context thread IS the application's thread"
if you are on main thread then new Handler().getLooper().getThread() returns exactly above thread. and also Looper.getMainLooper().getThread() so the two refrences are actually point to one object so the condition is true and
The handler thread IS the main thread
you can easily check those if statements. :-)
Why the following fails (both)?
No, i think you get confused the exception meaning. you throw the exception if the condition is true and the condition is true. that is not the same meaning of android exception this is your custom one and it throws correctly because the condition is satisfied.

Related

Thread completion in Android

I have some kotlin code like the following in an android app:
Thread(Runnable {
while (true) {
Thread.sleep(100)
... do useful work ...
if (checkCurrentCondition()) {
... do final useful work ...
return
}
}
}).start()
Here is my question: Is there some way (a callback function or some other mechanism) by which I can know in the main thread, that the background thread above has finished its job?
You may use AsyncTask or ThreadPoolExecutor. AsyncTask has the onPostExecute() method using which you can get the execution result right into UI Thread and do your job after. Also there is Looper/Handler usages for ThreadPoolExecutor to communicate between UI/Worker threads.
For detailed information's look here
If this is in an Activity, you could call runOnUiThread { doWhatever() } from the Thread - it'll hold a reference to the Activity, so don't do this in a long-running thread.
If you create a Handler with Handler(Looper.getMainLooper()) then you can post Runnables to it - basically the same as runOnUiThread except you're not holding onto an Activity, and you can do things like delay and cancel those posted messages.
If you already have some looping activity going on (so you can basically check if the thread's completed) then you could do anything from having a boolean done flag that the thread sets (make it #Volatile so the main thread sees the change) to all the stuff in java.util.concurrent like Futures and the like. Really depends on what you're doing.
Posting a Runnable to the main thread looper is the easiest general way to make a thing happen. But again, if that thread is creating any data in memory that the main thread needs to see, as always you need to handle synchronization, which is its own topic!

Robolectric Cannot invoke setValue on a background thread

I am testing an AsyncTask that onPostExecute calls setValue of a LiveData instance. Since I am invoking setValue from onPostExecute no issues were expected regarding the invocation being done by the UI thread.
Yet running this in a Robolectric unit test I got: java.lang.IllegalStateException: Cannot invoke setValue on a background thread
To make this unit test wait for background and foreground tasks completion I take advantage of awaitility tool in the following way:
var cf = new CompletableFuture<T>();
livedata.observe(ctrl.get(), it -> cf.complete(it));
// ... perform the AsyncTask that will update livedata in onPostExecute
await().until(() -> {
flushBackgroundThreadScheduler()
flushForegroundThreadScheduler()
cf.isDone
});
This is throwing an IllegalStateException: Cannot invoke setValue on a background thread on flushForegroundThreadScheduler() call!!!!
Why I am getting this exception? And how can I have the onPostExecute being performed like in the UI thread?
UPDATE
Logging threads it seems that both flushBackgroundThreadScheduler() and flushForegroundThreadScheduler() are executed synchronously inline. I can observe:
LiveData created on thread 763324286
Background thread 1519527121
LiveData updated on thread 1519527121
Since the lambda passed to await.until runs on another thread, then both flushBackgroundThreadScheduler() and flushForegroundThreadScheduler() are performed on that thread 1519527121.
Thus, I can solve my problem with the following workaround running in the test thread corresponding to UI Thread. Yet, I need that Thread.sleep() to succeed and I don't like it.
Thread.sleep(1000)
flushBackgroundThreadScheduler()
flushForegroundThreadScheduler()
cf.isDone
We must have the following considerations into account regarding:
Robolectric: flushForegroundThreadScheduler() is executed synchronously inline.
Awaitility: await.until(<Callable>) evaluates the until condition in a background thread.
From these two statements we observe that invoking flushForegroundThreadScheduler() from the Callable passed to await.until(<Callable>) results in the invocation of scheduled foreground tasks in a background thread, which answers the first OP question: Why I am getting this exception?
Answering the second OP question:
how can I have the onPostExecute being performed like in the UI thread?
Since Robolectric shares a single thread for both UI operations and Test code, then we must use pollInSameThread to instruct that the until condition should be evaluated on the same thread as the test case that starts Awaitility. The OP sample code should be fixed to:
await().pollInSameThread().until(() -> {
flushBackgroundThreadScheduler()
flushForegroundThreadScheduler()
cf.isDone
});

Android - myLooper() vs getMainLooper()

Just clarifying but in an Android activity on MAIN Thread if I call Looper.myLooper() vs Looper.getMainLooper() the return the same reference, right? they are the same thing? I know I would never have to call these usually as Android takes care of this but I'd like to know how they differ when being called from the main thread?
if from the main thread I call
Looper.myLooper().quit();
// or
Looper.getMainLooper().quit();
They both give the same runtime exception so I'm assuming they are the same reference:
Caused by: java.lang.RuntimeException: Main thread not allowed to quit.
can anyone confirm?
You have it described in the docs:
getMainLooper()
Returns the application's main looper, which lives in the main thread of the application.
myLooper()
Return the Looper object associated with the current thread. Returns null if the calling thread is not associated with a Looper.
As for whether getMainLooper() is of any use, I can assure you it really is. If you do some code on a background thread and want to execute code on the UI thread, e.g. update UI, use the following code:
new Handler(Looper.getMainLooper()).post(new Runnable() {
// execute code that must be run on the UI thread
});
Of course, there are other ways of achieving that.
Another use is if you want to check if the currently executed code is running on the UI thread, e.g. you want to throw/assert:
boolean isUiThread = Looper.getMainLooper().getThread() == Thread.currentThread();
or
boolean isUiThread = Looper.getMainLooper().isCurrentThread();
Looper.getMainLooper() is convenience API to get looper which is attached to the main thread of the activity.It is usefull when you want to excute some code on main thread from a background thread.
It is usually used as follows:
new Handler(Looper.getMainLooper()).post(task);
Looper.myLooper() is api to get looper attached to current thread
If you call these two methods in the main thread, they are the same object! You can find answers in the source code of ActivityThread.java, Looper.java and ThreadLocal.java.

Understanding what Looper is about in Android

I had to add Looper to the following code:
public class MyRunnable implements Runnable
{
#Override
public void run()
{
Looper.prepare();
final Looper looper = Looper.myLooper();
new Handler().postDelayed(
new Runnable()
{
#Override
public void run()
{
try
{
}
catch (Exception ex)
{
}
finally
{
looper.quit();
}
}
}, 100);
Looper.loop();
}
}
Notice that I have a runnable inside a runnable. The nested runnable gets executed through a Handler. Initially I didn't have Looper but Android complained that I needed to call Looper.prepare before executing another thread.
I read up on Looper but it still seems kind of cryptic. It seems to act like some kind of internal messaging pipeline. It isn't clear to me why this is necessary since there are no messages going from my outer runnable to my inner runnable. Even though that is true, it seems that Android just makes the hard rule that if you call a thread from a thread, you MUST also call Looper.prepare. Even if I accept that as-is, it still doesn't help to understand why I need to call looper.loop and looper.quit. If I omit Looper.loop, my Handler never runs and that is what isn't clear. What does Looper.loop do that allows my Handler to run?
Here is a great article about that.
Looper and Handler in Android
It comes along with a simple schema that leads to straight understanding of relationship between Loopers and Handler.
On this schema, we see that, within the same thread (depicted by the big rectangle), no matter how many handler you create, they will all be using the same Looper, i.e., the unique looper of this thread.
Note:
Looper have to be prepared to allow associated handler to process posted messages.
Android application, more precisely, android app UI thread(the main thread), already comes with a prepared looper (the mainLooper).
Here is how to Communicating with the UI Thread.
A simple concept of the looper:
Every worker thread you create and run ends once it performs its last operation.
To prevent your thread termination you can start a loop by calling Looper.loop(), think of it as while(true){} statement. Before calling Looper.loop() you have to prepare the loop with Looper.prepare(), if it is not prepared yet.
To terminate the loop and end your thread you will need to call looper.quit() on the looper.
Now for the notification you got from Android:
When you create a Handler in a thread, it will be bound to the thread it is created in and when you post runnable using this Handler, the code runs on the thread of the Handler.
So when the system saw that you want to run some code (especially 100ms in future) on a Handler that is bound to a thread that is going to die as soon as it finishes calling the post method it proposed to use Looper.loop() to prevent this thread from terminating and thus enabling you properly run the second Runnable in a still existing thread.
I find the following tutorial very helpful in understanding the concept of looper .
Intro to looper and handler

runOnUiThread vs Looper.getMainLooper().post in Android

Can anyone tell me if there's any difference between using runOnUiThread() versus Looper.getMainLooper().post() to execute a task on the UI thread in Android??
The only thing I can determine is that since runOnUiThread is a non-static Activity method, Looper.getMainLooper().post() is more convenient when you need to code something in a class that can't see the Activity (such as an interface).
I'm not looking for a discussion on WHETHER something should be executed on the UI thread, I get that some things can't and a great many things shouldn't, however, some things (like starting up an AsyncTask) MUST be executed from the UI thread.
The following behaves the same when called from background threads:
using Looper.getMainLooper()
Runnable task = getTask();
new Handler(Looper.getMainLooper()).post(task);
using Activity#runOnUiThread()
Runnable task = getTask();
runOnUiThread(task);
The only difference is when you do that from the UI thread since
public final void runOnUiThread(Runnable action) {
if (Thread.currentThread() != mUiThread) {
mHandler.post(action);
} else {
action.run();
}
}
will check if the current Thread is already the UI thread and then execute it directly. Posting it as a message will delay the execution until you return from the current UI-thread method.
There is also a third way to execute a Runnable on the UI thread which would be View#post(Runnable) - this one will always post the message even when called from the UI thread. That is useful since that will ensure that the View has been properly constructed and has a layout before the code is executed.

Categories

Resources