Detailed explanation of service based on android

Keywords: Android xml encoding Java

Basic usage of Service


The most basic usage of Service is how to start a Service. The way to start a Service is very similar to how to start an Activity. We need to use Intent to implement it. Let's look at it through a specific example.

  1. public class MyService extends Service {  
  2.   
  3.     public static final String TAG = "MyService";  
  4.   
  5.     @Override  
  6.     public void onCreate() {  
  7.         super.onCreate();  
  8.         Log.d(TAG, "onCreate() executed");  
  9.     }  
  10.   
  11.     @Override  
  12.     public int onStartCommand(Intent intent, int flags, int startId) {  
  13.         Log.d(TAG, "onStartCommand() executed");  
  14.         return super.onStartCommand(intent, flags, startId);  
  15.     }  
  16.       
  17.     @Override  
  18.     public void onDestroy() {  
  19.         super.onDestroy();  
  20.         Log.d(TAG, "onDestroy() executed");  
  21.     }  
  22.   
  23.     @Override  
  24.     public IBinder onBind(Intent intent) {  
  25.         return null;  
  26.     }  
  27.   
  28. }  

As you can see, we just printed a sentence in onCreate(), onStartCommand() and onDestroy() methods, without any other operation.


Then open or create activity_main.xml as the main layout file of the program. The code is as follows:

  1. <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"  
  2.     android:layout_width="match_parent"  
  3.     android:layout_height="match_parent"  
  4.     android:orientation="vertical" >  
  5.   
  6.     <Button  
  7.         android:id="@+id/start_service"  
  8.         android:layout_width="match_parent"  
  9.         android:layout_height="wrap_content"  
  10.         android:text="Start Service" />  
  11.   
  12.     <Button  
  13.         android:id="@+id/stop_service"  
  14.         android:layout_width="match_parent"  
  15.         android:layout_height="wrap_content"  
  16.         android:text="Stop Service" />  
  17.   
  18. </LinearLayout>  

We added two buttons to the layout file, one for starting the Service and one for stopping the Service.


Then open or create MainActivity as the main Activity of the program, and add the logic to start and stop the Service. The code is as follows:

  1. public class MainActivity extends Activity implements OnClickListener {  
  2.   
  3.     private Button startService;  
  4.   
  5.     private Button stopService;  
  6.   
  7.     @Override  
  8.     protected void onCreate(Bundle savedInstanceState) {  
  9.         super.onCreate(savedInstanceState);  
  10.         setContentView(R.layout.activity_main);  
  11.         startService = (Button) findViewById(R.id.start_service);  
  12.         stopService = (Button) findViewById(R.id.stop_service);  
  13.         startService.setOnClickListener(this);  
  14.         stopService.setOnClickListener(this);  
  15.     }  
  16.   
  17.     @Override  
  18.     public void onClick(View v) {  
  19.         switch (v.getId()) {  
  20.         case R.id.start_service:  
  21.             Intent startIntent = new Intent(this, MyService.class);  
  22.             startService(startIntent);  
  23.             break;  
  24.         case R.id.stop_service:  
  25.             Intent stopIntent = new Intent(this, MyService.class);  
  26.             stopService(stopIntent);  
  27.             break;  
  28.         default:  
  29.             break;  
  30.         }  
  31.     }  
  32.   
  33. }  
As you can see, in the click event of the Start Service button, we constructed an Intent object and invoked the startService() method to start MyService. Then in the click event of the Stop Serivce button, we also construct an Intent object and call the stopService() method to stop MyService. The logic of the code is very simple. I don't think I need to explain it any more.


