Android: Util class static method with background thread (async task) - android

I have utility class with static methods. Assuming that these methods can fire background task, something like below:
class ExampleAsyncUtil {
public static void doSomeAsyncJob() {
new AsyncTask<Void, Void, Void> () {
#Override
protected Void doInBackground (Void... params) {
//do heavy job here ...
return null;
}
}.execute();
}
}
if then i call the util class method inside activity which then becomes destroyed:
class ExampleActivity extends Activity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
ExampleAsyncUtil.doSomeAsyncJob();
}
#Override
protected void onDestroy() {
//should i stop async task in util on destroy ?
super.onDestroy();
}
}
Does it do any harm? (assuming that the background thread does not keep any refrence to activity which called the utility method)

Yes, you should cancel the AsyncTask from your UI. You can leak the AsyncTask otherwise (talking from experience). And, who knows, maybe even block another from being started on the same thread: they keep changing the implementation: http://developer.android.com/reference/android/os/AsyncTask.html. Or, contrary, multiple onCreate() could create duplicate "zombie" AsyncTask's. Anyway, it's not an intended use of AsyncTask: they are supposed to be short-lived and properly controlled. So, there seems to be no real gain in that static business.
I think you should either start a Service and spawn a tread from it or just use AsyncTask traditionally, within the life cycle of your Activity.
There is a good tutorial on asynchronous handling here: http://www.vogella.com/articles/AndroidPerformance/article.html.

Related

Android AsyncTask memory leaks

I read some questions here, some articles in Internet, but the question about memory leaks in AsyncTask isn't clear for me. Please, can you give me an advice?
Let's consider some situations:
1) AsyncTask is an inner class
I write MyAsyncTask for downloading small data from the server (<1 KB) in MyActivity code (not as static class). It will store an implicit reference to MyActivity instance. And if i'll start MyAsyncTask.execute(), then MyActivity instance cannot be Garbage Collected, until this AsyncTask will finish. So, if I'll rotate the screen during AsyncTask executing, then old MyActivity instance will be in memory - and it is memory leak.
What I decided to do: because of size of my data for downloading, I will cancel my AsyncTask in onDestroy() method in MyActivity. In this way, I have such code of MyActivity:
public class MyActivity extends Activity {
//views and constants
private MyAsyncTask air;
private ProgressDialog progressDialog;
protected void onCreate(Bundle savedInstanceState) {
// TODO Auto-generated method stub
super.onCreate(savedInstanceState);
setContentView(R.layout.account_info_layout);
progressDialog = new ProgressDialog(this);
//findViewById, etc.
}
#Override
protected void onStart() {
super.onStart();
air = new MyAsyncTask();
air.execute();
}
#Override
protected void onDestroy() {
if (air.getStatus() == AsyncTask.Status.RUNNING) {
air.cancel(true);
}
air = null;
super.onDestroy();
}
class MyAsyncTask extends AsyncTask<Void, Void, String> {
#Override
protected void onPreExecute() {
super.onPreExecute();
UserData.refreshTimer();
if (!progressDialog.isShowing())
progressDialog.show();
}
#Override
protected String doInBackground(Void... params) {
//GET request
return result;
}
#Override
protected void onPostExecute(String result) {
super.onPostExecute(result);
//handle results
progressDialog.dismiss();
}
}
}
So, if my activity instance is destroyed, I cancel my async task, and create new instance in onStart(). Will it produce memory leaks, or can it produce IllegalArgumentException/NullPointerException because of progressDialog instance? I suppose, it will not produce any exceptions, because if I cancel the AsyncTask, onPostExecute() will not be called.
2) AsyncTask in own file
The next case is when I write MyAsyncTask in other file, and pass in constructor Context instance. Will such approach produce memory leaks, if I'll store Context as WeakReference? And is it correct idea to cancel AsyncTask in onDestroy() method in calling Activity to avoid IllegalArgumentException/NullPointerException during onPostExecute() method? Or, other way to avoid these exceptions is to check my Context variable for null.
Other approaches: I've heard about Otto library, about using retained Fragments, but now I want to understand these questions. If somebody knows - please, answer.
Cancelling is a good way to solve your memory leak. You might want to consider cancelling in onStop though, since you set up a new task in onStart.
You might want to combine this with dismissing the progressDialog in onStop, since you're cancelling the task.
If you cancel the task, you will not cause a memory leak. If you don't, you might cause a temporary memory leak. You could for example solve that by constructing the new Java file with a context.getApplicationContext() instead of normal getContext / this (Activity). Then it will not be tied to the activity but to the application (the application survives orientation change). You however won't be able to access the dialog in onPostExecute(). Instead you could use a callback to a listener if you want. Make the activity implement the listener (and detach it onStop). But cancelling is a fine approach as well.
By the way cancelling an async task doesn't mean that it will immediately be cancelled.
I recommend you to switch to Intent Services, Handlers or if possible to RXJava.
Unlike async tasks where you may need to nest async tasks to execute multiple executions, with RXJava you could chain those executions, apply filters and different transformations and you can run that code on a worker thread.

