I understand that postInvalidate() is for non-UI threads. However calling postInvalidate() in the UI thread seems harmless and effectively equivalent. (The cost difference seems negligible.)
There is an advantage to always using postInvalidate(): you'll never get a crash when your code containing invalidate() one day makes its way into a non-UI thread. So why bother with invalidate()?
Related
I don't know is there any difference between CoroutineScope(Dispatchers.Main).launch and runOnUiThread, I think both will run on Main thread.
but still confusion is any difference there.?
Thanks.
Firstly, you can call runOnUiThread only in the context of Activity.
Secondly, you cannot call suspend functions from runOnUiThread, while you can do it in CoroutineScope(Dispatchers.Main).launch block.
Thirdly, runOnUiThread checks if the current ui thread is busy with other operations, if it is, the task is posted to an activity handler and will be executed when its turn will come.
They're different things using different technologies, and really it comes down to whether you're already using a coroutine or not. If so, just switch to the Main dispatcher when your code needs to be on the main thread. If you're not using coroutines, and you're explicitly using another thread, then you can use runOnUiThread to run some code on the main one. If you're not using coroutines or threads, then you don't need to call runOnUiThread at all!
There's nothing stopping you from using runOnUiThread from inside a coroutine - but it's not really the right tool for the job, you're stepping outside of coroutine handling unnecessarily. Plus like Steyrix says, you need access to something like an Activity to do it, which could mean holding a reference to one in a long-running coroutine when it should be garbage collected.
There are lots of other ways to get back on the main thread too, e.g. posting Runnables to a Handler (or through a View), or the postValue methods on LiveData. You don't have to switch dispatcher if there's something more concise and convenient available, it all depends on the situation
I just found out that some of my code is (unindentionally) running in a worker thread and reads some data from UI elements:
e.g. checkbox.isChecked(), textView.getText()
and to my surprise, this works just fine...
I expected that it would crash with an exception (like the following exception that I get when I want to write to UI elements):
android.view.ViewRootImpl$CalledFromWrongThreadException: Only the original thread that created a view hierarchy can touch its views.
According to the Android docs:
Only objects running on the UI thread have access to other objects on
that thread. Because tasks that you run on a thread from a thread pool
aren't running on your UI thread, they don't have access to UI
objects.
So, is it really okay to read data from UI elements from other threads?
If not: why is there no exception?
is it really okay to read data from UI elements from other threads?
No, but not for the reasons that you may be thinking.
First, as soon as you fork the background thread, the user might press BACK or otherwise do something that destroys your activity. It is not safe to try to use widgets of a destroyed activity.
Second, there is little to no thread synchronization logic in the View class or its subclasses. The main application thread may be modifying the data at the same time that you are trying to use it, resulting in possible concurrency issues.
I would pass the data from the widgets into the thread (e.g., thread constructor).
why is there no exception?
AFAIK, that specific exception is only thrown on setters or other things that modify the contents of the widget. This does not mean that any code that does not raise that exception is guaranteed to be safe.
You can't redraw (invalidate) your Views outside main thread which is also UI thread. Setting text for TextView also causes redrawing view ofc... But getting text just returns String (or Editable?), so its working...
Note that there are some other restrictions and limitations, e.g. when you pass EditText to some AsyncTask, but while it's working the main Activity (holding passed view) finish then you might get NPE, because view is also gone... In this and similar cases WeakReferences are very usefull
The following code in an AsycnTask:
#Override
protected Boolean doInBackground(View... params) {
try{
Drawable drawPhoto = DataDatero.ImageDownload(taskPhotoName);
((ImageView)params[0]).setImageDrawable(drawPhoto);
((TextView)params[1]).setText(taskItemListText);
((TextView)params[2]).setTextColor(taskColore);
((TextView)params[2]).setText(taskItemStockText);
[...]
}
Throws a CalledFromWrongThreadException , describing that:
Only the original thread that created a view hierarchy can touch its
views
This has been discussed in plenty of questions: example , another example ; and all reach the same conclusion.
But what I'm not only getting the Exception..
I call that execute from a getView in a List adapter, and the images (params[0]) are updated in the view while the exception is thrown for the text. Also, if I leave only the textview updates , the text is updated and no exception is thrown.
If I update the TextView first and the ImageView after, some text and images are updated while some images are not (that imageDownload includes a 2-3 sec network operation)
Why some of them are updated and others are not?
Notes: this was tested only with sdk 4.0, v4 support, on api 16 emulation.
I fixed it and I'm not touching the views in doInBackground
The second example is something similar... is it that the operation gets validated if onCreate is not finished?
I have come across a similar issue and asked a question here (self answered after a good bit of digging).
Essentially what it boils down to is that, contrary to what everybody thinks, you can modify UI elements from an AsyncTask execute() if those views haven't gone through a layout traversal yet. This happens asynchronously to the main flow of execution (activity lifecycle methods/callbacks), so if the View in question is created shortly before execute() is called, you can access them (meaning, the exception isn't thrown, it's of course still really bad practice and not advisable). Because execute() happens on another thread, the layout traversals (which run on the UI thread) may finish while your execute() is running, which explains why only some views may be modified and others throw the exception. It also explains why "leaving only the textview updates" (and presumably removing the ImageView updates) results in those updates "magically" working too. Since this is a timing related issue, it depends on many factors, among other things how long Drawable drawPhoto = DataDatero.ImageDownload(taskPhotoName); takes to run.
PS: I realise this is a late answer, but I think this can be useful for somebody finding this answer first, there aren't many posts dealing with issues like this.
The exception is clear enough. You can not update UI element from a thread different from the UI Thread. doInBackground executes code in a different thread
Why cant you pass the information to update the UI to the onPostExecute method? This is where the UI is intended to be updated.
When you run the execute method of your task, the doInBackground method is executed in a background thread.
And you are not allowed to modify UI from a background thread.
So, don't modify the UI in the doInBackground method.
You should do this UI stuff in onPostExecute method instead, which is guaranteed to be executed in UI thread.
So I am running some threads with a CountDownLatch.
My problem is that when I call latch.await() the UI seems to hang and even UI commands that were called beforehand have no effect. e.g.
btnShare.setVisibility(View.INVISIBLE);
prgSharing.setVisibility(View.VISIBILE);
latch.await()
The first two lines have no effect on the UI.
Any idea why this is and possibly a solution? Thanks.
This is most likely because you are blocking the UI-Thread before it can render the Views again. I think you should look at AsyncTask and maybe put your wait logic in the doInBackground() method, or somehow re-think your implementation.
if the UI hangs it is because you call:
latch.await()
on the UI Thread. You have to avoid blocking call on the UI Thread since those are the cause of ANR
I'm new to programming androids but I have quite a bit of experience programming blackberries.
I created an app that has an activity class (main.java) and a view class (game.java).
Inside the view class I have some bitmaps being drawn to the screen. I created a thread and I'm moving the images around in the thread. However when I call invalidate() inside the thread it never redraws the screen.
Are you not able to invalidate() the screen from a thread? I know the thread is running and the invalidate is being called, it just never makes the changes on the screen.
You have to use View.postInvalidate() if you call it from a non-UI thread.
According to docs:
public void postInvalidate ()
Since: API Level 1
Cause an invalidate to happen on a subsequent cycle through the event loop. Use this to invalidate the View from a non-UI thread.