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

}
有帮助吗?

解决方案 2

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

其他提示

This is a situation where I would probably use an IntentService over an AsyncTask. The basics on how to set this up can be found here in the android docs. The benefit of using a IntentService is that it can continue to operate once the Activity (or in your case the Fragment) that started it is destroyed. You can use intents to communicate between your Activity or Fragment and the IntentService so that you can notify other components of the IntentService's progress.

Set up/conversion from an AsyncTask model is pretty easy. The link to the docs will show you how to start/stop the service. Beyond that, because the service runs in the background, whatever you have in doInBackground in your AsyncTask can go wherever makes sense in your Service implementation. The just use a BroadcastReceiver in components you want receive updates.

Update - Service vs. IntentService

There are two kinds of Services in android. A normal Service which persists in the background, independent of an Activity, but runs on the main thread and an IntentService, which also persists in the background but runs on a separate worker thread. You can find a basic breakdown of the different ways to use separate threads in the link provided.

To clarify above, I would use an IntentService because of its ability to run off the main thread. This is crucial when performing I/O operations and has been required since API 3.0. IntentService also allows for updates back to the main thread, as described by using Intent. For more comparing the two types of Service see this post

许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top