Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

AsyncTask with ProgressDialog vs orientation change

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

}
like image 586
Jan Pfeifer Avatar asked Feb 21 '23 05:02

Jan Pfeifer


1 Answers

Yesterday, I wrote a blog post which describes handling configuration changes using retained Fragments.

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.

like image 197
Alex Lockwood Avatar answered Mar 04 '23 12:03

Alex Lockwood