Communication between streams through ResultReceiver

    As every Android Android Android SDK developer knows, it provides several ways to force a particular piece of code to execute in a parallel thread. Multithreading is good, but in addition to organizing it, you also need to establish a communication channel between threads. For example, between a UI thread and a thread that runs background tasks. In this short essay I want to highlight one of the methods based on the use of the built-in ResultReceiver class.

    Instead of joining


    In most Android projects, you have to organize communication with the outside world, i.e. organize networking. I will not repeat why it is bad to execute such a long-playing code in a UI stream. This is common knowledge. Moreover, starting with API 11 (Honeycomb which), the system beats the developer hands except when he tries to make network calls in the UI stream.
    One of the options for communicating a UI stream with a parallel stream (in which, for example, an http request is performed) is an approach based on the use of the built-in system class in android.os.ResultReceiverconjunction with the service.

    A bit about approach architecture


    To organize a separate thread, I chose IntentService. Why is it he and not a simple Service? Because IntentService, when a command arrives at it, automatically starts to execute the method onHandleIntent(Intent intent)in a separate thread from the UI. Simple Service does not allow this because it is executed in the main thread. It is necessary to organize the start of a stream from Service'a independently.
    Communication between Activity and IntentService will be done using Intents.

    Code


    First, the source code, then below a brief commentary on what is happening there and how.

    ResultReceiver implementation

    publicclassAppResultsReceiverextendsResultReceiver{
    	publicinterfaceReceiver{
    		publicvoidonReceiveResult(int resultCode, Bundle data);
    	}
    	private Receiver mReceiver;
    	publicAppResultsReceiver(Handler handler){
    		super(handler);
    	}
    	publicvoidsetReceiver(Receiver receiver){
    		mReceiver = receiver;
    	}
    	@OverrideprotectedvoidonReceiveResult(int resultCode, Bundle resultData){
    		if (mReceiver != null) {
    			mReceiver.onReceiveResult(resultCode, resultData);
    		}
    	}
    }
    

    Here you should pay attention to callback ( Receiver). Upon receipt of the result, onReceiveResult()a check is made for a non-null callback. Further in the activation code it will be shown how to activate and deactivate the receiver using this callback.

    IntentService

    publicclassAppServiceextendsIntentService{
    	publicAppService(){
    		this("AppService");
    	}
    	publicAppService(String name){
    		super(name);
    	}
    	@OverrideprotectedvoidonHandleIntent(Intent intent){
    		final ResultReceiver receiver = intent.getParcelableExtra(Constants.RECEIVER);
    		receiver.send(Constants.STATUS_RUNNING, Bundle.EMPTY);
    		final Bundle data = new Bundle();
    		try {
    			Thread.sleep(Constants.SERVICE_DELAY);
    			data.putString(Constants.RECEIVER_DATA, "Sample result data");
    		} catch (InterruptedException e) {
    			data.putString(Constants.RECEIVER_DATA, "Error");
    		}
    		receiver.send(Constants.STATUS_FINISHED, data);
    	}
    }
    

    onHandleIntent()will be called after the calling code (UI classes etc.) executes startService(). The ResultReceiver instance will be extracted from the intent and the command “OK, I went to work” will be sent to him immediately. After doing useful work in this method, the results (model classes, strings, anything extracted from JSON) are placed in the bundle and sent to the receiver. Moreover, different codes are used to indicate the type of response (described by constants). How ResultReceiver receives and sends data can be read in its sources.

    Sending a command to a service and processing the result (Activity)

    publicclassMainActivityextendsActivityimplementsAppResultsReceiver.Receiver{
    	private AppResultsReceiver mReceiver;
    	private ProgressBar mProgress;
    	@OverrideprotectedvoidonCreate(Bundle savedInstanceState){
    		super.onCreate(savedInstanceState);
    		setContentView(R.layout.activity_main);
    		mProgress = (ProgressBar) findViewById(R.id.progressBar); 
    	}
    	@OverrideprotectedvoidonResume(){
    		super.onResume();
    		mReceiver = new AppResultsReceiver(new Handler());
    		mReceiver.setReceiver(this);
    	}
    	@OverrideprotectedvoidonPause(){
    		super.onPause();
    		mReceiver.setReceiver(null);
    	}
    	publicvoidonStartButtonClick(View anchor){
    		final Intent intent = new Intent("SOME_COMMAND_ACTION", null, this, AppService.class);
    		intent.putExtra(Constants.RECEIVER, mReceiver);
    		startService(intent);
    	}
    	@OverridepublicvoidonReceiveResult(int resultCode, Bundle data){
    		switch (resultCode) {
    			case Constants.STATUS_RUNNING :
    				mProgress.setVisibility(View.VISIBLE);
    				break;
    			case Constants.STATUS_FINISHED :
    				mProgress.setVisibility(View.INVISIBLE);
    				Toast.makeText(this, "Service finished with data: " 
    						+ data.getString(Constants.RECEIVER_DATA), Toast.LENGTH_SHORT).show();
    				break;
    		}
    	}
    }
    

    Everything is simple here. Activity implements an interface AppResultsReceiver.Receiver. When starting, it creates an instance of the receiver, when paused, it detaches from listening to responses from the service. When you click on the button, a team (intent) is formed, a link to ours is placed in it, ResultReceiverand the service starts.
    When receiving a response from a service, the method onReceiveResult()checks the response code and takes the appropriate action. That's all.

    The demo application looks simple, it has only one button "Send request".


    The source code of the demo project is available on GitHub

    Instead of a conclusion



    Processing a command in the background service is implemented to a disgrace simply: the stream is simply paused for a while. Of course, in real applications, you need to transfer the code of the command (action) that needs to be executed, additional parameters, etc. OOP in your hands. It is also worth remembering that data (for example, models) that are packaged in a bundle must be Parcelable objects. This will increase the efficiency of their serialization.
    Of course, the described approach is not the ultimate truth. We are free to choose different architectural approaches, means and combinations. Whether it be AsyncTask, Service + Thread + BroadcastReceiver, or the “manual” transmission of Message through the Handler to the UI stream. Choose, as they say, you. But this is a completely different story.

    Also popular now: