AsyncQueryHandler with ContentProvider is necessary? - android

my question is very simple: if I insert or update or delete a single row in a SQLLite database through a ContentProvider from a UIThread, is the AsyncQueryHandler's implementation necessary?
I know that the best practice is to implement the CRUD operations in a async task but also that the CRUD stantment about one row is not so heavy to perform.
Infact also Android Studio does not alert his stantment that shouldn't run on UI Thread and all the guides that I found on the net about the ContentProvider don't mention the ASyncQueryHandler. All the CRUD operations are performed on the UI Thread invoked directly the ContentProvider.

It's probably best to just go the Async route for all your ContentProvider operations. I know it can be a pain but consider this:
Your simple, one-row insert that usually takes a few milliseconds having to wait for a larger transaction to complete. Maybe you're busy in a SyncAdapter doing lots of stuff? Your little tiny insert suddenly takes much longer and might even cause an ANR.
I know it's a pretty low chance, but the chance is still there. Much better to just accept the boilerplate code and get on with it ;-)
Example boilerplate code to paste into an activity class:
private class UpdateHandler extends AsyncQueryHandler {
private final WeakReference<YourActivityClass> mActivityRef;
public UpdateHandler(YourActivityClass activity, ContentResolver cr) {
super(cr);
mActivityRef = new WeakReference<>(activity); // do a weak reference incase the update takes ages and the activity gets destroyed during
}
#Override
protected void onUpdateComplete(int token, Object cookie, int result) {
super.onUpdateComplete(token, cookie, result);
YourActivityClass exampleActivity = mActivityRef.get();
if (exampleActivity != null) {
exampleActivity .onUpdateCompleted(token);
}
}
}
public void saveStuffToDatabase() {
// do some stuff like show a progress bar or whatever
// actually do the update operation
new UpdateHandler(this, getContentResolver()).startUpdate(
0, // this will be passed to "onUpdatedComplete" in the updateHandler
null, // so will this!
uri,
values
);
}
private void onUpdateCompleted(int token) {
// this runs in the main thread after the update you started in saveStuffToDatabase() is complete
}

Related

HTTP helper class in a thread?

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.

using handler for every single task(method) in android

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);
}
}

SQLite DB accessed from multiple threads

Consider following: I've got Service, which writes into DB in AsyncTask. And my Activity reads data from DB(consider UI thread for simplicity). I access DB using SQLiteOpenHelper. I create single instance in Application onCreate() and then obtain it in service and activity. Is there any possibility that I would get my DB 'dead locked'? Previously, I used ContentProvider for such operations. Though, it is based on using single SQLiteOpenHelper instance, I decided to simplify my project by excluding ContentProvider.
Consider code:
public class App extends Application {
private OpenHelper openHelper;
#Override
public void onCreate(){
super.onCreate();
openHelper=new OpenHelper();
}
public OpenHelper getHelper(){
return openHelper;
}
}
In Activity:
OpenHelper helper=(App)getApplication().getHelper();
SQLiteDatabase db=helper.getReadableDatabase();
// Do reading
And inside Serice, in separate thread:
OpenHelper helper=(App)getApplication().getHelper();
SQLiteDatabase db=helper.getWritableDatabase();
//Do writing
Would it be safe?
UPD This might be the solution, but not sure how to use it.
Late, late answer. You're totally fine. That's the proper way to do it, actually. See my blog post: http://touchlabblog.tumblr.com/post/24474750219/single-sqlite-connection/. Dig through my profile here. Lots of examples of this. ContentProvdier is just a lot of overhead and not needed unless you're sharing data outside of your app. Transactions are good to speed things up and (obviously) improve consistency, but not needed.
Just use one SqliteOpenHelper in your app and you're safe.
My bet: it isn't safe.
To be in safer position you should use SQL transactions. Begin with beginTransaction() or beginTransactionNonExclusive() and finish with endTransaction(). Like shown here
This is my solution
I created a class and a private static object to syncronize all db access
public class DBFunctions {
// ...
private static Object lockdb = new Object();
/**
* Do something using DB
*/
public boolean doInsertRecord(final RecordBean beanRecord) {
// ...
boolean success = false;
synchronized (lockdb) {
// ...
//
// here ... the access to db is in exclusive way
//
// ...
final SQLiteStatement statement = db.compileStatement(sqlQuery);
try {
// execute ...
statement.execute();
statement.close();
// ok
success = true;
} catch (Exception e) {
// error
success = false;
}
}
return success;
}
}
I tryed using ASYNC task and it works fine .
I hope is the right way to solve the problem.
Any other suggestions ???
Good question. My first thought is it wouldn't be safe. However, according to the SQLite docs, SQLite can be used in 3 modes. The default mode is "serialized" mode:
Serialized. In serialized mode, SQLite
can be safely used by multiple threads
with no restriction
So I assume this it's compiled in serialized mode on Android.
Just saw this while I was looking for something else.
This problem looks like it would be efficiently solved by using a ContentProvider. That way both the Activity as well as the Service can use the content provider and that will take care of the Db contention issues.

Background task, progress dialog, orientation change - is there any 100% working solution?

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.

Is AsyncTask really conceptually flawed or am I just missing something?

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.

Categories

Resources