Binder Summary 2-Binder use

In Binder’s summary of 1-Binder Principles, we have a general understanding of how Binder works.

As far as I am concerned, multi-process development is used in IM system development, which requires Binder to communicate. This paper is to write practical examples, involving the following points:

  1. AIDL

Includes supported data types, definitions, and usage

  1. Service

This includes some startup, data retrieval and data return.

The demo address of this article is AndroidBinderSample

AIDL

For a detailed description of AIDL, see the Android Interface Definition Language (AIDL) website.

It is an Interface definition language for Android, used to implement Binder communication process data transfer, the format is similar to Java interface code writing. It ends with an. Aidl file and is stored in the aidl folder under the man folder. Of course you can specify this by configuring aidl.

Data types supported by AIDL

  1. Basic Java types, including String and CharSequence
  2. List and Map, where the elements in List and Map must be data types supported by AIDL, and must be received on the Server using ArrayList or HashMap.
  3. Other aiDL-generated interfaces
  4. Implement Parcelable interface entities, you can see the detailed introduction to the principle and use of Parcelable Android

AIDL files. Generally speaking, AIDL is divided into two types of files, one is the interface type, which needs to be called and implemented. One is to declare Parcelable data, which is used to map the corresponding Java classes that implement the Parcelable interface to AIDL, and then be referenced by the AIDL interface file. Note that the package name of the AIDL file must be the same as the package name of the Java implementation Parcelable file. For example: we declare a JavaDomain under the package app.androidBinder.domain, which looks something like this

