preface

Wanted to write an article about Binder, but knew nothing about Binder. After reading a large number of excellent articles, terrified of the pen, not afraid of the article was laughed at, afraid of the mistake! Hope you find time to read this article at the same time, can be skeptical of the knowledge points of the article, common discussion, common progress!

1. Serialization

In everyday development, when an Intent is used to redirect data to an Activity, the data can only be carried by implementing the Serializable or Parcelable interface. The Serializable interface and Parcelabel interface mainly complete the object serialization process. Serialization is also required to persist objects to devices or over the network.

1. The Serializable interface

The Serializable interface is provided by Java and provides standard serialization and deserialization operations for objects. Generally, an object implementing the Serializable interface has the ability to be serialized and deserialized, and almost all of the work is done automatically by the system. Serializable interface in the Serializable interface serialVersionID can be specified or not specified, it is used to determine whether the version of the class before serialization and deserialization has changed. If the value of this variable is inconsistent, it indicates that some property or method in the class has changed. Deserialization is a problem. (Static member variables and transient keyword tag members do not participate in the serialization process)

2. The Parcelable interface

The Parcelable interface is provided by Android and its implementation is relatively complex. Objects of the class that implements the interface can then be passed in Intent and Binder.

3. Differences between the two

Serializable is an interface provided by Java. It is easy to use, but serialization and deserialization require a lot of IO operations, so the overhead is high. Parcelable is an Android serialization method that is cumbersome to use when efficient. In Android development, the Serializable interface is recommended for serializing objects to the device or transferring them over the network. In other cases, the Parcelable interface is recommended, especially for memory serialization. For example, IntEnts and binders transport data.

Second, the AIDL

At the Java layer, Binder uses AIDL (Android Interface Definition Language), an accepted programming interface for clients and services to communicate with each other using interprocess communication (IPC). AIDL is only necessary if you want to handle multiple threads in a service, and Messager is recommended if you want to handle multiple threads in a single application.

1. Data types supported by AIDL

  • All primitive types in the Java programming language (such as int, long, CHAR, Boolean, and so on)
  • String and CharSequence
  • All objects that implement the Parcelable interface
  • AIDL interface
  • List, currently List only supports the ArrayList type, which holds elements of the type described above.
  • Map, currently only supports the HashMap type, holding elements that must be of the above type.

Custom Parcelable objects and AIDL interfaces must be displayed and imported into AIDL files.

Trend of data

Parcelable objects and AIDL interfaces must indicate the direction of the data before they are used:

  • In client flows to server
  • Out The server flows to the client
  • The Inout server can interact with the client

Example:

void addUser(inout User user);
Copy the code

2. Implementation of the server side

2.1. Define data objects

Define a Parcelable interface implemented as a data object for client and server transmission.

public class User implements Parcelable {

    private String username;

    private String address;

    public User() {
    }
    
    public User(String username, String address) {
        this.username = username;
        this.address = address;
    }

    User(Parcel in) {
       readFromParcel(in); } // System default generation, deserialization process, Public static final Creator<User> Creator = new Creator<User>() {@override public User createFromParcel(Parcelin) {
            return new User(in);
        }

        @Override
        public User[] newArray(int size) {
            returnnew User[size]; }}; // Override public int returns 1 @override public int only if the file descriptor existsdescribeContents() {
        return0; } // Serialization process, @Override public void writeToParcel(Parcel dest, int flags) {dest.writeString(username); dest.writeString(address); } @Override public StringtoString() {
        return username+":"+address;
    }

    public void readFromParcel(Parcel in){ username=in.readString(); address=in.readString(); }}Copy the code

2.2 abstract server services

Create a usermanger. aidl file that shows what the server can provide to the client.

addUser
getUser

package com.gitcode.server; // Import the type of the pass object here, for example, User import com.gitcode.server.user; interface UserManager { void addUser(inout User user); User getUser(int index); }Copy the code

After the userManager.aidl file is defined, the system generates the userManager.java file by default.


The code for userManager.java is shown below, with some implementations removed to save space.

