Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Android - Best practice for retrying IntentService

My Android app sends a load of files to Amazon S3. Each file URI is passed in separate calls to IntentService which performs the upload.

However, I'm wondering what is the best way to handle failures... Should I detect the failure with my IntentService's onHandleIntent() method and retry within that same method, OR should I allow the failure to be handled outside of the method (and if so, how?)?

I'm personally leaning towards the first suggestion as I would prefer any file to be successfully uploaded before subsequent files are attempted to be uploaded, but I am not sure if detecting errors and performing retries within the onHandleIntent() method is good practice(?).

like image 973
ban-geoengineering Avatar asked May 29 '14 12:05

ban-geoengineering


People also ask

Why is IntentService deprecated in Android?

This class was deprecated in API level 30. IntentService is subject to all the background execution limits imposed with Android 8.0 (API level 26). Consider using WorkManager or JobIntentService , which uses jobs instead of services when running on Android 8.0 or higher.

Does IntentService run on background thread?

Fundamentals. The IntentService executes tasks on a single background thread—i.e., all tasks are executed sequentially.

What are the limitations of the IntentService?

Limitations / DrawbacksThe Service may block the Main Thread of the application. The IntentService cannot run tasks in parallel. Hence all the consecutive intents will go into the message queue for the worker thread and will execute sequentially.

What is difference between service and IntentService in Android?

If the task doesn't require any and also not a very long task you can use service. If the Background task is to be performed for a long time we can use the intent service. Service will always run on the main thread.


1 Answers

This is a very nice question. I was asked this in one interview and i had failed to answer it. But i will try and answer it here after some searching for the answer.

Step-1: You start an IntentService. You can start an IntentService either from an Activity or a Fragment.

/* Starting Download Service */

DownloadResultReceiver mReceiver = new DownloadResultReceiver(new Handler());
mReceiver.setReceiver(this);
Intent intent = new Intent(Intent.ACTION_SYNC, null, this, DownloadService.class);

/* Send optional extras to Download IntentService */
intent.putExtra("url", url);
intent.putExtra("receiver", mReceiver);
intent.putExtra("requestId", 101);
startService(intent);

Step-2: Make the class that extends IntentService.

public class DownloadService extends IntentService {

    public static final int STATUS_RUNNING = 0;
    public static final int STATUS_FINISHED = 1;
    public static final int STATUS_ERROR = 2;

    private static final String TAG = "DownloadService";

    public DownloadService() {
        super(DownloadService.class.getName());
    }

    @Override
    protected void onHandleIntent(Intent intent) {

        Log.d(TAG, "Service Started!");

        final ResultReceiver receiver = intent.getParcelableExtra("receiver");
        String url = intent.getStringExtra("url");

        Bundle bundle = new Bundle();

        if (!TextUtils.isEmpty(url)) {
            /* Update UI: Download Service is Running */
            receiver.send(STATUS_RUNNING, Bundle.EMPTY);

            try {
                String[] results = downloadData(url);//make your network call here and get the data or download a file.

                /* Sending result back to activity */
                if (null != results && results.length > 0) {
                    bundle.putStringArray("result", results);
                    receiver.send(STATUS_FINISHED, bundle);
                }
            } catch (Exception e) {

                /* Sending error message back to activity */
                bundle.putString(Intent.EXTRA_TEXT, e.toString());
                receiver.send(STATUS_ERROR, bundle);
            }
        }
        Log.d(TAG, "Service Stopping!");
        this.stopSelf();
    }
}

Step-3: To receive results back from IntentService, we can use subclass of ResultReciever. Once results are sent from Service the onReceiveResult() method will be called. Your activity handles this response and fetches the results from the Bundle. Once results are recieved, accordingly the activity instance updates the UI.

public class DownloadResultReceiver extends ResultReceiver {
    private Receiver mReceiver;

    public DownloadResultReceiver(Handler handler) {
        super(handler);
    }

    public void setReceiver(Receiver receiver) {
        mReceiver = receiver;
    }

    public interface Receiver {
        public void onReceiveResult(int resultCode, Bundle resultData);
    }

    @Override
    protected void onReceiveResult(int resultCode, Bundle resultData) {
        if (mReceiver != null) {
            mReceiver.onReceiveResult(resultCode, resultData);
        }
    }
}

Step-4: In your MainActivity:

@Override
    public void onReceiveResult(int resultCode, Bundle resultData) {
        switch (resultCode) {
            case DownloadService.STATUS_RUNNING:
                //progress bar visible.
                break;
            case DownloadService.STATUS_FINISHED:
                /* Hide progress & extract result from bundle */
                /* Update ListView with result */
                break;
            case DownloadService.STATUS_ERROR:
                /* Handle the error */
                String error = resultData.getString(Intent.EXTRA_TEXT);
                Toast.makeText(this, error, Toast.LENGTH_LONG).show();
                /*It is here, i think, that you can again check (eg your net connection) and call the IntentService to restart fetching of data from the network. */  
                break;
        }
    }

I hope the above answer helps you. Any suggestions to improve the answer are most welcome. Thanks.

like image 105
Kaveesh Kanwal Avatar answered Sep 27 '22 17:09

Kaveesh Kanwal