I know that in normal situation, one cannot call ListAdapter.notifyDataSetChanged() in a background thread to update a listview. So I tried to work around like this:
class mythread extends thread {
private ListAdapter listAdapter;
...
public void run() {
listAdpater.setNotifyOnChange(false);
listAdapter.addAll(A_LARGE_ARRAY);
}
...
}
When the thread finishes, it sends a message to a handler running in the UI thread, which would call listAdapter.notifyDataSetChanged() to update the listview.
It seemed to work, except when I tried to debug it, the debugger complained something like "The adapter is changed, while the listview doesn't receive a notification. Make sure that you modify adpater in UI thread only."
Does the android framework monitor the modification of an adapter even I called setNotifyOnChange(false)? Is there any way to work around?
PS. In my own benchmark, by moving listAdapter.addAll(A_LARGE_ARRAY) to a background thread, it saved me about 126ms of execution time, which means, if I modify the adapter in UI thread, it will be blocked for 126ms.
UPDATE
Anyone who run into this problem, please refer to this Google I/O Lecture link. It's really helpful.
You probably wouldn't want to do things this way, your app may inexplicably broke on some device or some version of Android while working perfectly on others.
Remember listAdpater is accessed every time the relating UI is being updated, which runs on the UI thread.
At the same time you are updating the same listAdpater in another thread. This means that:
If the UI is updated while you are modifying listAdapter, the UI may display incorrect items for even throw Exception (depending on the race condition and )
What's being updated to listAdapter in the other thread may not be updated to the main thread, if the OS sees it suitable to run that other thread on another processor, since there's no memory barrier (synchronization), there's no guarantee that these two threads will see the same thing
So the best course of action for you would be still run the changes on UI thread, but only update a fraction of A_LARGE_ARRAY to listAdapter at a time.
Related
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'm using the bump android sdk. I have a broadcast receiver and i would like to update some elements from my ui depending on different intent filters. for example
else if (action.equals(BumpAPIIntents.NOT_MATCHED)) {
Log.i("Bump Test", "Not matched.");
BumpTest inst = BumpTest.instance();
if(inst != null) {
inst.UpdateMyText("testing");
}
Problem is even though in logcat i can see the log entry the textview update method doesnt seem to be called
Am I doing something wrong?
You aren't allowed to update the UI from other threads (like the one that calls the BroadcastReceiver). You'll have to use a simple AsyncTask class. It was created for the sole purpose of updating the UI after performing actions that will take a long time to complete. It's a good idea to put things like requesting resources from the internet in an AsyncTask because it doesn't block the UI thread from executing. The nice side effect is it calls a function in your main program when it's done, and this call is done on the UI thread.
I know it sounds crazy that someone is using runOnUiThread inside AsyncTask. Somehow, it is working for me but I wanna know if it is an acceptable and robust approach or not. Here is the scenario:
I have an app in which after successful login, user is rendered to next screen. In this new screen, 3 different methods are loading different types of data from a web server. These methods are:
getMembersForList() : It loads the list of community members and shows it in a listview.
getProfileData() : It loads the profile of logged in user and shows his name , image etc on the screen.
getNotificationCounts : It loads the count of new notifications for the user.
I applied 3 different approaches for it :
(1) Calling all 3 methods simply in onCreate i.e. no exclusive thread is being used for any of the methods . In this case , the transition from login screen to this screen becomes very slow and black screen shows up for some time before this activity shows up.
(2) Calling getMembersForList() on UI thread and the other 2 methods on exclusive threads. In this case transition becomes fast and list shows up quickly but Notification counts and username etc. don't show up because WrongThreadException occurs saying that this thread can't touch other thread's views (TextViews for username, notification count etc. which are declared globally) . The same thing happens when I start these threads from an AsyncTask as well.
(3) Calling getMembersForList() on UI thread and then starting an AsyncTask in which the other 2 methods are being called in "runOnUiThread" inside doInBackground() method. This solves both the above issues. Now the screen transition is faster and the WrongThread exception is also not occuring.
So far the approach-(3) is working good for me but I am not sure if this is the right way to do it because runOnUiThread and AsyncTask are 2 completely opposite things. Can anyone please clear my doubts about this scenario. Thanx in advance.
Yes, use-cases like this are a big reason why the runOnUiThread() method exists in the first place. The idea is you allow your background thread(s)/AsyncTask instance(s) to run your lengthy operations in the background, and then provide a simple hook that they can use to update the interface when they have the result (or at arbitrary intervals, as different pieces of the result become available).
As long as that's what you're doing, then your usage is fine. What you want to avoid doing is performing a lengthy operation on the main thread, either directly or indirectly by passing in some lengthy operation from a background thread.
Of course you don't have to do it that way if you don't want to. You could use postExecute() instead. Or you could store the result somewhere and then use any sort of message-passing API to notify the main thread that the result is ready, and so on.
I would advice to run all the 3 calls in the asyncTask, and update the UI in the postExecute() of the AsyncTask after the background taks is complete, postExecute runs on UIthread so you need not call anything explicit to run them on UIthread.
I am writing an android app and I need to be able to do certain things periodically/continuously. I am coming from a C/C++ embedded firmware background and this new-fangled way of doing things is going to take some getting used to. It seems that there is no such thing as a "main loop" in Android, that everything is event-driven... I also understand that by default all code you write operates on the GUI thread, and I should probably make a new thread to execute the equivalent of a "main loop"...
So far what I have is an implementation of the AsyncTask class who's "doInBackground" method contains an infinite loop (my main loop), I create an instance of this class and run it immediately when my app starts. The problem I am having is in the interaction between this thread and the user interface... when something occurs in my main loop thread and I want to update the GUI understand that I must call "publishProgress", which is executed on the GUI thread. There are a few problems with this, primarily that many things I have tried to do in this "onProgressUpdate" method do not work, or do not occur in a predictable amount of time.
My question, is there a better way to accomplish what I am trying to do? In general, what do most people do when they have code that they want to run periodically and/or continuously while their application is running, code that must interact with the user interface in a timely manner (by timely I mean with zero delay).
Thank you.
public class MainLoopThread extends AsyncTask<Void, Void, Void>
{
#Override
protected Void doInBackground(Void... arg0)
{
while(true)
{
//Do stuff
//Update GUI
publishProgress();
}
}
protected void onProgressUpdate(Void...voids)
{
//Update GUI
}
}
It is unclear what you are trying to do, however just let me say using AsyncTask in this way may have negative consequences.
AsyncTask internally uses a thread pool pattern for running the stuff from doInBackground(). On Android OS before 1.6 and starting from 3.0 the pool size is just 1, meaning no parallel computations for a bunch of AsyncTasks. More details on this here.
So, this may result that only this current AsyncTask is running, while others even if started will have to wait untill the current one is done.
Depending on your needs for things to be done periodically Android exposes:
AlarmManager
Handler - it allows to post a runnable on UI thread with a delay or periodically
Timer + Activity.runOnUiThread(Runnable action) inside of TimerTask
UPDATE: basing on your comments it looks like you need a Service, that starts a thread that periodically sends broadcasts with the data for UI. Then your UI (Activity) registers broadcast receivers to catch those broadcasts, extract the data and use for UI updates.
So your saying that onProgessUpdate() isn't working? That seems weird because it should.
Another option that you have is just to make a Thread that loops.
The trick is that if you want to update the UI thread you will have to make a call to view.post() and give it a runnable that will actually perform the update. The idea here is that you must schedule an update on the UI thread, you can't just take it and say NOW!