Also note that each Service in the project must be registered in AndroidManifest.xml, so you also need to edit the AndroidManifest.xml file. The code is as follows:

  1. <?xml version="1.0" encoding="utf-8"?>  
  2. <manifest xmlns:android="http://schemas.android.com/apk/res/android"  
  3.     package="com.example.servicetest"  
  4.     android:versionCode="1"  
  5.     android:versionName="1.0" >  
  6.   
  7.     <uses-sdk  
  8.         android:minSdkVersion="14"  
  9.         android:targetSdkVersion="17" />  
  10.   
  11.     <application  
  12.         android:allowBackup="true"  
  13.         android:icon="@drawable/ic_launcher"  
  14.         android:label="@string/app_name"  
  15.         android:theme="@style/AppTheme" >  
  16.           
  17.     ……  
  18.   
  19.         <service android:name="com.example.servicetest.MyService" >  
  20.         </service>  
  21.     </application>  
  22.   
  23. </manifest>  

In this way, a simple program with Service function is written. Now let's run the program and click the Start Service button. You can see the print log of LogCat as follows:



That is, when a Service is started, onCreate() and onStartCommand() methods in the Service are called.


What if I click the Start Service button again? The print logs at this time are as follows:




As you can see, this time only the onStartCommand() method is executed, and the onCreate() method is not executed. Why? This is because the onCreate() method will only be invoked when the Service is first created. If the current Service has been created, the onCreate() method will not be executed anyway by calling the startService() method. So you can click the Start Service button a few more times to try it, each time only printing logs in the onStartCommand() method.




Service and Active Communication


We have learned the basic usage of Service above. After starting Service, we can execute some specific logic in onCreate() or onStartCommand() methods. But in that case, the relationship between Service and Activeness is not very big. Activity just notifies Service, "You can start it." Then Service went to work on his own business. So is there any way to make them more connected? For example, in Activity, you can specify what tasks a Service is supposed to perform. Sure, just have Activity and Service relate.


Looking at the code in MyService, you will find that there has always been an onBind() method that we haven't used. This method is actually used to establish association with Activeness. Modify the code in MyService, as follows:

  1. public class MyService extends Service {  
  2.   
  3.     public static final String TAG = "MyService";  
  4.   
  5.     private MyBinder mBinder = new MyBinder();  
  6.   
  7.     @Override  
  8.     public void onCreate() {  
  9.         super.onCreate();  
  10.         Log.d(TAG, "onCreate() executed");  
  11.     }  
  12.   
  13.     @Override  
  14.     public int onStartCommand(Intent intent, int flags, int startId) {  
  15.         Log.d(TAG, "onStartCommand() executed");  
  16.         return super.onStartCommand(intent, flags, startId);  
  17.     }  
  18.   
  19.     @Override  
  20.     public void onDestroy() {  
  21.         super.onDestroy();  
  22.         Log.d(TAG, "onDestroy() executed");  
  23.     }  
  24.   
  25.     @Override  
  26.     public IBinder onBind(Intent intent) {  
  27.         return mBinder;  
  28.     }  
  29.   
  30.     class MyBinder extends Binder {  
  31.   
  32.         public void startDownload() {  
  33.             Log.d("TAG""startDownload() executed");  
  34.             //Implementing specific download tasks  
  35.         }  
  36.   
  37.     }  
  38.   
  39. }  

Here we add a new MyBinder class inherited from the Binder class, and then add a startDownload() method to MyBinder to perform download tasks in the background. Of course, this is not really to download something, just to do it. test So the startDownload() method just prints a line of logs.


Then modify the code in activity_main.xml to add buttons for binding Service and unbound Service in the layout file:

  1. <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"  
  2.     android:layout_width="match_parent"  
  3.     android:layout_height="match_parent"  
  4.     android:orientation="vertical" >  
  5.   
  6.     <Button  
  7.         android:id="@+id/start_service"  
  8.         android:layout_width="match_parent"  
  9.         android:layout_height="wrap_content"  
  10.         android:text="Start Service" />  
  11.   
  12.     <Button  
  13.         android:id="@+id/stop_service"  
  14.         android:layout_width="match_parent"  
  15.         android:layout_height="wrap_content"  
  16.         android:text="Stop Service" />  
  17.   
  18.     <Button  
  19.         android:id="@+id/bind_service"  
  20.         android:layout_width="match_parent"  
  21.         android:layout_height="wrap_content"  
  22.         android:text="Bind Service" />  
  23.       
  24.     <Button   
  25.         android:id="@+id/unbind_service"  
  26.         android:layout_width="match_parent"  
  27.         android:layout_height="wrap_content"  
  28.         android:text="Unbind Service"  
  29.         />  
  30.       
  31. </LinearLayout>  