public interface UserManager extends android.os.IInterface { public static abstract class Stub extends android.os.Binder  implements com.gitcode.server.UserManager { private static final String DESCRIPTOR ="com.gitcode.server.UserManager";
        
        public Stub() {
            this.attachInterface(this, DESCRIPTOR);
        }
        
        public static com.gitcode.server.UserManager asInterface(android.os.IBinder obj) {
            if ((obj == null)) {
                return null;
            }
            android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
            if(((iin ! = null) && (iin instanceof com.gitcode.server.UserManager))) {return ((com.gitcode.server.UserManager) iin);
            }
            return new Stub.Proxy(obj);
        }

        @Override
        public android.os.IBinder asBinder() {
            return this;
        }

        @Override
        public boolean onTransact(int code, android.os.Parcel data, android.os.Parcel reply, int flags) throws android.os.RemoteException {
           ......
        }

        private static class Proxy implements com.gitcode.server.UserManager {
            private android.os.IBinder mRemote;

            Proxy(android.os.IBinder remote) {
                mRemote = remote;
            }

            @Override
            public android.os.IBinder asBinder() {
                return mRemote;
            }

            public String getInterfaceDescriptor() {
                return DESCRIPTOR;
            }

            @Override
            public void addUser(com.gitcode.server.User user) throws android.os.RemoteException {
                 ......
            }

            @Override
            public com.gitcode.server.User getUser(int index) throws android.os.RemoteException {
                .....
            }
        }

        static final int TRANSACTION_addUser = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
        static final int TRANSACTION_getUser = (android.os.IBinder.FIRST_CALL_TRANSACTION + 1);
    }

    public void addUser(com.gitcode.server.User user) throws android.os.RemoteException;

    public com.gitcode.server.User getUser(int index) throws android.os.RemoteException;
}

Copy the code

As you can see from the above, UserManager is itself an interface and inherits the IInterface interface. Usermanager. Java declares addUser and getUser, the same declaration as in userManager. aidl. Declare two integral types, TRANSACTION_addUser and TRANSACTION_getUser, to identify which server-side method to call in the transact() method. If the server and client are in different processes, the method call goes through the Transact () method, and the logic is done by Stub and Proxy inner classes.

Some of the conceptual and method implications of inner class stubs:

DESCRIPTOR

The unique identification of a Binder, typically with the full name of the current class name.

asInterface(IBinder obj)

The Binder object on the server is converted into an AIDL interface object on the client. If the client and the server are in the same process, the Stub object itself is directly returned. If the process is different, the stub. proxy object encapsulated by the system is returned.

asBinder

Returns the current Binder object

onTransact(int code, Parcel data, Parcel reply, int flags)

It runs in a server side Binder thread pool, and when a client makes a cross-process request, the system encapsulates it and handles it with this method. Code specifies what method to call on the server. Data indicates the data sent by the client, and reply indicates the reply from the server to the client.

The internal proxy class Poxy represents operations that clients can remotely perform on servers.

AddUser runs on the client, and when the client calls it remotely,


To create user. aidl in the same directory, you can directly copy userManager. aidl with the following changes.

package com.gitcode.server;

parcelable User;
Copy the code

On the server side, the Service is typically represented as Service, defined as UserServcie, and inherited from Service.

public class UserService extends Service {
    private static final String TAG = "Server";
    private List<User> list = new ArrayList<>();

    @Override
    public void onCreate() {
        super.onCreate();
        list.add(new User("GitCode"."Shenzhen"));
    }

    @Override
    public IBinder onBind(Intent intent) {
        Log.i(TAG,"on Bind");
        return stub;
    }


    private UserManager.Stub stub = new UserManager.Stub() {
        @Override
        public void addUser(User user) throws RemoteException {
            list.add(user);
            Log.i(TAG,"add user:"+user);
        }

        @Override
        public User getUser(int index) throws RemoteException {
            Log.i(TAG,"get user,index:"+index);
            returnlist.size() > index && index >= 0 ? list.get(index) : null; }}; }Copy the code

Declare the Service in the Androidmanifest.xml file. The two components form a separate app to represent the two processes and interact with each other through AIDL. Start the service on the client side with bindService().