package app.androidbinder.domain; import android.os.Parcel; import android.os.Parcelable; * email: [email protected] * Description: * Update by: * update day: * * @author liweijie */ public class UserInfo implements Parcelable {//UserInfo() {
    }
    protected UserInfo(Parcel inPublic static final Creator<UserInfo> Creator = new Creator<UserInfo>() {@override public UserInfo createFromParcel(Parcelin) {
            return new UserInfo(in);
        }
        @Override
        public UserInfo[] newArray(int size) {
            returnnew UserInfo[size]; }}; @Override public intdescribeContents() {
        return0; } @override public void writeToParcel(Parcel dest, int flags) {// skip code} public voidreadFromParcel(Parcel in) {// omit code}}Copy the code

Userinfo.aidl = userinfo.aidl = userinfo.aidl = userinfo.aidl = userinfo.aidl

package app.androidbinder.domain; * email: [email protected] * Description: * Update by: * update day: * * @author liweijie */ parcelable UserInfo;Copy the code

This way, UserInfo can be referenced in other AIDL’s. The AIDL defines the interface type as follows:

// UserService.aidl
package app.androidbinder;
import java.util.List;
import app.androidbinder.domain.UserInfo;
// Declare any non-default types here with import statements
interface UserService {
    String getUserName(int userId);
    void saveUser(in UserInfo param);
    UserInfo getUserInfo(int userId);
    List<UserInfo> queryUser();
    //for in out inout
    UserInfo handleIn(in UserInfo info);
    UserInfo handleOu(out UserInfo info);
    UserInfo handleInOut(inout UserInfo info);
}
Copy the code

These are the two TYPES of AIDL files and their general writing.

In, out, inout

In official documents, all non-primitive parameters need to indicate the direction of the data, which can be in, out, or inout. The default primitive is in, not other streams. The non-primitives specified here refer to arguments other than Java’s primitive types, namely objects. We need to know the direction of this parameter when using AIDL. So what is a directional marker for data? First, the direction of the data is marked for that method parameter passed in the client. Data flow identifiers cannot be used on return parameters, only on method parameters.

  1. In: he said this parameter can only flow from the client to the server, such as the client sends a User object to the server, the server will receive a complete User object, and then if the service side of the object, then the change is not reflected in the client, the flow is only from the client to the server.
  2. Out: When the client passes a parameter to the server, the server receives an empty object. If the server operates on the object, the object will be reflected to the client. For example, if the client passes a User object to the server, the server receives an empty User object (not null, just like a new User object). When the server makes changes to the User object, its value changes are reflected to the client.
  3. Inout, which has both functions, that is, the client passes the object to the server, can receive the complete object, and the server changes the object, will also reflect the client. To sum up, in is like passing a value, and out is like passing a reference, except that the reference value of out is null on the server side. Inout has both functions, and the default is in.

Service

The onBind() method returns the IBinder object that implements the AIDL interface. The onBind() method returns the IBinder object that implements the AIDL interface. The client is then connected via bindService(), called onServiceConnected method in ServiceConnection, and called asInterface() of the Stub in the aiDL-generated file to get the service instance from the client. The server-side methods are then called. Note that android:exported services need to be set to true in the manifest file for cross-app process calls. In general, we also configure some fillters for filtering. For information on how to use Service and some of its lifecycle, some of the method differences (such as startService and bindService) please refer to the documentation separately, which is not described here. Here is an example from this Demo, using the AIDL file above, before using it, ensure that the corresponding AIDL build file is successfully compiled:

Service side

package app.androidbinder2.services; import android.app.Service; import android.content.Intent; import android.os.Binder; import android.os.IBinder; import android.os.RemoteException; import android.support.annotation.Nullable; import android.util.Log; import java.util.ArrayList; import java.util.List; import app.androidbinder.UserService; import app.androidbinder.domain.UserInfo; * email: [email protected] * Description: * update by: * update day: ** @author liweijie */ public class App2Service extends Service {/** * private List<UserInfo> data = new ArrayList<>(); @Override public int onStartCommand(Intent intent, int flags, int startId) {return super.onStartCommand(intent, flags, startId);
    }
    @Nullable
    @Override
    public IBinder onBind(Intent intent) {
        data.clear();
        data.add(new UserInfo(1, "A", 20));
        data.add(new UserInfo(2, "B", 30));
        data.add(new UserInfo(3, "C", 40));
        data.add(new UserInfo(4, "D", 50));
        return userService;
    }
    private Binder userService = new UserService.Stub() {
        @Override
        public String getUserName(int userId) throws RemoteException {
            for (UserInfo item : data) {
                if (item.getUserId() == userId) {
                    returnitem.getUserName(); }}return null;
        }
        @Override
        public void saveUser(UserInfo param) throws RemoteException {
            data.add(param);
        }
        @Override
        public UserInfo getUserInfo(int userId) throws RemoteException {
            for (UserInfo item : data) {
                if (item.getUserId() == userId) {
                    returnitem; }}return null;
        }
        @Override
        public List<UserInfo> queryUser() throws RemoteException {
            return data;
        }
        @Override
        public UserInfo handleIn(UserInfo info) throws RemoteException {
            info.setUserName("Hey hey hey.");
            return info;
        }
        @Override
        public UserInfo handleOu(UserInfo info) throws RemoteException {
            if (info == null) {
                Log.e("App2Service"."UserInfi server is null");
                info = new UserInfo();
            }
            info.setUserName("Hee hee hee");
            return info;
        }
        @Override
        public UserInfo handleInOut(UserInfo info) throws RemoteException {
            info.setUserName("Ha ha ha.");
            returninfo; }}; }Copy the code

The configuration of the manifest file is:

     <service
            android:name=".services.App2Service"
            android:exported="true">
            <intent-filter>
                <action android:name="app.androidbinder2.user_service.action"/>
            </intent-filter>
        </service>
Copy the code

The client

The main code is as follows:

/ /... Private UserService userServerService; @Override protected void onCreate(Bundle savedInstanceState) { //... Omitted code} ServiceConnection userConnection = newServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            showToast("Connection successful");
            userServerService = UserService.Stub.asInterface(service);
        }
        @Override
        public void onServiceDisconnected(ComponentName name) {
            showToast("Disconnect from server"); }}; public void startServer(View view) { Intent startServer = new Intent(); startServer.setAction("app.androidbinder2.user_service.action");
        startServer.setComponent(new ComponentName("app.androidbinder2"."app.androidbinder2.services.App2Service"));
        bindService(startServer, userConnection, Service.BIND_AUTO_CREATE); {} public void getUserName (View View) try {/ / actual call String name = userServerService. GetUserName (1); showToast(name); } catch (Exception e) { e.printStackTrace(); showToast("Error occurred"); }} / /... Omit codeCopy the code

Practical use of AIDL

Let’s take the AIDL file defined above as our example. One requirement we need to implement is data exchange between two apps through AIDL. The same is true for multiple processes between one App. We will see examples later. In fact, the above is already listing the code of the example. Here are the general steps for calling Demo across APP processes:

  1. If you have custom data that needs to be passed by implementing Parcelable, write this Java file first
  2. In the client, write an AIDL file that introduces the Parcelable entities you wrote above, and write an AIDL interface (generally all cross-process interfaces are written together).
  3. After compiling, when there are no problems, you need to copy the above Parcelable Java files and AIDL files to the Server side with the same package name, and then compile.
  4. On the Server side, create a new Service implementation that inherits Service and, in onBinder(), return Stub subclasses that implement the AIDL interface files we compiled.
  5. On the Client side, obtain the Binder instance of the Server side through ServiceConnection and invoke it. The main use here is Stub’s static method asInterface(). The client can then make cross-process calls from this instance.

Communicate across processes in the same APP

In fact, we are in practice, the use of the most or this kind of situation, I am in contact with the IM system is used in their own design. In fact, it is no different from cross-app. In the same APP, it just needs an AIDL file. Because the amount of memory allocated to a process is limited, and the default main process handles more transactions, there are advantages in using multiple processes in terms of retention and data receiving and processing. To enable multiple processes in an Android APP, add Android: Process to the manifest file and set the name of the process in which the component is located. Such as

android:process=":local"// He sets the process name to package name +local, which indicates that the component is in its own private process. Components of other processes cannot run in the same process. android:process="com.app.sample.local"// Where name is the process name. This is a global process, and other application components can use ShareUID to run in the same process.Copy the code

The above is the basic use of AIDL, of course, in practice, it will be more complicated, for example, we will open multiple threads in THE IM process to process messages, receive messages, train messages, etc., and need to actively notify the main process through the way of callback, generally need one Service at the IM end, one Service at the main process end, etc. We’ll talk about that later.

If there are mistakes or infringement in this article, please also point out or contact to delete, thank you.

Refer to the article

Android Interface Definition Language (AIDL)

This paper introduces the principle and usage of Parcelable in Android in detail