Is it bad practice to access a view in doInBackground()? - android

Is it bad practice to pass a UI element, such as a TextView, to AsyncTask.doInBackground() (through AsyncTask.execute()) and to read fields from the view in the background thread? (I know it's not okay to alter GUI elements from a background thread.)

It is not great, for the simple reason that you do not know if that widget is any good anymore.
Suppose the following happens:
You execute the AsyncTask, but it is stuck behind other tasks and does not run right away
The user rotates the screen, or presses BACK, or otherwise destroys the activity
Your AsyncTask finally starts running and you try accessing this widget
In the best-case scenario, the widget is simply wrong. In the worst-case scenario, whatever you call on it causes some sort of crash, because the hosting activity is destroyed.
AsyncTask itself is fairly obsolete; modern Android development uses other things (LiveData, RxJava, and Kotlin coroutines being the biggest candidates). However, if you wish to use AsyncTask, please ensure that it does not try referencing the activity or its widgets from doInBackground().

Related

Using context in an AsyncTask via a Fragment that has no UI

I have previously had my App working with just activities and am now working on converting to fragments in order to improve the UI.
Previously my Activity started an AsyncTask and passed in itself to be used as the Context when certain methods required it (not UI operations, but calls to shared preferences and content providers). I have now learnt that this approach can lead to undesirable outcomes if the Activity is destroyed and garbage collected, but it did compile and run fine.
I began this change because I wanted to make my loading screen behave better when the app was paused and stopped. I realised people frown on loading screens in Android but for me it is required as I have an operation that will take 20 seconds or so and that needs to be completed before the app will function.
So using this guide, I began improving my app.
In short the guide moves the AsyncTask into a Fragment that does not have an attached UI, with a separate Fragment for displaying the loading screen with ProgressBar. This means that the Fragment that spawns the AsyncTask does not have a Context, meaning I cant pass one in to the AsyncTask.
As I said before I have operations in the AsyncTask that require a Context object, so where can I get it from? Should I just pass in that data to the AsyncTask before I start?
As far as I know, the context is not a static property, so you actually need one object to retrieve it.
Thus, you can either go the "hack-way" as in this post:
Static way to get 'Context' on Android?
or you can follow Android guidelines and use a Service for your background loading. Remember that AsyncTask is an utility class designed to help in background operations that later need to communicate with the UI, so you should use AsyncTask in correlation with a UI object.
If you, instead use a Service, then you got no problem, since the Service object itself is the context that you need.
If your AsyncTask is not handling any UI components you can use the parent Activity's context. So where you previously passed in this you'll now pass in getActivity(). Note, if you do have it changing the ui this will set you up for Null Pointer Exceptions.

Is using runOnUiThread inside AsyncTask inefficient and bad?

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.

Activity's background thread & configuration change

I have seen some discussion here on Stack Overflow related to using Activity.onRetainNonConfigurationInstance() to maintain a background thread started by one instance of an Activity and pass it to the next instance of the Activity which results, for example, when the phone's orientation changes from portrait to landscape.
The discussions do not specify exactly what can be done with the thread wrapped in the Object returned from onRetainNonConfigurationInstance().
For example:
1. Is there a way to actually keep the background thread running using this technique?
2. Do you need to somehow pause the thread when the previous instance of Activity is going away and then restart it again in the new instance?
Can anyone provide a short example?
Any details would be appreciated.
You can return anything you want to onRetainNonConfigurationInstance(). If you have a Thread that you want passed from one instance of the Activity to another, you can either return it directly, or put it inside another object that you return from onRetainNonConfigurationInstance(). You don't need to pause the thread or interact with it in any way. It just keeps running as if nothing happened.
The only thing you need to be concerned about is how the Thread interacts with the Activity (if at all). If the thread will call the Activity back (to indicate progress or something like that) then you somehow need to give the thread a reference to the new Activity, as the old Activity will be dead.
What do you want to do in your background thread?
EDIT (add more details about threads/activities):
Threads have their own lifetimes which are completely disconnected from Activities. If you create a Thread in an Activity and start it, it will run to completion no matter what your Activity does. The only thing that will stop the thread explicitly is if Android decides to kill your process (which it may do if your process contains no active activities).
The thread will continue to run. For an example of what you can do with this, you can check out the android Ignition project and its IgnitedAsyncTask (and related examples).
The idea is that you will maintain a reference to your thread (usually an AsyncTask) somewhere in your Activity, and occasionally your thread (again, especially if it's an AsyncTask) will require a reference to a Context in order to perform some kind of UI update upon the conclusion of its background task. You will need to make sure that the Context (and anything derived from it--like a TextView or the like) to which your thread has a reference is non-null, or else it will crash.
You might use getLastNonConfigurationInstance() to set your Activity's reference to the thread, and then call a setter on the thread to set its Context reference (to avoid any related null pointer crash).

Android AsyncTask inside AsyncTask

So, I'm working on a barcode decoder, which once we have the barcode goes to multiples API over the internet to decode what was just scanned.
The thing is that I have to link some XML parsing together, and I don't know if I'm doing it right.
So, once the barcode is scanned, my program calls an ASyncTask which goes over an API to retrieve the product name. Once it has the name, I want it to call another ASyncTask. I know this is possible by instantiating an ASyncTaks in the onPostExecute() of the other but, I think this is wrong, because it's like boxes within boxes.
So isn't it possible/better to instantiate my second ASyncTask inside my main Activity, and make it wait until my first ASyncTask is finished ?
(english isn't my primary language, I hope I made myself clear).
I think it's absolutely legitimate to start the second AsyncTask in the onPostExecute of the first AsyncTask, Mixing both operations is a bad logical idea, As "The Offspring" said - "You've gotta keep 'em separated"
If you don't want it to be directly inside the onPostExecute itself, set a handler to execute it in the activity and call this handler from onPostExecute.
And last thing - If you have a lot of logic - move it to a separate file, don't keep it all at the same file.
In situations like this it might be better to batch long running operations together in one AsyncTask.
Another option is to use the Loaders API, this makes chaining tasks much easier http://developer.android.com/guide/topics/fundamentals/loaders.html
You can go for another approach if you are facing often a situation like this. That is to merge requests and operations inside of runnables/callables and to manage them separately within say a queue for instance.
Here is a nice approach.
http://ugiagonzalez.com/2012/07/02/theres-life-after-asynctasks-in-android/

Structure App using Methods/threads

I know this is a fundamental java question, but i am relatively new to java.
How do i structure the code attached (monitoring) within a UI.
What the code does is just log values, and if certain conditions are activated, it does stuff. The loop () method does pretty much everything, but there are a few small things done by the preceding methods.
Let's say in the UI, I have a 'calibrate' button, which if pressed, runs a calibrate method/thread, and a 'monitoring' button which runs a different 'monitoring' method/thread. The problem is, these methods/threads are at the moment defined in their own project as classes. My ideas are along the lines that i need to construct these 2 classes and then call the methods i want from them in response to UI interaction. However, if i call just the method, for example:
if (monitoring button) {
monitoring.method1;
}
this means that i can't do anything in parallel to that, so I need to make what happens in those classes into thread somehow.
Cheers,
Rokky
Take a look at AsyncTask, this will allow you to run logic in a background thread, leaving your activity free to respond to the user.

Categories

Resources