<service android:name="com.gitcode.server.UserService"
    android:enabled="true"
    android:exported="true">
        <intent-filter>
            <action android:name="com.gitcode.server.userservice"/>
            <category android:name="android.intent.category.DEFAULT"/>
        </intent-filter>
</service>
Copy the code

3. Implementation of the client

The client makes requests to the server through a common convention (usermanger.aidl), and the server responds to the request from the client. In order to improve efficiency and reduce errors, AIDL files on the client side are implemented by copying. Copy the entire aiDL file from the server to the main directory of the client without modifying it.

Create a directory on the client side that is similar to the User package on the server side, and copy the User class from it without making any changes

public class MainActivity extends AppCompatActivity {
    private static final String TAG = "Client";
    private UserManager mUserManager;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        toBindService();
    }

    private void toBindService() {
        Intent intent = new Intent("com.gitcode.server.userservice");
        intent.setPackage("com.gitcode.server");
        bindService(intent, connection, Context.BIND_AUTO_CREATE);
    }

    private ServiceConnection connection = new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            mUserManager = UserManager.Stub.asInterface(service);

            try {
                User user = mUserManager.getUser(0);
                Log.e(TAG, user.toString());

                mUserManager.addUser(new User("Zhang"."Beijing"));
            } catch (RemoteException e) {
                e.printStackTrace();
            }
        }

        @Override
        public void onServiceDisconnected(ComponentName name) {
        }
    };
}
Copy the code

Operation effect:

Client:

4, summary

The client calls the service’s methods, the called methods run in the server’s Binder thread pool, and the client is suspended. If the server methods perform time-consuming operations, the client ANR will result, so do not access remote service methods from the client main thread. At the same time, the server should not create a new thread to run the service method, because the method is handed over to the thread pool, and the data should be concurrently accessed.

AIDL can be said to provide a wrapper for application layer development, Binder mechanism is not too much understanding, through the generated UserManager.java, a preliminary understanding of Binder IPC mechanism. Using AIDL for data communication between processes, more attention is paid to details and business implementation.

Demo address above

Third, Binder

Binder is an IPC mechanism provided by Android. Since Android is based on the Linux kernel, Binder also has other IPC mechanisms such as sockets, shared memory, pipes, and message queues. Binder mechanisms provide better performance, stability and security without using the original IPC mechanism. For example, sockets are a set of common interfaces with low transmission rates and are suitable for network transmission. However, pipes and message queues require two copies of data, making shared content difficult to control. Binder copies data only once and uses a C/S architecture with clear responsibilities and easy maintenance and use.

As you can see from the following figure, Binder implements cross-process communication through memory mapping. Binder only acts as A data carrier in the IPC mechanism. When process A writes data to the virtual memory space, the data is fed back to process B’s virtual memory space in real time. The entire process of sending data is copied from user space to virtual memory space only once.

Server

The Server process needs to register services with the ServiceManger to inform the public of the services it can provide. For example, in AIDL above, UserService is registered and the Client is provided with operations to add and obtain users. During the registration process, the Server process is the client and the ServiceManger is the Server.

Client

Perform service logic operations on the Sever process. Use the Service name to find the corresponding Service in the ServiceManger.

ServiceManager

The ServiceManger centrally manages all services in the system. A Service registers with the ServiceManger in the lookup table. When the Client requests the ServiceManger to query the corresponding Service based on the Service name, the ServiceManger can query the corresponding Service in the lookup table.

The figure shows the C/S architecture of the three. For example, when a Client queries a Service from a ServiceManger, the Client is the Client and ServiceManger is the server. The dashed lines represent interprocess communication with the Binder, so understanding the flow of a dashed line gives you an idea of the Binder mechanism.

1. Service registration process

The getSystemService() function is used to get system-related services, such as ActivityManagerService. Where do these services come from? Some Service processes register their services with the ServiceManager to indicate what services they can provide to clients. Learn how the Binder mechanism works by understanding the Service registration process.

1.1 ProcessState

