After a lengthy and unsuccessful search on google, it turns out that there is hardly any information about a handy library called Droid-Fu https://github.com/kaeppler/droid-fu
After reading the introduction by the creator (http://brainflush.wordpress.com/2009/11/16/introducing-droid-fu-for-android-betteractivity-betterservice-and-betterasynctask/) or the API (http://kaeppler.github.com/droid-fu), I could not figure out how to define a new betterasynctask (what methods hold what information etc).
So if there is anyone out there that could provide me (and apperently others as well) with some useful source code or tutorials, I would greatly appreciate it!
(If you need the jar file of the project, let me know, I can send you a copy)
EDIT:
A good example can be found here! and here
Ok, here is some additional information I found in the source code:
A progress dialog is automatically shown. See useCustomDialog(), disableDialog()
If an Exception is thrown from inside doInBackground, this is now handled by the handleError method.
You should now longer override onPreExecute(), doInBackground() and onPostExecute(), instead you should use before(), doCheckedInBackground() and after() respectively.
Let's see what I can achieve from here on then...still looking for a working example though!
EDIT 2:
A couple of examples can be found here and here. I stick to it but I get an error. Only difference is that my AsyncTask is not defined within the activity, but a class of its own. Stepping through the code reveals that the error happens upon creation of the (AsyncTask built-in) Dialog.
This is my stacktrace:
coming in a minute
Droid-fu is a little outdated now, mainly due to the lack of Fragment support. But I'll give an example from an app I wrote that used it.
First, your activity class has to subclass BetterActivity (or BetterXXXActivity). In my code I was using a ListActivity so mine here subclasses BetterListActivity. I also define a subclass of BetterAsyncTask so I can extend some functionality.
public class DroidFuExample extends BetterListActivity {
private ExampleTask mTask;
private List<Stuff> mMainStuff;
private class ExampleTask extends BetterAsyncTask<Void, Void, Integer> {
private List<Stuff> mStuff;
private DroidFuExample mContext; // a context for lifecycle management
...
}
}
Now, my task needs doesn't take a parameter, uses an indeterminate dialog so no progress is published, and needs to return an Integer. Your needs may differ, and that affects the types used in the class definition.
Next step is to define what the task processes in the background. In my case I need to populate mStuff. In your task class, define either doInBackground() or doCheckedInBackground() (doChecked... can throw an exception if you want to catch it).
protected Integer doCheckedInBackground(Context context, Void... params)
throws Exception {
mStuff = // some long-running code (no longer on the UI thread)
return 1;
}
Finally, at the very least you need to do something with your result, like update a class variable or populate the UI or something. This is done in after:
protected void after(Context context, Integer integer) {
if (integer >= someAcceptablePositiveConstant) {
mMainStuff = mStuff;
doSomethingInTheUIWithMainStuff();
} else {
//gah!
}
}
As you mentioned there's more you can do with the class, such as define a before() override that does work on the UI thread before the task, or failed() / handleError() to handle unchecked/checked failures. This is just a simple example, hope it helps.
What a pain in the #ss! The error was well hidden inside the BetterActivityHelper class:
public static ProgressDialog createProgressDialog(final Activity activity,
int progressDialogTitleId, int progressDialogMsgId) {
ProgressDialog progressDialog = new ProgressDialog(activity);
if (progressDialogTitleId <= 0) {
progressDialogTitleId = activity.getResources().getIdentifier(
PROGRESS_DIALOG_TITLE_RESOURCE, "string", activity.getPackageName());
}
progressDialog.setTitle(progressDialogTitleId);
if (progressDialogMsgId <= 0) {
progressDialogMsgId = activity.getResources().getIdentifier(
PROGRESS_DIALOG_MESSAGE_RESOURCE, "string", activity.getPackageName());
}
progressDialog.setMessage(activity.getString(progressDialogMsgId));
progressDialog.setIndeterminate(true);
progressDialog.setOnKeyListener(new OnKeyListener() {
public boolean onKey(DialogInterface dialog, int keyCode, KeyEvent event) {
activity.onKeyDown(keyCode, event);
return false;
}
});
progressDialogTitleId and progressDialogMsgId expect a value inside the res/values/string.xml :
<!-- Droid-Fu Progressdialog -->
<string name="droidfu_progress_dialog_title">Some nasty dialog title</string>
<string name="droidfu_progress_dialog_message">Some funny message</string>
If they are not defined, a runtime exception will be thrown.
Unfortunately, even the best helper classes are almost useless if they are UNDOCUMENTED. Took me a couple of hours to figure out what went wrong. So again, NO "Thank you" to the developer. Sor
Related
I am writing an app for android that connects to a server to get/post some xml data. I currently have a small class with static methods such as post(string URI, string body) and get() that wrap the httpclient calls to create a http post request and return the response. I am wondering if i should also have these method work in their own threads. Currently, i need to do a async task to call my Helper.post(..) method to connect to and get a request from a server. Is it better to just have the async stuff incorporated in the helper class to avoid having multiple repeated async tasks all across my app to just make post calls?
As a general principle it is best to wrap up repeated code so that you dont continually re-invent the wheel. Therefore if it is possible for you to wrap up the threading easily then it would be a good idea to do so.
This is not always very easy. Methods which get something from the network define want done with that data once it's been received. Usually you just return it. But if you're threading within the method then you have to push it somewhere. This leads to a lot of additional callbacks and you dont (in my experience) save much.
Rather than defining a bunch of static methods which do the threading for you, I would recommend you keep threading out of the static methods and define a bunch of abstract AsyncTasks instead. Each defines it's own doInBackground and leaves the onProgressUpdate and onPostExecute methods undefined. That way you get the best of both worlds - you re-use as much as possible (the doInBackground code) but are able to customize where the data is sent once received.
Example
Your static code:
public class MyStaticClass {
public static String getFoo( String name ) {
// use the network to get a string;
return "hello " + name; // Use your immagination.
}
}
An AsyncTask defined as public so that it can be re-used easily.
public class GetFooTask extends AsyncTask<String, String, String> {
#Override
protected String doInBackground( String... name ) {
return MyStaticClass.getFoo(name[0]);
}
}
Now to use it. Your static library or public async task could not have known what you need to do with the resulting string. So you tell it what to do with the result here:
public class MyActivity extends Activity {
#Override
protected void onCreate( Bundle savedInstanceState ) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main_view);
// You've already defined how to get the data
// so using it requires only minimal code now.
GetFooTask titleTask = new GetFooTask() {
#Override
public void onPostExecute( String heading ) {
((TextView) findViewById(R.id.heading)).setText(heading);
}
};
titleTask.execute("John");
}
}
In this example you can use the GetFooTask in as many activities as you like, just tell it where to put the data each time.
If you really think you will never want to do two network tasks on the same thread then you can combine the static code and the "abstract" AsyncTask. But more often than not I find I want to fire several things to and from the network before I finally return a result. If I did the threading in the network static code I would end up firing 10 threads for one request... therefore I keep threading out the static code.
Hello i am new to android and android thread so want to know that
How could we use more number of thread in order to perform every single task or method so that while user click on any UI component it does effect the performance ,having little knowledge of how the handler thread and asynctask work.But how can we run every method inside the asynctask so to do the operation and mean while user can do the other operation also.
In the application
i have voice recording from mic.
next showing progress bar.
next showing gallery with some image and with that setting effect to the picture.
The recommended way is to use AsyncTasks for long running tasks. So, not everything needs to be run with AsyncTasks, as you can get a performance hit due to the context switching.
As for how AsyncTasks work, read the documentation.
Use an AsyncTask and make sure to implement these as needed. You mention the idea of doing something in the background while a user is doing something so I'm guessing you'll want to alter the UI.
Take a look at these links for an more details from Android. They cover Runnable, AsyncTask and Handler
Overview of them all http://developer.android.com/guide/components/processes-and-threads.html
AsyncTask example http://developer.android.com/reference/android/os/AsyncTask.html
Old but relevant, Painless Threading http://android-developers.blogspot.com/2009/05/painless-threading.html
Another, more complex example http://developer.android.com/training/displaying-bitmaps/process-bitmap.html
I don't generally paste full examples in here but I had a lot of trouble finding an example I was happy with for a long time and to help you and others, here is my preferred method. I generally use an AsyncTask with a callback to the Activity that started the task.
In this example, I'm pretending that a user has triggered onClick(...) such as with a button, but could be anything that triggers a call into the Activity.
// Within your Activity, call a custom AsyncTask such as MyTask
public class MyActivity extends Activity implements View.OnClickListener, MyTask.OnTaskComplete {
//...
public void onClick(View v) {
// For example, thet user clicked a button
// get data via your task
// using `this` will tell the MyTask object to use this Activty
// for the listener
MyTask task = new MyTask(this);
task.execute(); // data returned in callback below
}
public void onTaskComplete(MyObject obj) {
// After the AsyncTask completes, it calls this callback.
// use your data here
mTextBox.setText(obj.getName);
}
}
Getting the data out of a task can be done many ways, but I prefer an interface such as OnTaskComplete that is implemented above and triggered below.
The main idea here is that I often want to keep away from inner classes as they become more complex. Mostly a personal preference, but it allows me to separate reusable tasks outside of one class.
public class MyTask extends AsyncTask<Void, Void, MyObject> {
public static interface OnTaskComplete {
public abstract void onTaskComplete(MyObject obj);
}
static final String TAG = "MyTask";
private OnTaskComplete mListener;
public MyTask(OnTaskComplete listener) {
Log.d(TAG, "new MyTask");
if (listener == null)
throw new NullPointerException("Listener may not be null");
this.mListener = listener;
}
#Override
protected MyObject doInBackground(Void... unused) {
Log.d(TAG, "doInBackground");
// do background tasks
MyObbject obj = new MyObject();
// Do long running tasks here to not block the UI
obj.populateData();
return
}
#Override
protected void onPostExecute(MyObject obj) {
Log.d(TAG, "onPostExecute");
this.mListener.onTaskComplete(obj);
}
}
I've already developed many Android apps that make web service requests, always with the following approach:
In every activity that need to make a web service request, I define an inner AsyncTask that shows a ProgressDialog in onPreExecute(), makes the web service call in doInBackground, and dismisses the progressDialog and updates the results in the UI from onPostExecute().
My concern is: Is there a better (shorter) way to do it? Does it make sense to repeat all that code in every activity? I've been googling a lot, but I've found nothing.
My question is: Couldn't I define a Callback interface? for example this one:
public interface RequestCallback {
public void onSuccess(Whatever whatever);
public void onError(ErrorCode errorCode, String message);
}
... and then define an external class, for example AsyncRequest, that wraps the AsyncTask definition and the ProgressDialog show() and dismiss() statements. So, all activities would just need to instantiate that class, and pass in the following parameters:
1) The method of the web service to run
2) A Bundle with all the parameters of that method of the web service
3) A RequestCallback instance (that could be an anonymous inline instance, where I could update the UI from onSuccess())
4) The context of the Activity (necessary to show the ProgressDialog(), so I would still need a way to prevent configuration change exceptions and so...),
Do you find this a good design? It could save hundreds of lines of code...
Your approach is what I did on my project. And it saved a lot of code as you said, I don't have any complaint about it. But here is some issues that I want to tell you:
You should create new instance of AsyncTask every time you do a background thread to avoid to pile callback.
For the progress dialog, I use it as Singleton, because you don't show many dialogs at the same time. The dialog will be showed when you call the background job, and will be dismiss in the callback. Here is what I did:
private void showProgressDialog(String strMess){
if(null == progressDialog){
progressDialog = new ProgressDialog(MainActivity.this);
}
if(!progressDialog.isShowing()){
progressDialog.setMessage(strMess);
progressDialog.show();
}
}
private void hideProgressDialog(){
if(null != progressDialog && progressDialog.isShowing()){
progressDialog.dismiss();
}
}
void someMethod(){
showProgressDialog("Loading...");
doBackgroundJob(param, new RequestCallBack() {
public void onRequestCompleted(String message, boolean isSuccess) {
hideProgressDialog();
if(isSuccess){
}else{
//do something on error
}
}
});
}
It is an optional, I defined an interface to notify instead of specific class, for each response I use one class, so in base class, I don't care what the response is. Here is it:
public interface OnRequestCompleted<TResponse> {
void requestCompleted(TResponse response);
}
public abstract class BaseRequest<TResponse> implements IRequest{
protected OnRequestCompleted<TResponse> delegate;
protected Class<TResponse> responseClass;
#Override
public void send() {
new HttpTask().execute();
}
private class HttpTask extends AsyncTask<Void, Void, String> {
//...
#Override
protected void onPostExecute(String result) {
if (null != response && null != delegate) {
delegate.requestCompleted(response);
}
}
}
// the response example
public class GroupResponse {
public static class Clip {
public int clipId;
public String detail;
}
public static class Movie {
public int movieId;
public String detail;
}
}
In the subclass of BaseRequest, I will tell it exactly what the response class is (Movie, Clip...)
Hope this help.
If you use it already and it works for you, then yes it makes sense to make it generic and save the time (and bugs) of reimplementing the same thing dozens of times. If you ever find yourself copy-pasting large sections of code with few to no differences you should turn it into a library function or class of some sort. Otherwise if you find a problem later you'll have to fix it in a dozen places. It doesn't even matter if you think of a better way to do things later- its still easier to change it in one place than a dozen.
The only real issue I'd have with your solution is I wouldn't add the progress bar to it- I'd handle it in the calling code and the onSuccess/onError implementations. That way you could also reuse it for a background call that doesn't need to put up a UI. I try to keep my UI decisions as far away from data grabbing code as possible, MVC patterns are good.
I download some data from internet in background thread (I use AsyncTask) and display a progress dialog while downloading. Orientation changes, Activity is restarted and then my AsyncTask is completed - I want to dismiss the progess dialog and start a new Activity. But calling dismissDialog sometimes throws an exception (probably because the Activity was destroyed and new Activity hasn't been started yet).
What is the best way to handle this kind of problem (updating UI from background thread that works even if user changes orientation)? Did someone from Google provide some "official solution"?
Step #1: Make your AsyncTask a static nested class, or an entirely separate class, just not an inner (non-static nested) class.
Step #2: Have the AsyncTask hold onto the Activity via a data member, set via the constructor and a setter.
Step #3: When creating the AsyncTask, supply the current Activity to the constructor.
Step #4: In onRetainNonConfigurationInstance(), return the AsyncTask, after detaching it from the original, now-going-away activity.
Step #5: In onCreate(), if getLastNonConfigurationInstance() is not null, cast it to your AsyncTask class and call your setter to associate your new activity with the task.
Step #6: Do not refer to the activity data member from doInBackground().
If you follow the above recipe, it will all work. onProgressUpdate() and onPostExecute() are suspended between the start of onRetainNonConfigurationInstance() and the end of the subsequent onCreate().
Here is a sample project demonstrating the technique.
Another approach is to ditch the AsyncTask and move your work into an IntentService. This is particularly useful if the work to be done may be long and should go on regardless of what the user does in terms of activities (e.g., downloading a large file). You can use an ordered broadcast Intent to either have the activity respond to the work being done (if it is still in the foreground) or raise a Notification to let the user know if the work has been done. Here is a blog post with more on this pattern.
The accepted answer was very helpful, but it doesn't have a progress dialog.
Fortunately for you, reader, I have created an extremely comprehensive and working example of an AsyncTask with a progress dialog!
Rotation works, and the dialog survives.
You can cancel the task and dialog by pressing the back button (if you want this behaviour).
It uses fragments.
The layout of the fragment underneath the activity changes properly when the device rotates.
I've toiled for a week to find a solution to this dilemma without resorting to editing the manifest file. The assumptions for this solution are:
You always need to use a progress dialog
Only one task is performed at a time
You need the task to persist when the phone is rotated and the progress dialog to be automatically dismisses.
Implementation
You will need to copy the two files found at the bottom of this post into your workspace. Just make sure that:
All your Activitys should extend BaseActivity
In onCreate(), super.onCreate() should be called after you initialize any members that need to be accessed by your ASyncTasks. Also, override getContentViewId() to provide the form layout id.
Override onCreateDialog() like usual to create dialogs managed by the activity.
See code below for a sample static inner class to make your AsyncTasks. You can store your result in mResult to access later.
final static class MyTask extends SuperAsyncTask<Void, Void, Void> {
public OpenDatabaseTask(BaseActivity activity) {
super(activity, MY_DIALOG_ID); // change your dialog ID here...
// and your dialog will be managed automatically!
}
#Override
protected Void doInBackground(Void... params) {
// your task code
return null;
}
#Override
public boolean onAfterExecute() {
// your after execute code
}
}
And finally, to launch your new task:
mCurrentTask = new MyTask(this);
((MyTask) mCurrentTask).execute();
That's it! I hope this robust solution will help someone.
BaseActivity.java (organize imports yourself)
protected abstract int getContentViewId();
public abstract class BaseActivity extends Activity {
protected SuperAsyncTask<?, ?, ?> mCurrentTask;
public HashMap<Integer, Boolean> mDialogMap = new HashMap<Integer, Boolean>();
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(getContentViewId());
mCurrentTask = (SuperAsyncTask<?, ?, ?>) getLastNonConfigurationInstance();
if (mCurrentTask != null) {
mCurrentTask.attach(this);
if (mDialogMap.get((Integer) mCurrentTask.dialogId) != null
&& mDialogMap.get((Integer) mCurrentTask.dialogId)) {
mCurrentTask.postExecution();
}
}
}
#Override
protected void onPrepareDialog(int id, Dialog dialog) {
super.onPrepareDialog(id, dialog);
mDialogMap.put(id, true);
}
#Override
public Object onRetainNonConfigurationInstance() {
if (mCurrentTask != null) {
mCurrentTask.detach();
if (mDialogMap.get((Integer) mCurrentTask.dialogId) != null
&& mDialogMap.get((Integer) mCurrentTask.dialogId)) {
return mCurrentTask;
}
}
return super.onRetainNonConfigurationInstance();
}
public void cleanupTask() {
if (mCurrentTask != null) {
mCurrentTask = null;
System.gc();
}
}
}
SuperAsyncTask.java
public abstract class SuperAsyncTask<Params, Progress, Result> extends AsyncTask<Params, Progress, Result> {
protected BaseActivity mActivity = null;
protected Result mResult;
public int dialogId = -1;
protected abstract void onAfterExecute();
public SuperAsyncTask(BaseActivity activity, int dialogId) {
super();
this.dialogId = dialogId;
attach(activity);
}
#Override
protected void onPreExecute() {
super.onPreExecute();
mActivity.showDialog(dialogId); // go polymorphism!
}
protected void onPostExecute(Result result) {
super.onPostExecute(result);
mResult = result;
if (mActivity != null &&
mActivity.mDialogMap.get((Integer) dialogId) != null
&& mActivity.mDialogMap.get((Integer) dialogId)) {
postExecution();
}
};
public void attach(BaseActivity activity) {
this.mActivity = activity;
}
public void detach() {
this.mActivity = null;
}
public synchronized boolean postExecution() {
Boolean dialogExists = mActivity.mDialogMap.get((Integer) dialogId);
if (dialogExists != null || dialogExists) {
onAfterExecute();
cleanUp();
}
public boolean cleanUp() {
mActivity.removeDialog(dialogId);
mActivity.mDialogMap.remove((Integer) dialogId);
mActivity.cleanupTask();
detach();
return true;
}
}
Did someone from Google provide some "official solution"?
Yes.
The solution is more of an application architecture proposal rather that just some code.
They proposed 3 design patterns that allows an application to work in-sync with a server, regardless of the application state (it will work even if the user finishes the app, the user changes screen, the app gets terminated, every other possible state where a background data operation could be interrumpted, this covers it)
The proposal is explained in the Android REST client applications speech during Google I/O 2010 by Virgil Dobjanschi. It is 1 hour long, but it is extremely worth watching.
The basis of it is abstracting network operations to a Service that works independently to any Activity in the application. If you're working with databases, the use of ContentResolver and Cursor would give you an out-of-the-box Observer pattern that is convenient to update UI without any aditional logic, once you updated your local database with the fetched remote data. Any other after-operation code would be run via a callback passed to the Service (I use a ResultReceiver subclass for this).
Anyway, my explanation is actually pretty vague, you should definititely watch the speech.
While Mark's (CommonsWare) answer does indeed work for orientation changes, it fails if the Activity is destroyed directly (like in the case of a phone call).
You can handle the orientation changes AND the rare destroyed Activity events by using an Application object to reference your ASyncTask.
There's an excellent explanation of the problem and the solution here:
Credit goes completely to Ryan for figuring this one out.
After 4 years Google solved the problem just calling setRetainInstance(true) in Activity onCreate. It will preserve your activity instance during device rotation. I have also a simple solution for older Android.
you should call all activity actions using activity handler. So if you are in some thread you should create a Runnable and posted using Activitie's Handler. Otherwise your app will crash sometimes with fatal exception.
This is my solution: https://github.com/Gotchamoh/Android-AsyncTask-ProgressDialog
Basically the steps are:
I use onSaveInstanceState to save the task if it is still
processing.
In onCreate I get the task if it was saved.
In onPause I discard the ProgressDialog if it is shown.
In onResume I show the ProgressDialog if the task is still
processing.
I have investigated this problem for months now, came up with different solutions to it, which I am not happy with since they are all massive hacks. I still cannot believe that a class that flawed in design made it into the framework and no-one is talking about it, so I guess I just must be missing something.
The problem is with AsyncTask. According to the documentation it
"allows to perform background
operations and publish results on the
UI thread without having to manipulate
threads and/or handlers."
The example then continues to show how some exemplary showDialog() method is called in onPostExecute(). This, however, seems entirely contrived to me, because showing a dialog always needs a reference to a valid Context, and an AsyncTask must never hold a strong reference to a context object.
The reason is obvious: what if the activity gets destroyed which triggered the task? This can happen all the time, e.g. because you flipped the screen. If the task would hold a reference to the context that created it, you're not only holding on to a useless context object (the window will have been destroyed and any UI interaction will fail with an exception!), you even risk creating a memory leak.
Unless my logic is flawed here, this translates to: onPostExecute() is entirely useless, because what good is it for this method to run on the UI thread if you don't have access to any context? You can't do anything meaningful here.
One workaround would be to not pass context instances to an AsyncTask, but a Handler instance. That works: since a Handler loosely binds the context and the task, you can exchange messages between them without risking a leak (right?). But that would mean that the premise of AsyncTask, namely that you don't need to bother with handlers, is wrong. It also seems like abusing Handler, since you are sending and receiving messages on the same thread (you create it on the UI thread and send through it in onPostExecute() which is also executed on the UI thread).
To top it all off, even with that workaround, you still have the problem that when the context gets destroyed, you have no record of the tasks it fired. That means that you have to re-start any tasks when re-creating the context, e.g. after a screen orientation change. This is slow and wasteful.
My solution to this (as implemented in the Droid-Fu library) is to maintain a mapping of WeakReferences from component names to their current instances on the unique application object. Whenever an AsyncTask is started, it records the calling context in that map, and on every callback, it will fetch the current context instance from that mapping. This ensures that you will never reference a stale context instance and you always have access to a valid context in the callbacks so you can do meaningful UI work there. It also doesn't leak, because the references are weak and are cleared when no instance of a given component exists anymore.
Still, it is a complex workaround and requires to sub-class some of the Droid-Fu library classes, making this a pretty intrusive approach.
Now I simply want to know: Am I just massively missing something or is AsyncTask really entirely flawed? How are your experiences working with it? How did you solve these problem?
Thanks for your input.
How about something like this:
class MyActivity extends Activity {
Worker mWorker;
static class Worker extends AsyncTask<URL, Integer, Long> {
MyActivity mActivity;
Worker(MyActivity activity) {
mActivity = activity;
}
#Override
protected Long doInBackground(URL... urls) {
int count = urls.length;
long totalSize = 0;
for (int i = 0; i < count; i++) {
totalSize += Downloader.downloadFile(urls[i]);
publishProgress((int) ((i / (float) count) * 100));
}
return totalSize;
}
#Override
protected void onProgressUpdate(Integer... progress) {
if (mActivity != null) {
mActivity.setProgressPercent(progress[0]);
}
}
#Override
protected void onPostExecute(Long result) {
if (mActivity != null) {
mActivity.showDialog("Downloaded " + result + " bytes");
}
}
}
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mWorker = (Worker)getLastNonConfigurationInstance();
if (mWorker != null) {
mWorker.mActivity = this;
}
...
}
#Override
public Object onRetainNonConfigurationInstance() {
return mWorker;
}
#Override
protected void onDestroy() {
super.onDestroy();
if (mWorker != null) {
mWorker.mActivity = null;
}
}
void startWork() {
mWorker = new Worker(this);
mWorker.execute(...);
}
}
The reason is obvious: what if the
activity gets destroyed which
triggered the task?
Manually disassociate the activity from the AsyncTask in onDestroy(). Manually re-associate the new activity to the AsyncTask in onCreate(). This requires either a static inner class or a standard Java class, plus perhaps 10 lines of code.
It looks like AsyncTask is a bit more than just conceptually flawed. It is also unusable by compatibility issues. The Android docs read:
When first introduced, AsyncTasks were executed serially on a single background thread. Starting with DONUT, this was changed to a pool of threads allowing multiple tasks to operate in parallel. Starting HONEYCOMB, tasks are back to being executed on a single thread to avoid common application errors caused by parallel execution. If you truly want parallel execution, you can use the executeOnExecutor(Executor, Params...) version of this method with THREAD_POOL_EXECUTOR; however, see commentary there for warnings on its use.
Both executeOnExecutor() and THREAD_POOL_EXECUTOR are Added in API level 11 (Android 3.0.x, HONEYCOMB).
This means that if you create two AsyncTasks to download two files, the 2nd download will not start until the first one finishes. If you chat via two servers, and the first server is down, you will not connect to the second one before the connection to the first one times out. (Unless you use the new API11 features, of course, but this will make your code incompatible with 2.x).
And if you want to target both 2.x and 3.0+, the stuff becomes really tricky.
In addition, the docs say:
Caution: Another problem you might encounter when using a worker thread is unexpected restarts in your activity due to a runtime configuration change (such as when the user changes the screen orientation), which may destroy your worker thread. To see how you can persist your task during one of these restarts and how to properly cancel the task when the activity is destroyed, see the source code for the Shelves sample application.
Probably we all, including Google, are misusing AsyncTask from the MVC point of view.
An Activity is a Controller, and the controller should not start operations that may outlive the View. That is, AsyncTasks should be used from Model, from a class that is not bound to the Activity life cycle -- remember that Activities are destroyed on rotation. (As to the View, you don't usually program classes derived from e.g. android.widget.Button, but you can. Usually, the only thing you do about the View is the xml.)
In other words, it is wrong to place AsyncTask derivatives in the methods of Activities. OTOH, if we must not use AsyncTasks in Activities, AsyncTask loses its attractiveness: it used to be advertised as a quick and easy fix.
I'm not sure it's true that you risk a memory leak with a reference to a context from an AsyncTask.
The usual way of implementing them is to create a new AsyncTask instance within the scope of one of the Activity's methods. So if the activity is destroyed, then once the AsyncTask completes won't it be unreachable and then eligible for garbage collection? So the reference to the activity won't matter because the AsyncTask itself won't hang around.
It would be more robust to keep a WeekReference on your activity :
public class WeakReferenceAsyncTaskTestActivity extends Activity {
private static final int MAX_COUNT = 100;
private ProgressBar progressBar;
private AsyncTaskCounter mWorker;
#SuppressWarnings("deprecation")
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_async_task_test);
mWorker = (AsyncTaskCounter) getLastNonConfigurationInstance();
if (mWorker != null) {
mWorker.mActivity = new WeakReference<WeakReferenceAsyncTaskTestActivity>(this);
}
progressBar = (ProgressBar) findViewById(R.id.progressBar1);
progressBar.setMax(MAX_COUNT);
}
#Override
public boolean onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.activity_async_task_test, menu);
return true;
}
public void onStartButtonClick(View v) {
startWork();
}
#Override
public Object onRetainNonConfigurationInstance() {
return mWorker;
}
#Override
protected void onDestroy() {
super.onDestroy();
if (mWorker != null) {
mWorker.mActivity = null;
}
}
void startWork() {
mWorker = new AsyncTaskCounter(this);
mWorker.execute();
}
static class AsyncTaskCounter extends AsyncTask<Void, Integer, Void> {
WeakReference<WeakReferenceAsyncTaskTestActivity> mActivity;
AsyncTaskCounter(WeakReferenceAsyncTaskTestActivity activity) {
mActivity = new WeakReference<WeakReferenceAsyncTaskTestActivity>(activity);
}
private static final int SLEEP_TIME = 200;
#Override
protected Void doInBackground(Void... params) {
for (int i = 0; i < MAX_COUNT; i++) {
try {
Thread.sleep(SLEEP_TIME);
} catch (InterruptedException e) {
e.printStackTrace();
}
Log.d(getClass().getSimpleName(), "Progress value is " + i);
Log.d(getClass().getSimpleName(), "getActivity is " + mActivity);
Log.d(getClass().getSimpleName(), "this is " + this);
publishProgress(i);
}
return null;
}
#Override
protected void onProgressUpdate(Integer... values) {
super.onProgressUpdate(values);
if (mActivity != null) {
mActivity.get().progressBar.setProgress(values[0]);
}
}
}
}
Why not just override the onPause() method in the owning Activity and cancel the AsyncTask from there?
You are absolutely right - that is why a movement away from using async tasks/loaders in the activities to fetch data is gaining momentum. One of the new ways is to use a Volley framework that essentially provides a callback once the data is ready - much more consistent with MVC model. Volley was populised in the Google I/O 2013. Not sure why more people aren't aware of this.
Personally, I just extend Thread and use a callback interface to update the UI. I could never get AsyncTask to work right without FC issues. I also use a non blocking queue to manage the execution pool.
I thought cancel works but it doesn't.
here they RTFMing about it:
""If the task has already started, then the mayInterruptIfRunning
parameter determines whether the thread executing this task should be
interrupted in an attempt to stop the task."
That does not imply, however, that the thread is interruptible. That's a
Java thing, not an AsyncTask thing."
http://groups.google.com/group/android-developers/browse_thread/thread/dcadb1bc7705f1bb/add136eb4949359d?show_docid=add136eb4949359d
You would be better off thinking of AsyncTask as something that is more tightly coupled with an Activity, Context, ContextWrapper, etc. It's more of a convenience when its scope is fully understood.
Ensure that you have a cancellation policy in your lifecycle so that it will eventually be garbage collected and no longer keeps a reference to your activity and it too can be garbage collected.
Without canceling your AsyncTask while traversing away from your Context you will run into memory leaks and NullPointerExceptions, if you simply need to provide feedback like a Toast a simple dialog then a singleton of your Application Context would help avoid the NPE issue.
AsyncTask isn't all bad but there's definitely a lot of magic going on that can lead to some unforeseen pitfalls.
As to "experiences working with it": it is possible to kill the process along with all AsyncTasks, Android will re-create the activity stack so that the user will not mention anything.