Next, we modify the code in MainActivity to establish an association between MainActivity and MyService. The code is as follows:
  1. public class MainActivity extends Activity implements OnClickListener {  
  2.   
  3.     private Button startService;  
  4.   
  5.     private Button stopService;  
  6.   
  7.     private Button bindService;  
  8.   
  9.     private Button unbindService;  
  10.   
  11.     private MyService.MyBinder myBinder;  
  12.   
  13.     private ServiceConnection connection = new ServiceConnection() {  
  14.   
  15.         @Override  
  16.         public void onServiceDisconnected(ComponentName name) {  
  17.         }  
  18.   
  19.         @Override  
  20.         public void onServiceConnected(ComponentName name, IBinder service) {  
  21.             myBinder = (MyService.MyBinder) service;  
  22.             myBinder.startDownload();  
  23.         }  
  24.     };  
  25.   
  26.     @Override  
  27.     protected void onCreate(Bundle savedInstanceState) {  
  28.         super.onCreate(savedInstanceState);  
  29.         setContentView(R.layout.activity_main);  
  30.         startService = (Button) findViewById(R.id.start_service);  
  31.         stopService = (Button) findViewById(R.id.stop_service);  
  32.         bindService = (Button) findViewById(R.id.bind_service);  
  33.         unbindService = (Button) findViewById(R.id.unbind_service);  
  34.         startService.setOnClickListener(this);  
  35.         stopService.setOnClickListener(this);  
  36.         bindService.setOnClickListener(this);  
  37.         unbindService.setOnClickListener(this);  
  38.     }  
  39.   
  40.     @Override  
  41.     public void onClick(View v) {  
  42.         switch (v.getId()) {  
  43.         case R.id.start_service:  
  44.             Intent startIntent = new Intent(this, MyService.class);  
  45.             startService(startIntent);  
  46.             break;  
  47.         case R.id.stop_service:  
  48.             Intent stopIntent = new Intent(this, MyService.class);  
  49.             stopService(stopIntent);  
  50.             break;  
  51.         case R.id.bind_service:  
  52.             Intent bindIntent = new Intent(this, MyService.class);  
  53.             bindService(bindIntent, connection, BIND_AUTO_CREATE);  
  54.             break;  
  55.         case R.id.unbind_service:  
  56.             unbindService(connection);  
  57.             break;  
  58.         default:  
  59.             break;  
  60.         }  
  61.     }  
  62.   
  63. }  

As you can see, here we first create an anonymous class of Service Connection, in which we override the onService Connected () method and the onService Disconnected () method, which are called when Activity establishes and de-associates with Service, respectively. In the onService Connected () method, we get an example of MyBinder by downward transformation. With this example, the relationship between Activity and Service becomes very close. Now we can call any public method in MyBinder according to specific scenarios in Activity, which implements the function that Activity commands Service to do what Service does.


Of course, now Activity and Service are not actually related, this function is completed in the click event of the Bind Service button. As you can see, here we still build an Intent object and then call the bindService() method to bind Activity and Service. The bindService() method receives three parameters. The first parameter is the Intent object that has just been constructed. The second parameter is the instance of Service Connection created earlier. The third parameter is a flag bit. Here, BIND_AUTO_CREATE is passed in to indicate that the service is automatically created after the association between Activity and Service is established. This will enable the onCreate() method in MyService to be executed, but o The nStartCommand () method does not execute.


Then how do we want to decouple the relationship between Activity and Service? Just call the unbindService() method, which is also the logic implemented in the click event of the Unbind Service button.