Each process creates a unique ProcessState object in singleton mode. In its constructor, the /dev/binder device is opened through the open_driver() method, which is equivalent to the Server process opening a channel to interact with the kernel’s binder drivers, and the maximum number of threads supported is set to 15. Binder devices are virtual devices set up in the Android kernel for interprocess communication.

1.2 BpBinder and BBinder

Both BpBinder and BBinder are Android and Binder communication related representatives, one to one, derived from IBinder. If BpBinder represents the client, BBinder represents the server, and a BpBinder interacts with the corresponding BBinder through the handler identifier. With Binder systems, the handler identifier 0 represents the BBinder for the ServiceManger. BpBinder and BBinder do not operate directly with binder devices that ProcessState opens.

1.3 BpServiceManger and BnserviceManger

Both inherit from IServiceManger and are related to business logic, so to speak, by architasing business layer logic onto Binder mechanisms. BnserviceManger, derived from IServiceManger BBinder, communicates directly with Binder, while BpServiceManger points to BpBinder via mRemote.

1.4 Registering services

Through the above three sections, BpServiceManger object realizes the business function to IServiceManger, and BpBinder as the communication representative, the following analysis of the registration process.

The string name and Service object are passed as arguments to the BpServiceManger object’s addService() function, which packages the argument data and passes it to BpBidner’s Transact () function. The logic of the business layer ends here, and the main function is to package the request information to the communication layer for processing.

The Transact () function at BpBinder calls the Transact () function of the IPCThreadState object, so BpBinder itself does not interact with the Binder device. Each thread has an IPCThreadState object with a buffer of mOut, which stores and forwards data from the Binder devices, and mIn, which receives data from the Binder devices. Interact with Binder devices through IOCTL.

1.5 summary

The Binder mechanism was analyzed through the Service registration process above. Binder is just a communication mechanism. Business can be based on Binder as well as other IPC mechanisms (BpServiceManger and BpBinder). Binder is complex because Android skillfully integrates business and communication through layers of encapsulation. The main thing is the design ideal is awesome.

2, ServiceManger

Based on the analysis in section 1, should there also be a class that inherits from BnServiceManger to handle remote requests?

Unfortunately, there is no BnServiceManger subclass on the server to respond to requests from remote clients. Instead, it is handled by ServiceManger.

2.1 Becoming the Service Management Center

ServiceManger opens binder devices with the binder_open function and maps memory. Identifies itself with handler equal to 0 to make itself the management center. When all services register with ServiceManger, The BpBinder 0 of Handle identifies the BBinder corresponding to ServiceManger. The ServiceManager stores information about the Service to be registered for Client to search. Not all services can be registered with ServiceManger. If the Server process does not have the permission of root or system, you need to add the corresponding item in Allowed.

2.2 Benefits of Centralized ServiceManger Management

  • Unified management, exert control right
  • The notification string name looks for Service
  • Server processes are unstable. With ServiceManger, clients can know the most dynamic Server processes in real time.

3, the Client

If the Client wants to use the Service provided by the Server process, what steps should it take?

3.1 query ServiceManger

When a Client wants to obtain information about a Service, it interacts with a ServiceManager and calls getService () to obtain information about the Service. The Client queries the corresponding Service from the ServiceManger based on the Service name. If the Service is not registered, the loop waits until the Service is registered. If registered, it corresponds to a BpXXXService that encapsulates a BpBinder to communicate with a remote Service through which the Client invokes the relevant business logic functions.

3.2 Processing of request information

The business function called by Client packages the request parameters and sends them to Binder drivers. BpBinder finds the corresponding Service through the value of handler to process them.

In section 1.4, the IPCThreadState object is addressed directly to the business layer by calling onTransact, an object implementing BnServiceXXX, in its executeCommand function. This is why in AIDL, response data is processed in the onTransact() function.

Four,

Through the study of Binder mechanism, understand how Android integrates Binder mechanism into applications through layers of encapsulation, and gain a deeper understanding of Binder mechanism. Better understanding of Binder mechanisms can be gained through the use of AIDL at the Java layer.

Personal level is limited, error please help correct, thank big guy. Give me a thumbs up if you like.

Around making

References:

In-depth understanding of Android Volume 1