Async Task Best Practice

In regards to Async Task in android it is best practice to include them as a Inner Class of the Activity you are working in or is it better to have the as their own stand alone class? for example
public MyClass extents Activity
{
public void onCreate(){
}
public class MyAsyncTask extents AsyncTask<Void, Void, Void>
{
protected void doInBackground()
{
//do stuff here....
}
}
public void onResume(){
}
}
or as an external class
public class MyAsyncTask extents AsyncTask<Void, Void, Void>
{
protected void doInBackground()
{
//do stuff here....
}
}
public MyClass extents Activity
{
public void onCreate(){
}
}
AsyncTask has it's own lifecycle which doesn't depend on Activity lifecycle. You need to make sure that it doesn't have a reference to activity or views when activity is already destroyed.
Implementing AsyncTask as an inner class makes sense only if it's static. If it's not static then it will have an implicit reference to outer activity which will lead to memory leaks. If you need references to views from your static async task use WeakReference.
Implementing AsyncTask in a separate file is also a good idea, but same rules applied. Use weak references if needed.
The only difference between static inner async task and async task in a separate file is code readability. If there is a lot of logic inside async task, go ahead with a separate file.

Android AsyncTask with notification

I am currently starting to develop Android applications, and I must say that it all came out very very simple and straightforward.
I have a small question about the AsyncTask. Maybe I've been doing something wrong, but here's the situation.
I have a small app that needs to load a list's content from the web.
I developed everything based on fake requests, and it all came out awesome. Then I updated the code with actual requests and got the 'Network on main thread error'. So I decided to switch to AsyncTask.
I was wondering if I could let AsyncTask just do the asynchronous work, and handle the result somewhere else (where I actually have the GUI connections and everything). I thought that in terms of readability and logic it makes more sense to have all the code that handles the interface in the Activity, but how could I let the Activity know when a task was completed?
I wrote these simple classes and interfaces (and it works) but I wanted to know from you if this is a good thing or there are better methods to do that.
So, here's the code:
public interface AsyncDelegate {
public void executionFinished(LazyLoaderWithDelegate lazyLoaderWithDelegate);
}
This is a simple interface. The purpose is to have the Activity implement this and handle the 'executionFinished' method. Something like a listener.
import android.os.AsyncTask;
public class LazyLoaderWithDelegate<Params, Progress, Result> extends AsyncTask<Params, Progress, Result>{
AsyncDelegate delegate;
Result result;
public LazyLoaderWithDelegate(AsyncDelegate delegate){
this.delegate = delegate;
}
#Override
protected Result doInBackground(Object... params) {
//This will be Overridden again from the subclasses anyway.
return null;
}
#Override
protected void onPostExecute(Result r){
this.result = r;
delegate.executionFinished(this);
}
public Result getResult(){
return result;
}
}
This class basically gives a skeleton structure to notify the delegate when the task is finished.
That's all. Here's an example of using this classes:
public class LazyVideoLoader extends LazyLoaderWithDelegate<Void, Void, List<List<Video>>>{
public LazyVideoLoader(AsyncDelegate delegate) {
super(delegate);
}
#Override
protected List<Video> doInBackground(Void ...params) {
return ServerInterface.getVideos();
}
}
public class MainActivity extends Activity implements AsyncDelegate {
private LazyVideoLoader videoLoader;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
/*
* Set up the lazy loaders
*/
videoLoader = new LazyVideoLoader(this);
videoLoader.execute();
}
#Override
public void executionFinished(LazyLoaderWithDelegate task) {
if(task == videoLoader){
List<Video> result = videoLoader.getResult();
//Do whatever you need...
}
}
Everything you run on onPostExecute is in the UI Thread. Also you can run a code on UI Thread once a certain part of the work is done simply on onProgressUpdate by calling publishProgress on doInBackground.
Refer this for more information. It has everything you need to know about AsyncTask.
If I understand this correct you have a seperate class, which runs an AsyncTask. If the task is completed the as callback used interface informs the Activity. This is good if you think in components to make the code more modular.
The most common practice is to use an AsyncTask as an inner class in an Activity. If you just wanna download a picture or something similar with relative small size this is the prefered way. Because you can access all fields in your inner class, which makes things easier than passing them around in constructors/interfaces.
Don't use an AsyncTask in an extra Class just for readability. If you have to do some fair calculation/modification on the results with different methods your way is ok.