Now let's run the program again. Click on the Bind Service button in MainActivity. The print log in LogCat is shown as follows:




It is also important to note that any Service is generic throughout the application, that is, MyService can not only associate with MainActivity, but also with any Activity, and they can get the same MyBinder instance when establishing association.


How to Destroy Service


In the basic usage section of Service, we introduce the simplest way to destroy Service. Click the Start Service button to start the Service and then click the Stop Service button to stop the Service. MyService is destroyed. You can see the print log as follows:



So what if we clicked the Bind Service button? Since the specified flag is BIND_AUTO_CREATE when binding a Service, it indicates that the Service will be created when the Bind Service button is clicked. How should the Service be destroyed? In fact, it's also very simple. Click on the Unbind Service button to remove the association between Activity and Service.


First click the Bind Service button, then click the Unbind Service button. Print the log as follows:



These two ways of destruction are well understood. So what if we clicked both the Start Service button and the Bind Service button? At this point, you will find that no matter you click the Stop Service button or the Unbind Service button alone, the Service will not be destroyed. It is necessary to click both buttons before the Service will be destroyed. That is to say, clicking on the Stop Service button will only stop the service, clicking on the Unbind Service button will only make the service and Active de-associate. A service must be destroyed when it is not associated with any Activity and handles the stop state.


To confirm, we add a print log to the click events of the Stop Service and Unbind Service buttons:

  1. public void onClick(View v) {  
  2.     switch (v.getId()) {  
  3.     case R.id.start_service:  
  4.         Intent startIntent = new Intent(this, MyService.class);  
  5.         startService(startIntent);  
  6.         break;  
  7.     case R.id.stop_service:  
  8.         Log.d("MyService""click Stop Service button");  
  9.         Intent stopIntent = new Intent(this, MyService.class);  
  10.         stopService(stopIntent);  
  11.         break;  
  12.     case R.id.bind_service:  
  13.         Intent bindIntent = new Intent(this, MyService.class);  
  14.         bindService(bindIntent, connection, BIND_AUTO_CREATE);  
  15.         break;  
  16.     case R.id.unbind_service:  
  17.         Log.d("MyService""click Unbind Service button");  
  18.         unbindService(connection);  
  19.         break;  
  20.     default:  
  21.         break;  
  22.     }  
  23. }  

Then run the program again, first click on the Start Service button, then click on the Bind Service button, so that the Service can be started and connected with Active. Then click the Stop Service button and the Service will not be destroyed. Then click the Unbind Service button and the Service will be destroyed. Print the log as follows:



We should always remember to clean up the resources that are no longer used in the on Destroy () method of Service to prevent some objects that are no longer used from still occupying memory after Service is destroyed.


The relationship between Service and Thread


Many Android beginners may wonder what the relationship between Service and Thread is. When should I use Service and when should I use Thread? The answer may surprise you a little, because there is no relationship between Service and Thread!


The reason why many people associate them is mainly because of the background concept of Service. Thread, as we all know, is used to open a sub-thread, where to perform some time-consuming operations will not block the operation of the main thread. When we first understand Service, we always feel that it is used to handle some background tasks, and some time-consuming operations can also be run here, which will cause confusion. But if I tell you that Service actually runs in the main thread, do you think it has anything to do with Thread? Let's take a look at this cruel fact.


Add a line to the onCreate() method of MainActivity that prints the current thread id:

  1. Log.d("MyService""MainActivity thread id is " + Thread.currentThread().getId());  
Then add a line to MyService's onCreate() method that prints the current thread id:
  1. Log.d("MyService""MyService thread id is " + Thread.currentThread().getId());  

Now run the program again and click the Start Service button. You will see the following print logs:



As you can see, their thread IDs are exactly the same, which confirms that Service does run in the main thread, that is to say, if you write very time-consuming code in Service, the program will appear ANR.


