I have an app with a long running action. I am using AsyncTask. In its onPostExecute() I call a function that modifies some views. I am getting this error on calling setEnabled on the 5th view of 10 views being modified. And it happens very seldom, getting the odd error report from users.
Obviously a multithreading issue. But I thought onPostExecute() always runs on the UI thread? Could it be that the UI thread suddenly gets changed to another thread??
Stack Trace:
generated the following exception:
android.view.ViewRoot$CalledFromWrongThreadException: Only the original
thread that created a view hierarchy can touch its views.
--------- Instruction Stack trace ---------
android.view.ViewRoot.checkThread(ViewRoot.java:3041)
android.view.ViewRoot.invalidateChild(ViewRoot.java:647)
android.view.ViewRoot.invalidateChildInParent(ViewRoot.java:673)
android.view.ViewGroup.invalidateChild(ViewGroup.java:2511)
android.view.View.invalidate(View.java:5255)
android.view.View.invalidateDrawable(View.java:7293)
android.graphics.drawable.Drawable.invalidateSelf(Drawable.java:300)
8.
android.graphics.drawable.DrawableContainer.selectDrawable(DrawableContainer.java:227)
9.
android.graphics.drawable.StateListDrawable.onStateChange(StateListDrawable.java:99)
android.graphics.drawable.Drawable.setState(Drawable.java:400)
android.view.View.drawableStateChanged(View.java:7374)
android.view.ViewGroup.drawableStateChanged(ViewGroup.java:3357)
android.widget.FrameLayout.drawableStateChanged(FrameLayout.java:164)
android.view.View.refreshDrawableState(View.java:7388)
android.view.View.setEnabled(View.java:3147)
com.voltup.powermax.ac.a(ActivityAppUiProxy.java:383)
com.voltup.powermax.cp.onPostExecute(ModeChange.java:1)
android.os.AsyncTask.finish(AsyncTask.java:417)
But I thought onPostExecute() always runs on the UI thread?
Yes, it does. Per Selvin's comment, AFAIK, if you try to create an AsyncTask from another thread, it fails.
Could it be that the UI thread suddenly gets changed to another thread??
No. More likely, you are accidentally going down a code path in doInBackground() that is updating the UI.
If you have a full stack trace showing that you are getting this exception from logic executed in onPostExecute(), please edit your question and paste in that stack trace.
Related
I have a fragment that takes too long to load due to its OnCreateView() function configuring about 40 textboxes. Is there a way to put this in a background thread and display a progress indicator circle while it loads?
I have tried using AsyncTask, but when the OnCreateView() is called a second time, I get an error stating that I must use the original thread. Also the device is frozen and will not display any indicator while loading through AsyncTask.
You can't do the operation about the UI in the other thread. The official document said:
By design, Android View objects are not thread-safe. An app is expected to create, use, and destroy UI objects, all on the main thread. If you try to modify or even reference a UI object in a thread other than the main thread, the result can be exceptions, silent failures, crashes, and other undefined misbehavior.
So you should try to optimize the code about the fragment's OnCreateView() method instead of trying run it in the background thread. All the UI operation should be excuted in the main thread.
For more information, you can check the official document about the etter performance through threading.
This is Strange, I used to do GoogleAnalytics for my Unity Projects because I had two major uses, one was to see the number of users on Screen and second for some events. I heard about Firebase and wanted to explore it, I was successful in initializing the sdk and logging different events, but now there is one major problem which I can't seem to get over. Apparently when I try to Log my Current Screen using:
Firebase.Analytics.FirebaseAnalytics.SetCurrentScreen ("MainActivity", "MainMenu");
and then read somewhere to use it like this:
FirebaseAnalytics.SetCurrentScreen ("MainActivity", "MainMenu");
both of these functions gave the same error when viewed in Monitor(ddms)
SetCurrentScreen must be called from Main Thread
Everytime I called the function this came up, I don't know why this is happening and can't find a solution for this anywhere.
The Function calling the Method is:
public void AnalyticsLogScreen() {
// Log an event with a float.
DebugLog("Logging a Screen");
Firebase.Analytics.FirebaseAnalytics.SetCurrentScreen ("MainActivity", "MainMenu");
}
It appears that you are calling SetCurrentScreen from worker thread. You can check this by looking at the name of the thread making the call or comparing the TID from the logcat to the process PID. The main thread with have name "main" and TID that matched the app PID.
Unity SetCurrentScreen is wrapped on Java FirebaseAnalytics.setCurrentScreen() method .
setCurrentScreen can only be called from the main thread.
The reason for this requirement is that Activities in Android can only be displayed displayed from the UI thread and allowing SetCurrentScreen from worker thread creates a race condition between the Activity displayed in the UI thread and the worker thread executing the call to setCurrentScreen. To avoid this race condition Firebase requires the call to setCurrentScreen to be made on the UI thread. If you still like to set the screen from worker thread you can just call runOnUiThread though this will create the race condition so some event can be attributed to the wrong screen or appear logged not on any screen.
https://developers.google.com/tag-manager/android/v4/ua#send-screen-views this is container settings. After that you can log screen :
Firebase.Analytics.FirebaseAnalytics.LogEvent("openScreen", "screenName", "main_screen");
Tag settings:
Trigger :
Varible:
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.
I noticed a similar problem for dot NET, but my problem is for Android, so perhaps solution looks different.
The process is activated by clicking button. The process was running as part of UI thread and at the end it did updating UI. I have added progress dialog to be more user friendly, so I instantiate a thread running the process and at the end it updates UI and dismisses progress dialog. Unfortunately UI update is failing with the exception below:
07-19 21:14:04.602: ERROR/Atjeews(283): android.view.ViewRoot$CalledFromWrongThreadException: Only the original thread that created a view hierarchy can touch its views.
If I try to block UI thread and release it after long process finishes to update UI, the progress dialog doesn't get shown. Should I try to show progress dialog in a separate thread instead, or there is another simpler solution?
please check http://developer.android.com/resources/articles/painless-threading.html
Taken from the Android Developers Processes and Threads page:
To fix this problem, Android offers several ways to access the UI thread from other threads. Here is a list of methods that can help:
Activity.runOnUiThread(Runnable)
View.post(Runnable)
View.postDelayed(Runnable, long)
You can just instantiate an anonymous Runnable as the argument, like post(new Runnable(){doWhatever();}), that will do whatever you want on the UI thread instead.