📜 ⬆️ ⬇️

Communication between threads through ResultReceiver

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

Instead of intro


In most Android projects, it is necessary to organize communication with the outside world, i.e. organize networking I will not repeat why it is bad to execute such a long-running code in the UI stream. Everyone knows that. Moreover, starting from API 11 (Honeycomb which), the system beats the developer except when he tries to make network calls in the UI stream.
One of the ways to communicate a UI stream with a parallel stream (in which, for example, an http request is executed) is an approach based on using the built-in system class android.os.ResultReceiver together with the service.

A little bit about the architecture of the approach


For organizing a separate thread, I chose the IntentService. Why him, and not simple Service? Because the IntentService when a command arrives at it automatically starts the onHandleIntent(Intent intent) method in a separate thread from the UI. Simple Service does not allow this because it runs in the main thread. It is necessary to organize the launch of the stream from the Service.
Communication between the Activity and the IntentService will occur through the use of Intents.
')

Code


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

Implementing ResultReceiver

 public class AppResultsReceiver extends ResultReceiver { public interface Receiver { public void onReceiveResult(int resultCode, Bundle data); } private Receiver mReceiver; public AppResultsReceiver(Handler handler) { super(handler); } public void setReceiver(Receiver receiver) { mReceiver = receiver; } @Override protected void onReceiveResult(int resultCode, Bundle resultData) { if (mReceiver != null) { mReceiver.onReceiveResult(resultCode, resultData); } } } 

Here you should pay attention to the callback ( Receiver ). When the result is onReceiveResult() in 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

 public class AppService extends IntentService { public AppService() { this("AppService"); } public AppService(String name) { super(name); } @Override protected void onHandleIntent(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 retrieved from the intent and the OK command will be sent to it immediately, I went to work. After doing useful work in this method, the results (extracted from JSON classes-models, strings, anything) are placed in a 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 source code.

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

 public class MainActivity extends Activity implements AppResultsReceiver.Receiver { private AppResultsReceiver mReceiver; private ProgressBar mProgress; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); mProgress = (ProgressBar) findViewById(R.id.progressBar); } @Override protected void onResume() { super.onResume(); mReceiver = new AppResultsReceiver(new Handler()); mReceiver.setReceiver(this); } @Override protected void onPause() { super.onPause(); mReceiver.setReceiver(null); } public void onStartButtonClick(View anchor) { final Intent intent = new Intent("SOME_COMMAND_ACTION", null, this, AppService.class); intent.putExtra(Constants.RECEIVER, mReceiver); startService(intent); } @Override public void onReceiveResult(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 the AppResultsReceiver.Receiver interface. When starting, it creates an instance of the receiver; when it is paused, it is decoupled from listening to responses from the service. When a button is clicked, a command is formed (intent), a link to our ResultReceiver is placed in it and the service is started.
When a response from the service is onReceiveResult() method checks the response code and takes the appropriate action. That's all.

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


Source code of the demo project is available on GitHub

Instead of conclusion



The processing of the command in the background service is carried out to disgrace is simple: the flow is simply paused for a while. Of course, in real-world applications, it is necessary in order to transfer the command code (action) to be executed, additional parameters, and so on. OOP you in hand. It is also worth remembering that the data (for example, models) that, when bundled into 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, tools and combinations. Whether it is AsyncTask, Service + Thread + BroadcastReceiver, or the “manual” transfer of Message through Handler to the UI stream. Choose, as they say, you. But that's another story.

Source: https://habr.com/ru/post/167679/


All Articles