You may exclaim, isn't this a pit dad!? What's the use of a service? In fact, we should not link the background and sub-threads together. These are two completely different concepts. The background of Android is that it runs completely independent of the UI. Even if the Activity is destroyed or the program is shut down, the service can continue to run as long as the process is still there. For example, some applications, which always need to maintain a heartbeat connection with the server, can be implemented using Service. You might ask, haven't you just verified that Service runs in the main thread? The heartbeat connection is always executed here, won't it block the running of the main thread? Sure, but we can create a sub-thread in Service and then deal with time-consuming logic here.


Well, since a sub-thread is also created in Service, why not create it directly in Activity? This is because it is difficult for Activity to control Thread. When Activity is destroyed, there is no other way to retrieve instances of previously created sub-threads. And a subthread created in one activity cannot be manipulated by another activity. But Service is different. All Activities can be associated with Service, and then they can easily manipulate the method. Even if Activity is destroyed, as long as it is re-associated with Service, it can get the instance of Binder in the original Service. Therefore, using Service to process background tasks, Activity can safely finish without worrying about the situation that background tasks can not be controlled.


A standard Service can be written as:

  1. @Override  
  2. public int onStartCommand(Intent intent, int flags, int startId) {  
  3.     new Thread(new Runnable() {  
  4.         @Override  
  5.         public void run() {  
  6.             //Start backstage tasks  
  7.         }  
  8.     }).start();  
  9.     return super.onStartCommand(intent, flags, startId);  
  10. }  
  11.   
  12. class MyBinder extends Binder {  
  13.   
  14.     public void startDownload() {  
  15.         new Thread(new Runnable() {  
  16.             @Override  
  17.             public void run() {  
  18.                 //Implementing specific download tasks  
  19.             }  
  20.         }).start();  
  21.     }  
  22.   
  23. }  

Create Front Office Service


Service is almost always running in the background, it has been quietly doing hard work. However, the system priority of Service is still relatively low. When the system has insufficient memory, it is possible to reclaim the service running in the background. If you want the service to remain running without being reclaimed due to insufficient system memory, consider using the front-end service. The biggest difference between the front desk service and the ordinary service is that it will always have a running icon in the status bar of the system to display, drop down the status bar to see more detailed information, very similar to the effect of notification. Of course, sometimes you may not only use the front desk service to prevent the service from being recycled, but also use the front desk service because of special requirements, such as ink weather. Its service updates the weather data in the background, and displays the current weather information in the system status bar all the time, as shown in the following figure:



So let's see how to create a front-end Service. It's not complicated. Modify the code in MyService, as follows:

  1. public class MyService extends Service {  
  2.   
  3.     public static final String TAG = "MyService";  
  4.   
  5.     private MyBinder mBinder = new MyBinder();  
  6.   
  7.     @Override  
  8.     public void onCreate() {  
  9.         super.onCreate();  
  10.         Notification notification = new Notification(R.drawable.ic_launcher,  
  11.                 "Notice arrives", System.currentTimeMillis());  
  12.         Intent notificationIntent = new Intent(this, MainActivity.class);  
  13.         PendingIntent pendingIntent = PendingIntent.getActivity(this0,  
  14.                 notificationIntent, 0);  
  15.         notification.setLatestEventInfo(this"This is the title of the notice.""This is the content of the notice.",  
  16.                 pendingIntent);  
  17.         startForeground(1, notification);  
  18.         Log.d(TAG, "onCreate() executed");  
  19.     }  
  20.   
  21.     .........  
  22.   
  23. }  

This is just a modification of the code for the onCreate() method in MyService. As you can see, we first create a Notification object, then call its setLatestEventInfo() method to initialize the layout and data for the notification, and set the click notification here to open MainActivity. Then call the startForeground() method to make MyService a front-end Service and display the picture of the notification.


Now run the program again, and click the Start Service or Bind Service button, MyService will start up in front of the service mode, and a bar icon will pop up in the system status bar. After dropping down the status bar, you can see the details of the notification, as shown in the figure below.


Service actually runs in the main thread. If you process some time-consuming logic directly in Service, it will lead to ANR.


