after few desperate days I have finally created almost working example.
The goal: in onCreate I want to download and parseXML files in AsyncTask, show progress dialog, update UI and close the dialog.
The problem: When orientation changes Activity is restarted and AsyncTask loses reference to it. There are a lot of questions and blogs about it. But I cant find out why this particular solution doesnt work. Or how android handles dialogs in this case.
The state: When I start an app everything is ok. I can rotate the device and I can manually start the task again via menu. BUT after task finishes and I change the orientation again dialogs pops up (as expected) and nothing else happens. No progress change, no dialog dismiss. AsyncTask finishes normally.
The code:
package com.test;
import java.io.*;
import java.net.URL;
import java.net.URLConnection;
import java.util.*;
public class Test extends TabActivity {
DownloadFileAsync task;
ProgressDialog progressDialog;
static final int PROGRESS_DIALOG = 0;
private static Data data;
/** Called when the activity is first created. */
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
/* - Run from different locations bug - */
//http://code.google.com/p/android/issues/detail?id=2373
if (!isTaskRoot()) {
final Intent intent = getIntent();
final String intentAction = intent.getAction();
if (intent.hasCategory(Intent.CATEGORY_LAUNCHER) &&
intentAction != null && intentAction.equals(Intent.ACTION_MAIN)) {
finish();
}
}
/* - /Run from different locations bug */
/* -------------- Tabs ---------------- */
Resources res = getResources();
TabHost mTabHost = getTabHost();
mTabHost.addTab(mTabHost.newTabSpec("overview").setIndicator("MYTAB1",res.getDrawable(R.drawable.ic_tab_home)).setContent(R.id.tab1));
mTabHost.setCurrentTab(0);
/* -------------- /Tabs --------------- */
/* -------------- /Data --------------- */
task = (DownloadFileAsync)getLastNonConfigurationInstance();
if(task!= null) {
task.setActivity(this);
} else {
if(data == null) {
File datafile = this.getFileStreamPath("data.dat");
if(datafile.exists()){
//Log.d("log", "File exists!");
try {
long time = System.currentTimeMillis();
ObjectInputStream obj = new ObjectInputStream(new FileInputStream(datafile));
data = (Data)obj.readObject();
obj.close();
Log.d("time", "loaded in:"+(System.currentTimeMillis()- time));
if(data.isUpToDate() || !isOnline()){
update();
}
} catch (Exception e) {
e.printStackTrace();
datafile.delete();
data = null;
}
//Log.d("log", "Passed?");
}
}
/* DEBUG if(data == null || !data.isUpToDate())*/ this.synchronize();
}
/* -------------- /Data --------------- */
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
menu.add("Synchronize").setIcon(R.drawable.ic_menu_refresh);
return true;
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
synchronize();
return super.onOptionsItemSelected(item);
}
@Override
public Object onRetainNonConfigurationInstance() {
if(task != null) task.setActivity(null);
return(task);
}
protected Dialog onCreateDialog(int id) {
switch (id) {
case PROGRESS_DIALOG:
progressDialog = new ProgressDialog(this);
progressDialog.setMessage("Aktualizuji ...");
progressDialog.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);
progressDialog.setCancelable(false);
//progressDialog.show();
return progressDialog;
default:
return null;
}
}
public void update() {
}
private void onTaskCompleted() {
task = null;
dismissDialog(PROGRESS_DIALOG);
Log.d("tok","Task.onComplete");
update();
}
public void synchronize(){
if(isOnline()) {
showDialog(PROGRESS_DIALOG);
progressDialog.setProgress(0); // <-- this is the last time progressDialog updates
task = new DownloadFileAsync(this);
task.execute();
}
}
public boolean isOnline() {
ConnectivityManager cm =
(ConnectivityManager) getSystemService(WaspActivity.CONNECTIVITY_SERVICE);
NetworkInfo netInfo = cm.getActiveNetworkInfo();
if (netInfo != null && netInfo.isConnectedOrConnecting()) {
return true;
}
return false;
}
private static class DownloadFileAsync extends AsyncTask<String, String, String> {
private Data tempData;
private Test activity;
private int progress = 0;
private File metafile;
private File tempDir;
private FileOutputStream fos;
public DownloadFileAsync(Test activity) {
this.setActivity(activity);
... some more init ...
}
public void setActivity(Test activity) {
this.activity = activity;
}
@Override
protected void onPreExecute() {
super.onPreExecute();
tempData = new Data();
}
@Override
protected String doInBackground(String... aurl) {
try {
... some heavy load ...
//this.progress = someValue;
} catch (Exception e) {
Log.d("Error", "Error while processing files. Code:"+e.getMessage());
e.printStackTrace();
}
//Log.d("time","Task "+(System.currentTimeMillis() - time));
return null;
}
protected void onProgressUpdate(String... progress) {
if(activity != null) activity.progressDialog.setProgress(this.progress);
}
@Override
protected void onPostExecute(String unused) {
data = tempData;
tempData = null;
if(activity != null) {
activity.onTaskCompleted();
activity = null;
}
}
}
}
Yesterday, I wrote a blog post which describes handling configuration changes using retained Fragment
s.
The TL;DR is to use host your AsyncTask
inside a Fragment
, call setRetainInstance(true)
on the Fragment
, and report the AsyncTask
's progress/results back to it's Activity
through the retained Fragment
.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With