how to pass the result of asynctask onpostexecute method into the parent activity android

I am developing an application in which i need to send the value of the asynctask's onPostExecute method's result in to the previous activity , ie the activity in which the aync task is being called.pls put some codes. Anyhelp is appreciated
Two ways:
Declare class extending AsyncTask as private class in parent Activity
Pass Handler or Activity itself as param of class extending AsyncTask
If I were you, I'd follow the first option.
Look at DOCS:
class MyActivitySubclass extends Activity {
function runOnPostExecute(){
// whatever
}
private class MyTask extends AsyncTask<Void, Void, Void> {
void doInBackground(Void... params){
// do your background stuff
}
void onPostExecute(Void... result){
runOnPostExecute();
}
}
}
Note 1
Code placed in body of function onPostExecute is already run on Activity thread, you should just mention that this keywords leads to MyTask.this and not MyActivitySubclass.this
Well if your AsyncTask is an inner class, you could simply call a method in your activity from onPostExecute():
public class MyActivity extends Activity {
public void someMethod(String someParam) {
// do something with string here
}
public class InnerTask extends AsyncTask<...> {
protected void onPostExecute(result) {
someMethod(Send parameters);
}
}
}
The onPostExecute method is fired on the main UI thread, so anything done there is already on the AsyncTasks caller.
http://developer.android.com/reference/android/os/AsyncTask.html
Fire an event in the OnPostExecute.
Its an add on to the answer by Marek Sebera, he pointed to use a handler. To keep the code simple and intuitive use an interface. This isn't alien concept, we use it all the time for callback functions (eg: OnClickListner etc..). The code would look some thing like this.
public class InnerTask extends AsyncTask<...>
{
interface ResultHandler
{
void gotResult(<> result);
}
private ResultHandler myResult;
//constructor
public InnerTask(....params...,ResultHandler callback)
{
...
this.myResult = callback;
}
protected void onPostExecute(<>result)
{
...
myResult.gotResult(result);
}
}
public class MyActivity extends Activity implements InnerTask.ResultHandler
{
#Override
protected void onCreate(Bundle savedInstanceState)
{
//do something
//if you want the InnerTask to execute here
InnerTask i = new InnerTask(....params...,this); //send 'this' as parameter
i.execute();
}
#Override
public void gotResult(<> result)
{
//from onPostExecute
}
}
If we want to use the same AsynTask class at multiple sites we can use this type of implementation instead of using nested classes implementation.

How to refresh asyncTask from another acctivity?

I am launching a activity, and once a user is logged in, i want to refresh the main activity. To load the data from the logged in user.
Such as the image and name. I have all of this set up already.
I just need to know is it possible to launch another activity and run its async task again.From an launching an intent from inside another activity?
It's not clear what exactly your design is, but if you need to use the same AsyncTask from two different activities, it should be a separate class, not tied to a particular activity. You can have the two activities implement a common interface, so that the AsyncTask doesn't need to know which activity it is updating. Then instantiate the task by passing a reference to the enclosing activity, and start it as needed. There is no need for one activity to start the other.
Something like:
public interface UserActivity {
void updateUserData(UserData userData);
}
public class Activity1 implements UserActivity {
public void onStart() {
UpdateUserDataTask task = new UpdateUserDataTask(this);
task.execute();
}
public void updateUserData(UserData userData) {
// update
}
}
public class UpdateUserDataTask extends AsyncTask<Void, Void, UserData> {
UserActivity userActivity;
public UpdateUserDataTask(UserActivitiy userActivity) {
this.userActivity = userActivity;
}
// doInBackground, etc implementation.
protected void onPostExecute(UserData userData) {
userActivity.updateUserData(userData);
}
}
As far as I'm aware, AsyncTasks aren't supposed to be reused. They're supposed to be run once and then you can create a new one if you need it.
Once an AsyncTask is executed once, you cannot execute it again. What you can do, though, is control it's "refresh" using onProgressUpdate() and publishProgress() as follows. Note that this will only work for a one-time refresh. If you wanted to be more semantically correct, you might do the "normal" operation in onProgressUpdate() and use onPostExecute() for your resfresh.
public class MyAsyncTask extends AsyncTask<Void, String, Void> {
private boolean isRefresh = false;
#Override
protected Void doInBackground(Void... arg0) {
while (!isRefresh){
//Perform your normal operation
}
//When isRefresh is true, you want to refresh.
this.publishProgress(values);
return null;
}
#Override
protected void onProgressUpdate(String... values) {
// Refresh code here
super.onProgressUpdate(values);
}
public void refreshTask(){
this.isRefresh = true;
}
}
You could then maintain a reference to the object of MyAsyncTask and invoke refreshTask() on it whenever you want to refresh it.

Categories

Resources