Let's do an experiment to verify that by modifying the ServiceTest project created in the previous article, we can sleep threads for 60 seconds in MyService's onCreate() method, as follows:

  1. public class MyService extends Service {  
  2.   
  3.     ......  
  4.   
  5.     @Override  
  6.     public void onCreate() {  
  7.         super.onCreate();  
  8.         Log.d(TAG, "onCreate() executed");  
  9.         try {  
  10.             Thread.sleep(60000);  
  11.         } catch (InterruptedException e) {  
  12.             e.printStackTrace();  
  13.         }  
  14.     }  
  15.       
  16.     ......  
  17.   
  18. }  

After re-running, click on the Start Service button or the Bind Service button, the program will block and can not do any other operation, after a period of time, the ANR prompt box will pop up, as shown in the following figure.



As we mentioned earlier, threads should be opened in Service to perform time-consuming tasks, so that ANR can be effectively avoided.


So the theme of this article is to introduce the use of remote Service. If MyService is converted into a remote Service, will there be ANR? Let's give it a try.


Converting an ordinary Service to a remote service is very simple. You just need to specify its android:process attribute as: remote when registering a service. The code is as follows:

  1. <?xml version="1.0" encoding="utf-8"?>  
  2. <manifest xmlns:android="http://schemas.android.com/apk/res/android"  
  3.     package="com.example.servicetest"  
  4.     android:versionCode="1"  
  5.     android:versionName="1.0" >  
  6.   
  7.     ......  
  8.       
  9.     <service  
  10.         android:name="com.example.servicetest.MyService"  
  11.         android:process=":remote" >  
  12.     </service>  
  13.   
  14. </manifest>  

Now run the program again and click on the Start Service button, and you will see that the console immediately prints the onCreate() executed information, and the main interface is not blocked, and there will be no ANR. After about a minute, you'll see onStartCommand() executed printed again.


Why does converting MyService into remote Service not result in ANR? This is because after using remote Service, MyService has been running in another process, so it will only block the main thread in the process, and will not affect the current application.


To confirm that MyService is now running in another process, we add a line of logs to onCreate() method of MainActivity and onCreate() method of MyService, and print out the process id of MyService, as follows:


  1. Log.d("TAG""process id is " + Process.myPid());  

Rerun the program again and click the Start Service button. The print results are as follows:



As you can see, not only is the process id different, but even the application package name is different. The log printed in MyService is followed by the package name: remote identifier.


Now that remote services are so useful, let's convert all services into remote services in the future and save the need to open threads again. In fact, not only is remote Service not easy to use, but it can even be said to be more difficult to use. In general, if you can not use remote Service, try not to use it.


Let's take a look at its drawbacks. First, remove the code that lets the thread sleep in MyService's onCreate() method, then run the program again, and click the Bind Service button, you will find that the program crashed! Why doesn't the Start Service button crash and the Bind Service button crash? This is because in the click event of the Bind Service button we will associate MainActivity with MyService, but MyService is now a remote service, and Activity and Service run in two different processes. At this time, the traditional way of establishing association can no longer be used, and the program will collapse.


So how can Activity be associated with a remote Service? This requires the use of AIDL for cross-process communication (IPC).


AIDL (Android Interface Definition Language) is the meaning of Android Interface Definition Language. It can be used to enable a Service to communicate with multiple application components across processes, so that multiple applications can share the same Service.


Let's take a step-by-step look at how AIDL is used. First, we need to create a new AIDL file, in which we define the method that Activity needs to communicate with Service. New MyAIDLService.aidl file, code as follows:

  1. package com.example.servicetest;  
  2. interface MyAIDLService {  
  3.     int plus(int a, int b);  
  4.     String toUpperCase(String str);  
  5. }  

After clicking Save, a corresponding one will be generated in the gen directory. Java The file, as shown in the following figure:


 


