Recently, a project happened to use Service, especially AIDL remote service. After this project, I have a better understanding of Service. I will give a summary here.
startService / bindService mixed use
- Every time you call startService, onStartCommand will be called back, and after stopService is called, it will destroy Service. Even if multiple clients start the service, you can destroy Service by calling stopService once. Another advantage in this way is that the Service can exit actively by calling stopSelf.
- OnBind will be called the first time bindService is called onBind. If there are multiple clients connecting to the service, onUnbind will be called onUnbind and destroy Service will be called onUnbind when the last client calls unbindService.
If startService / bindService is used mixed, what will happen to the service's life in the same period? In fact, just remember the above ideas, this problem is easy to understand. First of all, startService corresponds to stopService. Before there is no stopService, bindService corresponds to unbindService, and before there is no unbindService, destroy Service will not be destroyed.
Add permissions to the service
I believe that the services we provide are public, that is, all applications can be called. But if I want my service to only give specific application calls, how should I set it up? We can add permissions to the service. Regarding permissions, the Android system gives permissions for four categories:
- Normal level: These permissions cannot really harm the user (such as changing wallpapers). When the program requires these permissions, the developer does not need to specify the program to automatically grant these permissions.
- Danger level: These permissions may cause real harm (such as making calls, opening web links, etc.), and if you want to use them, the developer needs to declare the corresponding permissions in it.
- Signature Level: If the application uses the same signature certificate, these permissions are automatically granted to the program that declares or creates these permissions. The purpose of designing this hierarchical permissions is to facilitate data sharing among components.
- Signature/System Level: Like the signature level, the exception is that system images automatically obtain these permissions, and this level is designed for device manufacturers.
<uses-permission android:name=""/>
If we want the Service we develop can only be called by a specific Client, we can add custom permissions. For example, at the danger level, we can declare the corresponding permissions in it. Only when the application also sets this permission can the service be started normally.
About AIDL Remote Service
The so-called AIDL remote service is a service that runs in another process. The services we call usually run on the main thread. To use AIDL services, you must write the AIDL interface. If you expose the interface to the outside, you can interact with the remote service. There are several notable points for AIDL:
- Functions of the AIDL interface do not support overloading, that is, the function names cannot be the same, even if the number of function parameters is different.
- The only parameters passed by the AIDL interface are the basic data type, String and CharSequence, List and Map, and classes that implement the interface.
- Since AIDL is a service in another process, is the AIDL interface called by the client blocked? The answer is yes. If the oneway modifier is not added, the interface called by the client will be blocked, but the oneway modifier also has restrictions, that is, the methods below the oneway interface must return void type and cannot return other types of data.
How to upgrade the AIDL interface?
When working on a relatively large project, the project will continue to iterate, and it is possible to add and modify the AIDL interface. How to ensure that the AIDL interface and the old interface will not be confused? According to my experience, the following summary is:
- For interfaces that add or delete parameters: access to AIDL functions will detect parameters. Interfaces with parameters with Client can call the Service's interface (regardless of whether there are or not). Conversely, if the Client interface does not have parameters, it can only call the interfaces without parameters with Service. For example, if our new interface definition function adds parameters, the client must be modified at the same time or in advance. Otherwise, if we publish the service application of the new interface, the client cannot be called, but the client can use the new interface to access the services of the old interface.
- For the interface of adding and deleting functions: adding functions on the server does not affect the client. On the contrary, adding interfaces that the client does not have will be accessed without effect. If the client adds the interface with a return value, the default value will be returned.
Service manages multiple clients
If the Service has multiple clients, how can you communicate with them safely? How to give each client a callback result? Here I want to talk about a problem that I have encountered in the recent project. I want to do a public service in the project, similar to fingerprint unlocking. Other applications get the results by calling my service. I designed two interfaces: start(callback) and stop(). From the beginning, I used a single callback method, that is, define a callback in the code. Whoever calls start will set the callback to whom. Only the last client who calls start can get the callback. The code is as follows:
private Callbak mCallback; public void start(Callback callback) { = callback; } public void stop() { = null; }
This method is very effective in a single application. In multiple applications, as long as the application can execute start and stop in sequence, then there is no problem with the design of this interface. But things are not as simple as expected. If Client1 calls start and Client2 also calls start, then Client1 wants to stop, what will happen? Then the entire service will be stopped. This is a big problem that arises in the service I designed. Later, I thought about making some changes to my service interface to adapt to this kind of multi-application indirect calls. My first idea is to use register and unregister to collect all callbacks using a list. You can cycle when calling back, and you can also judge the number of lists when stop. If it is less than or equal to 1, then execute stop:
private List<Callbak> mCallbacks; public void start(Callback callback) { (callback); } public void stop(Callback callback) { (callback); if(() >= 1) return; }
Considering the interface upgrade, this change is minimal, and only one parameter is added to the stop. However, this method also has problems. The reference to Callback in our service is a strong reference. If the Client exits abnormally, the reference will still be there and will accumulate more and more. During callbacks, there may also be a DeadObjectException error. Looking up information through the network, I found the RemoteCallbackList. RemoteCallbackList is also a list, which saves the callback interface, and uses Link-To-Death callback (receive this Binder object in the Service, and register a DeathRecipient callback with () and implements DeathRecipient. When the Client exits unexpectedly, () will be called back, and we can release the relevant resources here.). The final code is as follows:
private RemoteCallbackList<Callback> mCallbacks = new RemoteCallbackList<>(); public void start(Callback callback) { (callback); } public void stop(Callback callback) { (callback); if(() >= 1) return; } private void notifyResult(String result) { final int len = (); for (int i = 0; i < len; i++) { try { (i).onResult(result); } catch (RemoteException e) { (); } } (); }
Use Messenger to implement Servie communication with Client
Messenger is based on Handler, and the data is passed and processed by adding a Handler to Messenger. After that, the communication between the Service and Client is carried out through the passed Handler. In this way, there is no need to define the AIDL interface, so there will be no trouble of not corresponding to the interface version caused by modifying the AIDl interface.
The use of Messenger is to pass messages through Handler. The client sends a Message, which points to a Messenger, which also holds a Binder object (MessengerImpl) of the client. The server uses this Binder object to communicate with the client.
The above is all the content of this article. I hope it will be helpful to everyone's study and I hope everyone will support me more.