I have built a user interface in Android Studio to test the PSO algorithm in Java. I took this project on from someone else who did it last year, the person before me used AsyncTask with Boolean[] parameters to execute his application. Below is his version of this class, this is because he used a checkbox in his MainActivity that the user can check, so it can either be one or the other.
public class runTests extends AsyncTask<Boolean, Void, Void> {
#Override
protected Void doInBackground(Boolean... params) {
boolean one = params[0];
boolean custom = params[1];
if (one)
results = runTest("TestOne");
else if (custom) {
double[] re = runTest("customTest");
if (re != null) results = re;
}
return null;
}
#Override
protected void onPostExecute(Void v) {
// execution of result of Long time consuming operation
pd.dismiss();
if (results[0] != -1 || results != null) {
loadIntent(results);
}
}
#Override
protected void onPreExecute() {
pd = ProgressDialog.show(MainActivity.this, "Busy", "Algorithm is currently executing");
}
}
Whereas my code doesn't have a checkbox it only needs to implement one test rather than having the option of two. I just want to run "CustomUseCase" and nothing else. I don't want to use a boolean parameter however I still want to have AsyncTask. Please help me!
public class runTests extends AsyncTask<Boolean, Void, Void> {
#Override
protected Void doInBackground(Boolean... params) { //sort this out
boolean one = params[0];
boolean custom = params[1];
if (one)
results = runTest("CustomUseCase"); //i only want to run this one!!!
else if (custom) {
double[] re = runTest("testOne"); //I don't need this, I dont want to run this test
if (re != null) results = re;
}
return null;
}
#Override
protected void onPostExecute(Void v) {
// execution of result of Long time consuming operation
if(pd!=null){
pd.dismiss();
pd = null;
}
if (results[0] != -1 || results != null) {
loadIntent(results);
}
}
#Override
protected void onPreExecute() {
pd = ProgressDialog.show(ParticleActivity.this, "Busy", "Algorithm is currently executing");
}
}
First, you need to change
if (results[0] != -1 || results != null) {
loadIntent(results);
}
to
if (results != null && results.length > 0 && results[0] != -1) {
loadIntent(results);
}
That is an example of a "short circuiting" if statement. The logical AND (&&) operator will cause the entire statement to fail if results is null, otherwise it will evaluate the next logic statement results[0] != -1 with no chance of a NullPointerException.
Try this as well
public class runTests extends AsyncTask<Void, Void, Void> {
#Override
protected Void doInBackground(Void... params) { //sort this out
results = runTest("CustomUseCase"); //i only want to run this one!!!
}
...
}
UPDATE
do I return null when executing the doInBackground?
To clarify how the Template Type List (ie, <Void,Void,Void>) works, here is an example. Here, we have <URL, Integer, Long>. Notice how doInBackground takes an array of URL (urls) and returns a Long, onProgressUpdate takes an Integer array, and onPostExecute takes a Long.
private class DownloadFilesTask extends AsyncTask<URL, Integer, Long> {
protected Long doInBackground(URL... urls) {
long totalSize = urls.length;
...
return totalSize;
}
protected void onProgressUpdate(Integer... progress) {
setProgressPercent(progress[0]);
}
protected void onPostExecute(Long result) {
showDialog("Downloaded " + result + " bytes");
}
}
For <Void, Void, Void> we would have (empty function argument list "()" corresponds to Void):
private class DownloadFilesTask extends AsyncTask<Void, Void, Void> {
protected Void doInBackground() {
...
return;
}
protected void onProgressUpdate() {
setProgressPercent("hi");
}
protected void onPostExecute() {
showDialog("bye");
}
}
Related
My applications hang for a bit when I populate data from realm database to my listview.
So I planned to do it using Asynchronously so meanwhile data is collected I display a Loading dialogue here is the Code.
Already referred to this question by not able to implement in my case.
private class YourAsyncTask extends AsyncTask<String, String, RealmResults> {
ProgressDialog progressDialog;
#Override
protected void onPreExecute() {
// start loading animation maybe?
progressDialog = ProgressDialog.show(DictionarySscWords.this,
"ProgressDialog",
"Loading all words!");
}
#Override
protected RealmResults doInBackground(String... params) {
RealmConfiguration realmConfig = new RealmConfiguration.Builder(context).build();
Realm.setDefaultConfiguration(realmConfig);
realm = realm.getDefaultInstance();
RealmQuery<Word> query = realm.where(Word.class);
for (int i = 0; i < words_for_ssc[Integer.parseInt(params[0])].length; i++) {
if (i == words_for_ssc[Integer.parseInt(params[0])].length - 1) {
query = query.equalTo("word", words_for_ssc[Integer.parseInt(params[0])][i]);
} else {
query = query.equalTo("word", words_for_ssc[Integer.parseInt(params[0])][i])
.or();
}
}
sscresult = query.findAll(); //error 1
return sscresult;
}
#Override
protected void onPostExecute(RealmResults r) {
progressDialog.dismiss();
list.setAdapter(new MyAdapter(sscresult)); //error 2
realm.close();
}
}
ok so there are two problems if anyone can be solved my application would be error-free
if I try to run list.setAdapter(new MyAdapter(sscresult)); in background process the error is:-
this can run only in UI thread
if try to run in postExecute error is :-
Realm access from incorrect thread. Realm objects can only be accessed on the thread they were created.
I am not able to solve this issue please help
You can have your query evaluated on a background thread using asynchronous query API in Realm.
private OrderedRealmCollectionChangeListener<RealmResults<User> callback = new OrderedRealmCollectionChangeListener<>() {
#Override
public void onChange(RealmResults<User> results, OrderedCollectionChangeSet changeSet) {
if (changeSet == null) {
// The first time async returns with an null changeSet.
} else {
// Called on every future update.
}
}
};
private RealmResults<User> result;
public void onStart() {
result = realm.where(User.class).findAllAsync();
result.addChangeListener(callback);
}
But if you give the RealmResults to a RealmRecyclerViewAdapter, then this is all automatic.
P.S. not closing Realm instance in doInBackground() is like, S-class horrible mistake. Please close your Realm instance on non-looping background threads.
Specifically the following:
// private class YourAsyncTask extends AsyncTask<String, String, RealmResults> {
//
// ProgressDialog progressDialog;
// #Override
// protected void onPreExecute() {
// // start loading animation maybe?
// progressDialog = ProgressDialog.show(DictionarySscWords.this,
// "ProgressDialog",
// "Loading all words!");
// }
//
// #Override
// protected RealmResults doInBackground(String... params) {
// RealmConfiguration realmConfig = new RealmConfiguration.Builder(context).build();
// Realm.setDefaultConfiguration(realmConfig);
// realm = realm.getDefaultInstance();
// RealmQuery<Word> query = realm.where(Word.class);
//
// for (int i = 0; i < words_for_ssc[Integer.parseInt(params[0])].length; i++) {
// if (i == words_for_ssc[Integer.parseInt(params[0])].length - 1) {
//
// query = query.equalTo("word", words_for_ssc[Integer.parseInt(params[0])][i]);
// } else {
// query = query.equalTo("word", words_for_ssc[Integer.parseInt(params[0])][i])
// .or();
//
// }
//
// }
// sscresult = query.findAll(); //error 1
// return sscresult;
//
// }
//
// #Override
// protected void onPostExecute(RealmResults r) {
// progressDialog.dismiss();
// list.setAdapter(new MyAdapter(sscresult)); //error 2
// realm.close();
// }
//}
and
public class MyActivity extends AppCompatActivity {
private RealmResults<Word> words;
private Realm realm;
private WordAdapter wordAdapter;
#BindView(R.id.recycler_view)
RecyclerView recyclerView;
#Override
public void onCreate(Bundle bundle) {
super.onCreate(bundle);
setContentView(R.layout.my_activity);
ButterKnife.bind(this);
realm = Realm.getDefaultInstance();
words_for_ssc = ...
RealmQuery<Word> query = realm.where(Word.class);
String[] array = words_for_ssc[Integer.parseInt(params[0])];
for (int i = 0; i < array.length; i++) {
query = query.equalTo("word", array[i]);
if (i != array.length - 1) {
query = query.or();
}
}
words = query.findAllSortedAsync("word");
wordAdapter = new WordAdapter(words);
recyclerView.setLayoutManager(new LinearLayoutManager(this));
recyclerView.setAdapter(wordAdapter);
}
#Override
public void onDestroy() {
super.onDestroy();
realm.close();
realm = null;
}
}
public class WordAdapter extends RealmRecyclerViewAdapter<Word, WordViewHolder> {
public class WordAdapter(OrderedRealmCollection<Word> words) {
super(words, true);
}
#Override
public WordViewHolder onCreateViewHolder(...) {
...
}
#Override
public void onBindViewHolder(WordViewHolder holder, int position) {
holder.bind(getData().get(position));
}
public static class WordViewHolder extends RecyclerView.ViewHolder {
public WordViewHolder(View view) {
super(view);
ButterKnife.bind(this, view);
}
public void bind(Word word) {
...
}
}
}
I think a cleaner solution to your problem without changing much of the code can be written below. In this case, everything that realm does happen on the background thread inside doInBackground. The realm instance is also closed on the thread it was created.
Now what I did basically is that I extracted a deep copy of the list of Words from RealmResult from realm.copyFromRealm(sscresult) which is completely detached from realm and can be moved around and modified inside any thread. All these objects are now free from realm and can be used in onPostExecute without any worries. The only thing you need to modify is the MyAdapter constructor which doesn't take a RealmResult but instead a List of Words which is exactly what you need and can be iterated the same way as RealmResult was.
The only downside of this approach is that the list of Words will not get synced automatically since they're detached and their value won't change automatically if they get altered inside Realm from somewhere else. But I'm pretty sure though that it won't bother you.
I'm also going to attach an official reference for realm.copyFromRealm() which is here.
private class YourAsyncTask extends AsyncTask<String, String, List<Word>> {
ProgressDialog progressDialog;
#Override
protected void onPreExecute() {
// start loading animation maybe?
progressDialog = ProgressDialog.show(DictionarySscWords.this,
"ProgressDialog",
"Loading all words!");
}
#Override
protected List<Word> doInBackground(String... params) {
RealmConfiguration realmConfig = new RealmConfiguration.Builder(context).build();
Realm.setDefaultConfiguration(realmConfig);
try(realm = realm.getDefaultInstance()) {
RealmQuery<Word> query = realm.where(Word.class);
for (int i = 0; i < words_for_ssc[Integer.parseInt(params[0])].length; i++) {
if (i == words_for_ssc[Integer.parseInt(params[0])].length - 1) {
query = query.equalTo("word", words_for_ssc[Integer.parseInt(params[0])][i]);
} else {
query = query.equalTo("word", words_for_ssc[Integer.parseInt(params[0])][i])
.or();
}
}
// Here's the sort. Use findAllSorted instead.
// You can change Sort.ASCENDING to Sort.DESCENDING to reverse
// the order.
sscresult = query.findAllSorted("word", Sort.ASCENDING);
// This is where the magic happens. realm.copyFromRealm() takes
// a RealmResult and essentially returns a deep copy of the
// list that it contains. The elements of this list is however
// completely detached from realm and is not monitored by realm
// for changes. Thus this list of values is free to move around
// inside any thread.
ArrayList<Word> safeWords = realm.copyFromRealm(sscresult);
realm.close();
return safeWords;
}
}
#Override
protected void onPostExecute(List<Word> words) {
progressDialog.dismiss();
// Please note here MyAdaptor constructor will now take the
// list of words directly and not RealmResults so you slightly
// modify the MyAdapter constructor.
list.setAdapter(new MyAdapter(words));
}
}
Hope it helps!
My app consists of a custom list view and each row contains text.
My MainActivity puts the translated strings into a HashMap called feedData which is retrieved by a CustomListView Class where the TextView for each row is set.
Everything about my custom list view performs perfectly. I am getting this error because I'm implementing the Google API Translation which forces me to use AsyncTask or a Thread. The values populate into each row but only when I scroll up and down.
MainActivity
TranslateString(feedText, feedData);
arrayFeedList.add(feedData);
Translate String Method:
public void TranslateString(final String mText, final HashMap<String, String> mList) {
AsyncTask<Void, Void, Void> asyncTask = new AsyncTask<Void, Void, Void>() {
#Override
protected Void doInBackground(Void... voids) {
try {
Translate translate = TranslateOptions.builder().setApiKey("AIzaSyB7cCDBbeoZ2tYTH-Ynv25OaPraLmTG7Hw").build().getService();
Translation translation =
translate.translate(
mText,
TranslateOption.sourceLanguage("tr"),
TranslateOption.targetLanguage("en"));
returnedString = translation.getTranslatedText();
check = 1;
} catch (Exception e) {
check = 2;
}
return null;
}
#Override
protected void onPostExecute(Void result) {
if(check == 1){
mList.put("feed", returnedString);
}
else if(check == 2){
mList.put("feed", mText + " " + getContext().getString(R.string.translationfailed) + " in catch");
}
}
};
asyncTask.execute();
}
}
Doing this in my MainActivity fixed my problem: (After deleting the AsyncTask Method Completely)
if (android.os.Build.VERSION.SDK_INT > 9)
{
StrictMode.ThreadPolicy policy = new StrictMode.ThreadPolicy.Builder().permitAll().build();
StrictMode.setThreadPolicy(policy);
}
I am having some problem when trying to pass a String and object to AsyncTask class. So when my button on click, it should pass in a String and an EventReview object into the AsyncTask class:
viewDtlEventBtn.setOnClickListener(new OnClickListener(){
public void onClick(View v){
new GetEventDetailAsyncTask(new GetEventDetailAsyncTask.OnRoutineFinished() {
public void onFinish() {
//Get the values returned from AsyncTask and pass it to another activity
}
}).execute(String.valueOf(eventIDTV.getText()));
}
});
And inside my AsyncTask class, I am getting String as the parameter:
public static class GetEventDetailAsyncTask extends AsyncTask<String, Integer, Double> {
EventController eventCtrl = new EventController();
Context context;
public interface OnRoutineFinished { // interface
void onFinish();
}
private OnRoutineFinished mCallbacks;
public GetEventDetailAsyncTask(OnRoutineFinished callback) {
mCallbacks = callback;
}
public GetEventDetailAsyncTask() {
} // empty constructor to maintain compatibility
public GetEventDetailAsyncTask(Context context){
this.context = context;
}
#Override
protected Double doInBackground(String... params) {
try {
eventCommentModel = eventCtrl.getEventCommentByID(params[0]);
} catch (JSONException e) {
e.printStackTrace();
}
return null;
}
protected void onPostExecute(Double result) {
if (mCallbacks != null)
mCallbacks.onFinish(); // call interface on finish
}
protected void onProgressUpdate(Integer... progress) {
}
}
So I wonder is there any possible way to pass in a String and EventReview object to the execute() and then when doInBackground(), each execute each method. Any guides?
Thanks in advance.
You can pass String and your custom class' object in Object[] in asynctask.
Object[] obj = new Object[2];
obj[0] = "my data";
obj[1] = myEventReviewObj;
new GetEventDetailAsyncTask().execute(obj);
AsyncTask:
public static class GetEventDetailAsyncTask extends AsyncTask<Object, Integer, Double> {
#Override
protected Double doInBackground(Object... params) {
String paramStr = "";
EventReview eventReview = null;
if(params[0] instanceof String && params[1] instanceof EventReview) {
paramStr = (String) params[0];
eventReview = (EventReview) params[1];
}
else {
eventReview = params[0];
paramStr = params[1];
}
try {
//perform operation using String and Object as per your need
} catch (JSONException e) {
e.printStackTrace();
}
return null;
}
}
Hope this helps.
You can change the class to accept Objects as input:
public static class GetEventDetailAsyncTask extends AsyncTask<Object, Integer, Double>
and check if the object is an instance of String or of EventReview
#Override
protected Double doInBackground(Object... params) {
if(params[0] instanceof String) // it is String
else if(params[0] instanceof EventReview) // it is EventReview
}
Create custom constructor and save the passed variables in your AsyncTask:
public static class GetEventDetailAsyncTask extends AsyncTask<String, Integer, Double> {
EventReview eventReview;
private OnRoutineFinished mCallbacks;
String string;
Context context;
public GetEventDetailAsyncTask(OnRoutineFinished callback, String str, EventReview review) {
mCallbacks = callback;
string = str;
eventReview = review;
}
...
}
And then call the AsyncTask by passing your vars:
public void onClick(View v){
new GetEventDetailAsyncTask(
new GetEventDetailAsyncTask.OnRoutineFinished() {
public void onFinish() {
// Get the values returned from AsyncTask and pass it to another activity
}
},
String.valueOf(eventIDTV.getText(),
eventReview).execute());
}
How can I call a function defined in class#A from class#B? Class#B extends AsynchTask and fetches remote data from a web service. The class#A function I am attempting to call from class#B is used to send the retrieved remote data for Class#A to display.
I am trying to pass the current instance of class#A to class#B using this but that just passes the context so the functions are not recognized.
I also tried using static but as the function runs a new thread, defining the function as static generates a compiler error.
The code I am trying to call is as follows:
public void test(List<DATA> Data){
this.Data = Data;
runOnUiThread(new Runnable() {
#Override
public void run() {
// TODO Auto-generated method stub
for(DATA data : MainActivity.this.Data){
Toast.makeText(MainActivity.this, data.title, Toast.LENGTH_SHORT).show();
}
}
});
}
This is how you would do it:
In MainActivity:
ClassBAsyncTask mat = new ClassBAsyncTask(this, .., ..);
ClassBAsyncTask's contructor:
public ClassBAsyncTask(MainActivity context, .., ..) {
mContext = context;
....
....
}
To call MainActivity's method test(List<DATA>) from within ClassBAsyncTask:
((MainActivity)mContext).test(yourListVariable);
Look into weak references to make sure that the AsyncTask does not use mContext when the activity no longer exists.
From your question, I understood that u want to show the result of asynctask in another class. You can use onProressUpdate api of asynctask. You can do the work in doInBackground and then call publishProgress(value) whihc in turn calls onProgressUpdate and run on UI thred.
private class DownloadFilesTask extends AsyncTask<URL, Integer, Long> {
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));
// Escape early if cancel() is called
if (isCancelled()) break;
}
return totalSize;
}
protected void onProgressUpdate(Integer... progress) {
setProgressPercent(progress[0]);
}
Other way:
You can define a handler in class#A and call it from class#B
Sorry if i mis understood your question.
as I understand you start from A an AsyncTask B, cause web fetching only works in async way in Android. You want the result of the fetch to be accessible for an object in A.
A would code something like this.
public class A extends FragmentActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
TextView fetch = (TextView) findViewById(R.id.fetch);
new B(getApplicationContext(),fetch).execute();
// This will update a TextView when fetching is done.
}
}
public class B extends AsyncTask<Void, Integer, Void> {
Context c;
TextView f;
public B(Context c, TextView f) {
super();
this.f = f;
this.c = c;
}
#Override
protected Void doInBackground(Void... params) {
// TODO: the fetching: fetch()
String t = fetch();
f.setText();
return null;
}
}
You can do other tricks with pre and post execute methods. I hope I could help!
You can make an interface ie.:
public interface RecieveDataDelegate {
public void receiveData(Object dataFromWebService, boolean success);
}
Have Class#A implement the interface. And when calling the asyncTask from Class#A pass itself to a contructor for the AsyncTask that has a parameter of type ReceiveDataDelegate.
new AsyncTask1 (this).execute();
And setup AsyncTask with a constructor and send data back to Class#A onPostExecute() so you can update ui.
class AsyncTask1 extends AsyncTask<Void, Void, Boolean> {
Object dataReceived ;
protected RecieveDataDelegate delegate;
public AsyncTask1 (RecieveDataDelegate delegate) {
this.delegate = delegate;
}
protected Boolean doInBackground(Void... params) {
//do your service request and set dataRecieved
}
protected void onPostExecute(Boolean result) {
super.onPostExecute(result);
delegate.RecieveDataDelegate (dataReceived, result.booleanValue());
}
That way any class that implements the interface can use the same asyncTask.
In one of my Activities I do have up to six different AsyncTasks that may run on different events. All AsyncTasks handle orientation changes gracefully (at least I hope so). In onRetainNonConfigurationInstance() I do return the AsyncTask object of a currently running AsyncTask. Later, during onCreate(), I need to find out what AsyncTask object is returned from getLastNonConfigurationInstance().
I use the Activity context in all onPostExecute() methods to get the new activity (if there is a new one). I found this concept here on StackOverflow and did modify it a little bit because I don't like that "Trash running tasks on orientation change" paradigma. Hope this concept is correct.
In the code shown below you'll find two different AsyncTasks. During onRetainNonConfigurationInstance() I will return the currently running AsyncTask. My problem is within onCreate(). How to find out what object is returned? What AsyncTask was running when orientation change bumped in?
Both AsyncTasks are different in many areas (not shown here) so I didn't use an own extended AsyncTask base object.
Many thanks in advance.
public class MyActivity extends ListActivity {
// First AsyncTask
private class MyLoadAsyncTask extends AsyncTask<Void, Void, Cursor> {
private MyActivity context;
private MyProgressDialog dialog;
public MyLoadAsyncTask(MyActivity context) {
super();
this.context = context;
}
#Override
protected Cursor doInBackground(Void... voids) {
return MyApplication.getSqliteOpenHelper().fetchSoomething();
}
#Override
protected void onPostExecute(Cursor cursor) {
if (dialog != null) {
dialog.dismiss();
dialog = null;
}
if (cursor != null) {
context.startManagingCursor(cursor);
context.adapter = new InternetradiosAdapter(context,
R.layout.row,
cursor,
new String[] { "text1",
"text2" },
new int[] { R.id.row_text1,
R.id.row_text2 } );
if (context.adapter != null) {
context.setListAdapter(context.adapter);
}
}
context.loadTask = null;
}
#Override
protected void onPreExecute () {
dialog = MyProgressDialog.show(context, null, null, true, false);
}
}
private class MyDeleteAsyncTask extends AsyncTask<Void, Void, Boolean> {
// Second AsyncTask
private MyActivity context;
private MyProgressDialog dialog;
private long id;
public MyDeleteAsyncTask(MyActivity context, long id) {
super();
this.context = context;
this.id = id;
}
#Override
protected Boolean doInBackground(Void... voids) {
return MyApplication.getSqliteOpenHelper().deleteSomething(id);
}
#Override
protected void onPostExecute(Boolean result) {
if (dialog != null) {
dialog.dismiss();
dialog = null;
}
if (result) {
context.doRefresh();
}
context.deleteTask = null;
}
#Override
protected void onPreExecute () {
dialog = MyProgressDialog.show(context, null, null, true, false);
}
}
private MyDeleteAsyncTask deleteTask;
private MyLoadAsyncTask loadTask;
#Override
public void onCreate(Bundle bundle) {
// ...
// What Task is returned by getLastNonConfigurationInstance()?
// ...
// xxxTask = (MyXxxAsyncTask) getLastNonConfigurationInstance();
// ...
if (deleteTask != null) {
deleteTask.context = this;
deleteTask.dialog = MyProgressDialog.show(this, null, null, true, false);
} else if (loadTask != null) {
loadTask.context = this;
loadTask.dialog = MyProgressDialog.show(this, null, null, true, false);
} else {
loadTask = new MyLoadAsyncTask(this);
loadTask.execute();
}
}
#Override
public Object onRetainNonConfigurationInstance() {
if (deleteTask != null) {
if (deleteTask.dialog != null) {
deleteTask.dialog.dismiss();
deleteTask.dialog = null;
return deleteTask;
}
} else if (loadTask != null) {
if (loadTask.dialog != null) {
loadTask.dialog.dismiss();
loadTask.dialog = null;
return loadTask;
}
return null;
}
}
In your onCreate() add:
Object savedInstance = getLastNonConfigurationInstance();
if(savedInstance instanceof MyDeleteAsyncTask){
//it's a MyDeleteAsyncTask
}else if(savedInstance instanceof MyLoadAsyncTask){
//it's a MyLoadAsyncTask
}
I've found that the best way to deal with activities that have multiple running AsyncTasks inside of them is to actually return an object that contains all of the running ones and automatically reinitialize all of their contexts in onCreate(). E.g.
private class AsyncTaskList() {
List<ActivityTask> tasks; //interface all of your AsyncTasks implement
public void addTask() { /* add to list*/ }
public void completeTask { /* remove from list */ }
public void attachContext(Activity activity) {
for ( ActivityTask task : tasks) {
//You can also check the type here and do specific initialization for each AsyncTask
task.attachContext(activity);
}
}
}
public void onCreate(Bundle bundle) {
AsyncTaskList taskList = (AsyncTaskList) getLastNonConfigurationInstance();
if (taskList != null) {
taskList.attachContext(this);
}
...
}
Now you just need to add and remove the tasks when they start/finish.