Then we modify the code in MyService to implement the MyAIDLService interface we just defined, as follows:

  1. public class MyService extends Service {  
  2.   
  3.     ......  
  4.   
  5.     @Override  
  6.     public IBinder onBind(Intent intent) {  
  7.         return mBinder;  
  8.     }  
  9.   
  10.     MyAIDLService.Stub mBinder = new Stub() {  
  11.   
  12.         @Override  
  13.         public String toUpperCase(String str) throws RemoteException {  
  14.             if (str != null) {  
  15.                 return str.toUpperCase();  
  16.             }  
  17.             return null;  
  18.         }  
  19.   
  20.         @Override  
  21.         public int plus(int a, int b) throws RemoteException {  
  22.             return a + b;  
  23.         }  
  24.     };  
  25.   
  26. }  

First, MyAIDLService.Stub is implemented. toUpperCase() and plus() are rewritten. The purpose of these two methods is to convert a string into uppercase format and add two incoming integers. Then the implementation of MyAIDLService.Stub is returned in the onBind() method. Why can I write this here? Because Stub is actually a subclass of Binder, you can directly return the implementation of Stub in the onBind() method.


Next, modify the code in MainActivity as follows:

  1. public class MainActivity extends Activity implements OnClickListener {  
  2.   
  3.     private Button startService;  
  4.   
  5.     private Button stopService;  
  6.   
  7.     private Button bindService;  
  8.   
  9.     private Button unbindService;  
  10.       
  11.     private MyAIDLService myAIDLService;  
  12.   
  13.     private ServiceConnection connection = new ServiceConnection() {  
  14.   
  15.         @Override  
  16.         public void onServiceDisconnected(ComponentName name) {  
  17.         }  
  18.   
  19.         @Override  
  20.         public void onServiceConnected(ComponentName name, IBinder service) {  
  21.             myAIDLService = MyAIDLService.Stub.asInterface(service);  
  22.             try {  
  23.                 int result = myAIDLService.plus(35);  
  24.                 String upperStr = myAIDLService.toUpperCase("hello world");  
  25.                 Log.d("TAG""result is " + result);  
  26.                 Log.d("TAG""upperStr is " + upperStr);  
  27.             } catch (RemoteException e) {  
  28.                 e.printStackTrace();  
  29.             }  
  30.         }  
  31.     };  
  32.   
  33.     ......  
  34.   
  35. }  

We just changed the code in Service Connection. As you can see, you first use the MyAIDLService.Stub.asInterface() method to transfer the incoming IBinder object to the MyAIDLService object, and then you can call all the interfaces defined in the MyAIDLService.aidl file. Here we first call the plus() method and pass in 3 and 5 as parameters. Then we call the toUpperCase() method and pass in the hello world string as parameters. Finally, we print out the return result of the calling method.


Now run the program again and click the Bind Service button. You can see the print log as follows:




Thus, we have succeeded in cross-process communication, accessing methods from one process to another.


However, you can also see that the current cross-process communication actually has no real effect, because it is only in an Activity that calls the method in the service of the same application. The real meaning of cross-process communication is to enable one application to access Services in another application in order to realize the function of shared Service. So let's naturally learn how to call methods in MyService in other applications.


As we already know in the previous article, if you want to associate Activity with Service, you need to call the bindService() method and pass in Intent as a parameter. In Intent, you specify the service to be bound. The sample code is as follows:

  1. Intent bindIntent = new Intent(this, MyService.class);  
  2. bindService(bindIntent, connection, BIND_AUTO_CREATE);  
MyService.class is used to specify which service to bind when building Intent, but MyService is not used when Binding Service in another application, so implicit Intent must be used. Now modify the code in AndroidManifest.xml and add an action to MyService as follows:
  1. <?xml version="1.0" encoding="utf-8"?>  
  2. <manifest xmlns:android="http://schemas.android.com/apk/res/android"  
  3.     package="com.example.servicetest"  
  4.     android:versionCode="1"  
  5.     android:versionName="1.0" >  
  6.   
  7.     ......  
  8.   
  9.     <service  
  10.         android:name="com.example.servicetest.MyService"  
  11.         android:process=":remote" >  
  12.         <intent-filter>  
  13.             <action android:name="com.example.servicetest.MyAIDLService"/>  
  14.         </intent-filter>  
  15.     </service>  
  16.   
  17. </manifest>  

