📜 ⬆️ ⬇️

Communication between Activity and Service

Somehow I had a task to transfer data from the service to the activation. The search for a solution began in the standard SDK, but since there was no time, I made a bad decision in the form of using the database. But the question was opened and after a while I figured out the more correct way that the SDK has - using the classes Message, Handler, Messenger.

Idea


We need to transfer data from the activation to the service and back. How do we do this? To solve our problem, we already have everything we need. All you need to do is bind the service to ativiti, using bindService, transfer the necessary parameters and a little magic in the form of using Message classes. And the magic is to use Message instance variables and, in particular, replyTo. We need this variable so that we can access the instance of the Messanger service from the activation and in the service to the instance of Messanger. In fact, not so easy. At least for my not very gifted mind. In part, I am improving the documentation that already exists - Services . I improve by adding a link to the activation, transferring data back and forth, which is not in the documentation. Also, there is a good example on StackOverflow. In any case, I hope the article will be useful to at least someone, and I have not bothered so much.

Example


As an example, we will implement a service that will increase and decrease the value of the counter and return the result in activation, in TextView. I will omit the layout code, for there are two buttons and a text field - everything is simple.

Implementation


I will give the full activation code:
')
public class MainActivity extends Activity { public static final String TAG = "TestService"; TestServiceConnection testServConn; TextView testTxt; final Messenger messenger = new Messenger(new IncomingHandler()); Messenger toServiceMessenger; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); testTxt = (TextView)findViewById(R.id.test_txt); bindService(new Intent(this, TestService.class), (testServConn = new TestServiceConnection()), Context.BIND_AUTO_CREATE); } @Override public void onDestroy(){ super.onDestroy(); unbindService(testServConn); } public void countIncrClick(View button){ Message msg = Message.obtain(null, TestService.COUNT_PLUS); msg.replyTo = messenger; try { toServiceMessenger.send(msg); } catch (RemoteException e) { e.printStackTrace(); } } public void countDecrClick(View button){ Message msg = Message.obtain(null, TestService.COUNT_MINUS); msg.replyTo = messenger; try { toServiceMessenger.send(msg); } catch (RemoteException e) { e.printStackTrace(); } } private class IncomingHandler extends Handler { @Override public void handleMessage(Message msg){ switch (msg.what) { case TestService.GET_COUNT: Log.d(TAG, "(activity)...get count"); testTxt.setText(""+msg.arg1); break; } } } private class TestServiceConnection implements ServiceConnection { @Override public void onServiceConnected(ComponentName name, IBinder service) { toServiceMessenger = new Messenger(service); //    Message msg = Message.obtain(null, TestService.SET_COUNT); msg.replyTo = messenger; msg.arg1 = 0; //  try { toServiceMessenger.send(msg); } catch (RemoteException e) { e.printStackTrace(); } } @Override public void onServiceDisconnected(ComponentName name) { } } } 


I will explain. When creating an activity, we immediately bind to the service, implementing the ServiceConnection interface and sending the message “set the counter value” to the service, passing a zero and creating a Service Interface to IB, transferring to the Designer IBinder. By the way, in the service it is necessary to return this instance, otherwise it will be NPE. With this class we send messages to the service. And here it is magic - in the replyTo variable we save our other Messenger instance - the one that receives the answer from the server and it is through him that the connection with the activation will be carried out.

To receive a message from the service, we use our Handler and simply look for the variables we need and take actions on them. By clicking on the buttons (countIncrClick, countDecrClick methods), we send requests to the service, specifying the desired action in the msg.what variable.

Further, the complete service code:

 package com.example.servicetest; import android.app.Service; import android.content.*; import android.os.*; import android.os.Process; import android.util.Log; public class TestService extends Service { public static final int COUNT_PLUS = 1; public static final int COUNT_MINUS = 2; public static final int SET_COUNT = 0; public static final int GET_COUNT = 3; int count = 0; IncomingHandler inHandler; Messenger messanger; Messenger toActivityMessenger; @Override public void onCreate(){ super.onCreate(); HandlerThread thread = new HandlerThread("ServiceStartArguments", Process.THREAD_PRIORITY_BACKGROUND); thread.start(); inHandler = new IncomingHandler(thread.getLooper()); messanger = new Messenger(inHandler); } @Override public IBinder onBind(Intent arg0) { return messanger.getBinder(); } @Override public int onStartCommand(Intent intent, int flags, int startId) { return START_STICKY; } //   private class IncomingHandler extends Handler { public IncomingHandler(Looper looper){ super(looper); } @Override public void handleMessage(Message msg){ //super.handleMessage(msg); toActivityMessenger = msg.replyTo; switch (msg.what) { case SET_COUNT: count = msg.arg1; Log.d(MainActivity.TAG, "(service)...set count"); break; case COUNT_PLUS: count++; Log.d(MainActivity.TAG, "(service)...count plus"); break; case COUNT_MINUS: Log.d(MainActivity.TAG, "(service)...count minus"); count--; break; } //     Message outMsg = Message.obtain(inHandler, GET_COUNT); outMsg.arg1 = count; outMsg.replyTo = messanger; try { if( toActivityMessenger != null ) toActivityMessenger.send(outMsg); } catch (RemoteException e) { e.printStackTrace(); } } } } 


All by analogy with the logic in the activity. I don’t even know if I need to explain something. The only point is that I immediately send the request back to the activation in handleMessage, using the magic variable replyTo and pulling out the desired Messenger above. And the second point I mentioned earlier is:

 @Override public IBinder onBind(Intent arg0) { return messanger.getBinder(); } 


without which everything will fall. It is this instance of the interface that will be passed to the ServiceConnection.

Conclusion


All in all. Such is a far-fetched example of the interaction of activity and service. It seems to me quite a non-trivial interaction, although it may seem different to someone.

The project code is on Bitbucket

Questions, clarifications, etc. in a personal. There may be inaccuracies about any aspects, so feel free to write and correct.
I hope the post was useful to habrayuzer.

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


All Articles