Search code examples
androidandroid-notificationsandroidhttpclient

Android: Retaining HttpClient and AsyncTask when relaunching Activity through Notification


I have an Android application that uploads files to my webservice using HttpClient. The app has a notification that updates as the files are uploaded and allows the user to minimize the application and do other things as it goes in the background. I have an issue when I tap on the Notification and the Activity relaunches. My HttpClient connection contains cookies with the session information to the webserver and this connection is lost when the activity is restarted by taping the notification. It seems that the old fragments that were doing the uploads are destroyed on the restart (I know this because I see the asynctask gracefully stop when the relaunch happens). I have tried to use a fragment that is retained but when the app relaunches it is unable to locate the fragment despite the fact that I set the fragment as retained.

So my question is, how can I relaunch an activity and retain both my HttpClient connection and my AsyncTask (I need this in the case where uploads are still going during the relaunch)?


Building the Notification (as recommended by Android dev guide)

Builder builder = new NotificationCompat.Builder( getActivity() );
builder.setSmallIcon( R.drawable.ic_stat_notify_upload );
builder.setContentTitle( "Uploads" );
builder.setContentText( "Uploading " + fileCount + " file(s)" );
builder.setProgress( fileCount, 0, false );
Intent notificationActionIntent = new Intent( getActivity(), UploaderActivity.class );
TaskStackBuilder stackBuilder = TaskStackBuilder.create( getActivity() );
stackBuilder.addParentStack( UploaderActivity.class );
stackBuilder.addNextIntent( notificationActionIntent );
PendingIntent notificationPendingIntent = stackBuilder.getPendingIntent( 0, PendingIntent.FLAG_UPDATE_CURRENT );
builder.setContentIntent( notificationPendingIntent );

RetainFragment code (UploadTask extends AsyncTask and CasRestProxyClient extends HttpClient)

public class RetainFragment extends Fragment {

    private CasRestProxyClient casClient;
    private UploadEftTask uploadTask;

    @Override
    public void onCreate ( Bundle savedInstanceState ) {
        super.onCreate( savedInstanceState );
        setRetainInstance( true );
    }
    public ICasRestProxyClient getCasClient() {
        return casClient;
    }

    public void setCasClient(ICasRestProxyClient casClient) {
        this.casClient = casClient;
    }

    public UploadEftTask getUploadTask() {
        return uploadTask;
    }

    public void setUploadTask(UploadEftTask uploadTask) {
        this.uploadTask = uploadTask;
    }

}

Uploader Activity (cut down to what I thought was relevant)

public class UploaderActivity extends Activity {

    private static final String RetainFragmentIdString = "RETAIN_FRAGMENT";
    private RetainFragment retainFragment;

    @Override
    protected void onCreate(Bundle savedInstanceState) {

        super.onCreate(savedInstanceState);

        LoggerConfigurator.configure();
        setContentView(R.layout.activity_uploader);

        FragmentManager fragMgr = getFragmentManager();
        Fragment dataFragment = fragMgr.findFragmentByTag( RetainFragmentIdString );
        if ( dataFragment != null && dataFragment instanceof RetainFragment ) {
            retainFragment = (RetainFragment)dataFragment;
            // Restore state here...
        } else {
            retainFragment = new RetainFragment();
            FragmentTransaction fragTrans = fragMgr.beginTransaction();
            fragTrans.add( retainFragment, RetainFragmentIdString );
            fragTrans.commit();
            // Show initial state here...
        }

    }

    @Override
    public void onDestroy ( ) {
        retainFragment.setCasClient( uploadFragment.getCasClient() );
        retainFragment.setUploadTask( uploadFragment.getUploadTask() );
        super.onDestroy();
    }

}

Solution

  • I ended up getting this to do what I wanted by using a BoundService that implements a Messenger interface (Android Dev Doc). Following this alone still caused the service to destroy itself when the app closed. The key to prevent the service destruction and allow a reconnect is to call startService() before calling bindService(). This will keep the service running in the background until my app calls stopService() (in my case when the user explicitly logs out).

    Called in the UploaderActivity.onStart() method:

        Intent initService = new Intent( this, UploaderService.class );
        startService( initService );
        // serviceConnection is a member of UploaderActivity of type 
        // android.content.ServiceConnection that handles when the service binds.
        // This needs to be a class member to unbind() when the activity is destroyed
        bindService( initService, serviceConnection, Context.BIND_AUTO_CREATE );