This means that MyService can respond to Intent with com. example. service test. MyAIDLService.


Now run the program again, so that all the work on the remote Service side is finished.


Then we create a new Android project called ClientTest, where we try to call methods in MyService remotely.


It's not difficult for Activity in ClientTest to establish an association with MyService. First, you need to copy the MyAIDLService.aidl file from the ServiceTest project. Note that the original package path should be copied together. After completion, the structure of the project is as follows:




Then open or create activity_main.xml and add a Bind Service button to the layout file:

  1. <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"  
  2.     android:layout_width="match_parent"  
  3.     android:layout_height="match_parent"  
  4.     android:orientation="vertical"  
  5.      >  
  6.   
  7.    <Button   
  8.        android:id="@+id/bind_service"  
  9.        android:layout_width="match_parent"  
  10.        android:layout_height="wrap_content"  
  11.        android:text="Bind Service"  
  12.        />  
  13.   
  14. </LinearLayout>  
Next, open or create a new MainActivity, in which you add the code associated with MyService, as follows:
  1. public class MainActivity extends Activity {  
  2.   
  3.     private MyAIDLService myAIDLService;  
  4.   
  5.     private ServiceConnection connection = new ServiceConnection() {  
  6.   
  7.         @Override  
  8.         public void onServiceDisconnected(ComponentName name) {  
  9.         }  
  10.   
  11.         @Override  
  12.         public void onServiceConnected(ComponentName name, IBinder service) {  
  13.             myAIDLService = MyAIDLService.Stub.asInterface(service);  
  14.             try {  
  15.                 int result = myAIDLService.plus(5050);  
  16.                 String upperStr = myAIDLService.toUpperCase("comes from ClientTest");  
  17.                 Log.d("TAG""result is " + result);  
  18.                 Log.d("TAG""upperStr is " + upperStr);  
  19.             } catch (RemoteException e) {  
  20.                 e.printStackTrace();  
  21.             }  
  22.         }  
  23.     };  
  24.   
  25.     @Override  
  26.     protected void onCreate(Bundle savedInstanceState) {  
  27.         super.onCreate(savedInstanceState);  
  28.         setContentView(R.layout.activity_main);  
  29.         Button bindService = (Button) findViewById(R.id.bind_service);  
  30.         bindService.setOnClickListener(new OnClickListener() {  
  31.             @Override  
  32.             public void onClick(View v) {  
  33.                 Intent intent = new Intent("com.example.servicetest.MyAIDLService");  
  34.                 bindService(intent, connection, BIND_AUTO_CREATE);  
  35.             }  
  36.         });  
  37.     }  
  38.   
  39. }  

This part of the code will be very familiar to you, right? Yes, it's almost identical to the code in Service Test's MainActivity, except that we use implicit Intent to associate Activity with Service, specifying Intent's action as com. example. service test. MyAIDLService.


After the current Activity is associated with MyService, we still call the plus() and toUpperCase() methods. The remote MyService processes the incoming parameters and returns the results, and then prints them out.


In this way, the code in ClientTest is complete. Now run the project and click on the Bind Service button. Then you will associate with the remote MyService. Observe the print information in LogCat as follows:



Needless to say, we have all seen that our cross-process communication function has been perfectly implemented.


However, it should also be noted that Android has very limited support for the format of such data because it transfers data between different processes, and basically only transfers Java's basic data types, strings, lists or Map s. So what if I want to pass a custom class? This requires the class to implement the Parcelable interface and to define an AIDL file of the same name for the class. This part of the content is not complicated, and has little to do with Service, so no more detailed explanation, interested friends can go to check the relevant information.

Posted by animedls on Wed, 03 Apr 2019 20:00:34 -0700