** Briefly, the first time to write an article, a little not accustomed to, from determined to take a look at the Android system source code, read the simple system source code how to modify compilation, simple understanding of a little bit of Linux kernel driver knowledge, Then follow Luo’s Android system source code scenario analysis book to see the Android startup process and Activity jump, Binder mechanism because these knowledge points need to understand, so I decided to take a look at the Binder mechanism, this paper from the perspective of Service cross-process access. Because ibinder.stub uses the proxy pattern, this article also covers the proxy pattern briefly. (Note: Please correct me if there is anything wrong.)
The proxy pattern
Definition of proxy pattern: A proxy pattern is a structural pattern that provides a proxy for other objects to control access to that object. In some cases, an object is inappropriate or cannot directly reference another object, and a proxy object can act as an intermediary between the client and the target object.
Application scenario: When there is a problem with the Client accessing the RealSubject directly to achieve the goal of the Subject (for example, object creation is expensive, some operations require security control, or out-of-process access), a Proxy is required to perform the request operation instead of the Subject. For example: AOP aspect programming AIDL cross – process communication
The main roles of the proxy mode are as follows:
- Abstract (Subject) classes: Business methods that declare real subjects and proxy object implementations through interfaces or abstract classes.
- Real Subject class: Implements a concrete business in an abstract topic, is the Real object represented by the proxy object, and is the object ultimately referenced.
- Proxy class: Provides the same interface as a real topic, with internal references to real topics that can access, control, or extend the functionality of real topics.
The class diagram is as follows:
Code examples:
Public interface ProxyInterface {public void handlingEvents(); Public RealClass implements ProxyInterface {@override public void implements ProxyInterfacehandlingEvents() {
System.out.println("In process......"); }} /** * public class implements ProxyInterface {private ProxyInterface; public Pursuit(ProxyInterface real) { this.real = real; } @Override public voidhandlingEvents() {// Todo can handle real.handlingEvents() here before executing the real class method; Public static void main(String[] args) {RealClass real = new RealClass(); ProxyClass daili = new ProxyClass(real); daili.handlingEvents(); }Copy the code
Code description:
In the above code example, the proxy class calls the method of the same name as the real class. At this time, the method in the real class can be processed before and after execution, such as calculating the execution time of the method in the real class or printing the Log before and after the execution of the real class method. , the proxy class in the AIDL-generated Stub class invokes write and read driver operations before and after cross-process method execution, which will be discussed in more detail later. The benefits of proxy classes: The proxy class implements the same interface as the real class. Therefore, different proxy classes that implement the same interface can also proxy each other. The following code is used to illustrate:
/** * public class ProxyClass1 implements ProxyInterface {private ProxyInterface real; public Pursuit(ProxyInterface real) { this.real = real; } @Override public voidhandlingEvents() {
System.out.println("Before method");
real.handlingEvents();
System.out.println("After method"); }} /** * public class ProxyInterface {private ProxyInterface real; public Pursuit(ProxyInterface real) { this.real = real; } @Override public voidhandlingEvents() {
long startTime=System.currentTimeMillis();
real.handlingEvents();
long endTime=System.currentTimeMillis();
float excTime=(float)(endTime-startTime)/1000;
System.out.println("Execution time:"+excTime+"s"); Public static void main(String[] args) {RealClass real = new RealClass(); ProxyClass1 daili1 = new ProxyClass1(real); ProxyClass2 daili2 = new ProxyClass2(daili1); daili2.handlingEvents(); }Copy the code
In the above example code, we only use timing and printing to represent the use of proxy mode and some scenarios. Of course, there are more scenarios. First, we give some code from the proxy class of the AIDL dynamically generated Stub class:
/** create 3 objects _data input object, _reply output object, _result return value object and write parameter information to _data. Then call transact to send RPC request, and then suspend the current thread. The onTransace method on the server is called, and the current thread continues executing until it retrieves the return result of RPC from _reply and returns the data from _reply. */ android.os.parcel _data = android.os.parcel. Obtain (); android.os.Parcel _reply = android.os.Parcel.obtain(); java.util.List<com.example.administrator.aidlmessagetest.Person> _result; Try {// Before the method _data.writeInterfaceToken(DESCRIPTOR);if((person ! = null)) { _data.writeInt(1); person.writeToParcel(_data, 0); }else{ _data.writeInt(0); } // Truly call the inter-process method mremote. transact(stub.transaction_addperson, _data, _reply, 0); // After executing the method _reply.readexception (); _result = _reply.createTypedArrayList(com.example.administrator.aidlmessagetest.Person.CREATOR); } finally { _reply.recycle(); _data.recycle(); }return _result;
Copy the code
Dynamic proxy:
Now to generate a proxy object for an object, this proxy object will usually also be generated by writing a class, so you first write the class used to generate the proxy object. Java provides a “java.lang.reflect.Proxy” class after JDK1.5. This class provides a newProxyInstance method to create a Proxy object for an object.
Method in Proxy class: (the most common method is newProxyInstance)
-
Method 1: This method is used to get the InvocationHandler associated with the specified proxy object
static InvocationHandler getInvocationHandler(Object proxy) Copy the code
-
Method 2: This method is used to get a class object associated with a dynamic proxy class that specifies a class loader and a set of interfaces
static Class getProxyClass(ClassLoader loader, Class[] interfaces) Copy the code
-
Method 3: This method is used to determine whether the specified class is a dynamic proxy class
static boolean isProxyClass(Class cl) Copy the code
-
Method 4: This method is used to generate dynamic proxy class instances for the specified class loader, a set of interfaces, and the calling handler
static Object newProxyInstance(ClassLoader loader, Class[] interfaces, InvocationHandler h) Copy the code
Dynamic proxies in the JDK are implemented through reflection class Proxies and the InvocationHandler callback interface. However, classes in the JDK that want to dynamically proxy must implement an interface, that is, only the methods defined in the interface that the class implements can be proxy. This has some programming limitations, and reflection is not very efficient.
JDK dynamic proxy:
- JDK (Proxy)
- ASM (Bytecode Proxy) : When ASM creates class Bytecode, it operates at the assembly instruction level of the underlying JVM. This requires ASM users to have a certain understanding of the class organization structure and JVM assembly instructions (which need to be manually generated).
- CGLIB (Based on ASM packaging)
- JAVAASSIST (Proxy/BytecodeProxy) : Javassist is an open source library for analyzing, editing, and creating Java bytecodes. It was created by Shigeru Chiba of the Department of Mathematics and computer Science at Tokyo Institute of Technology. It has joined the open source JBoss application Server project to implement a dynamic AOP framework for JBoss by using Javassist for bytecode manipulation. Javassist is a subproject of JBoss, and its main advantages are simplicity and speed. You can dynamically change the structure of a class, or generate a class on the fly, using Java encoded form directly without needing to understand virtual machine instructions.
Bytecode Proxy: a Proxy class that directly generates binary Bytecode format
Performance comparison of dynamic proxy schemes:
- ASM and JAVAASSIST Bytecode Proxy generate in a similar manner, both of which are five times faster than CGLIB.
- CGLIB comes in second, twice as large as the JDK itself.
- Again, dynamic proxies are optimized for JDK1.6, and if the JDK is slower in older versions, note that the JDK also implements dynamic proxies through bytecode generation, not reflection.
- The JAVAASSIST Proxy dynamic Proxy interface is the slowest, slower than the JDK’s built-in interface.
(This is why some people online say that JAVAASSIST is slower than the JDK, and that it is best to use JAVAASSIST instead of the dynamic proxy interface it provides, consider using its bytecode generation approach.)
The reason for the difference: The bytecodes generated by each solution are different. JDK and CGLIB both take many factors into account and inherit or wrap some of their own classes, so the bytecodes generated by manual generation are very small, so they are very fast.
Hahaha, steal a picture to wake up
NewProxyInstance (ClassLoader loader,Class<? > [] interfaces, InvocationHandler handler), this method has three arguments, respectively, have a look at below:
- Loader: Which class loader is used to load the proxy object
- Interfaces: Interfaces that dynamic proxy classes need to implement
- Handler: When a dynamic proxy method is executed, it invokes the invoke method in this handler, and if it needs to be handled before and after the actual class method is executed, it is handled in the InvocationHandler, as shown in the following code:
Public Class TestInvacationHandler implements InvocationHandler {private Final ProxyInterface Object; public TestInvacationHandler(ProxyInterface object){ this.object = object; } /** * proxy: proxy object, newProxyInstance return object * proxy: You can use reflection to get information about proxy objects (proxy.getClass().getName()). * 2. Proxy objects can be returned for continuous invocation, which is why proxies exist. Since this is not a proxy object, * method: the method called * args: Public Object invoke(Object proxy, Method Method, Object[] args) throws Throwable {// System.out.println("---------before-------"); Invoke = method.invoke(Object, args); // Object invoke = method.invoke(proxy, args); System.out.println(system.out.println (system.out.println ("---------after-------");
returninvoke; }}Copy the code
ProxyInterface RealClass and TestInvacationHandler
public static void main(String[] args) {
ProxyInterface real = new RealClass();
ProxyInterface proInterface = (ProxyInterface)Proxy
.newProxyInstance(real.getClass().getClassLoader()
,RealClass.class.getInterfaces(), new TestInvacationHandler(real));
proInterface.handlingEvents();
}
Copy the code
This raises an interesting question: Why do we use dynamic proxies?
A: From the full invocation of dynamic proxy, we can see that dynamic proxy saves us from rewriting the methods in the interface and focuses on extending the corresponding functionality or method enhancement, which is much simpler than static proxy and can reduce the amount of business in the projectCopy the code
At this point, respect, the agency model to this end, if there is anything wrong, please correct.
Binder mechanism
The Service binding process also involves IBinder calls such as IActivityManager (ActivityManagerService).
Basic understanding of Binder mechanism:
- Process of communication
-
Process isolation: For security reasons, one process cannot manipulate another process’s data, and therefore an operating system must have process isolation. Virtual memory mechanism in Linux system, allocation of the linear continuous memory space for each process, the operating system will be this kind of virtual memory mapped to physical memory space, each process has its own virtual memory space, and cannot operate other processes memory space, each process can only operate their own virtual memory space, Only the operating system has permission to manipulate the physical memory space. Process isolation ensures memory safety for each process, but in most cases, data communication between different processes is unavoidable, so the operating system must provide a mechanism for cross-process communication. Process space is divided into Kernel space and user space. Kernel space is the running space of the system Kernel. A User Space is the Space in which a User program runs, and they are isolated from each other. The kernel has all access permissions, and the user space can also access the kernel space through the system interface. User Spaces can be accessed to each other through kernel Spaces, similar to intermediaries.
-
Advantages of Binder mechanism:
Good transmission performance:
- Socket: is a common interface, resulting in low transmission efficiency and high overhead
- Shared memory: Although data does not need to be copied during transmission, its control mechanism is complex
- Binder: Complex data type transfer can reuse memory, requires one copy of data
- Pipe and message queue: In store-and-forward mode, data needs to be copied at least twice, which is inefficient
Stability:
- Binder based on C/S architecture, the Server and Client are relatively independent, providing high stability.
- Shared memory is not distinguishable between Server and Client, and synchronization deadlock may occur. Binder is more stable than shared memory.
High security:
- Traditional process: communication mode does not make strict authentication for the identity of the communication parties, only to set up on the upper protocol
- Binder mechanism: Supports identity verification for both communication parties through the protocol itself, thus greatly improving security
-
Binder mechanism implementation schematic diagram :(borrowed picture, the location will be indicated below)
-
AIDL for Binder mechanism:
Aidl: new-> aiDL -> aiDL File-> Build
/ * * standard Proxy pattern, the Proxy class for the Proxy, this file is generated dynamically, so the Stub Proxy for fixed class name * / package com. Example. Administrator. Aidlmessagetest; // The interface is a subclass of Android.os.iInterface. Public Interface IPersonManager extends Android.os. IInterface {public interface IPersonManager extends Android.os. IInterface {public interface IPersonManager extends Android.os. IInterface {public interface IPersonManager extends Android.os. IInterface {public interface IPersonManager extends Android.os. IInterface {public interface IPersonManager extends Android.os. IInterface { Binder implements IPersonManager public Static Abstract Class Stub extends Android.os.Binder implements Com. Example. Administrator. Aidlmessagetest. IPersonManager {/ / this is the only binder Can see the full path name is IPersonManager private static final java.lang.String DESCRIPTOR ="com.example.administrator.aidlmessagetest.IPersonManager"; /** * This is the Stub constructor. How to write a service after writing the aiDL file? * private final IPersonManager.Stub mBinder = new IPersonManager.Stub() {} * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *Stub() { this.attachInterface(this, DESCRIPTOR); } // The only thing this method does is return the Stub object itself if it is the same process. The Proxy object. It returns a Stub Proxy the public static com. Example. Administrator. Aidlmessagetest. IPersonManager asInterface(android.os.IBinder obj) {if ((obj == null)) {
returnnull; } android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR); // We return the object in parentheses if the process is communicating with the same processif(((iin ! = null) && (iin instanceof com.example.administrator.aidlmessagetest.IPersonManager))) {return((com.example.administrator.aidlmessagetest.IPersonManager) iin); } // If it is not the same process, but two processes communicating with each other, then we have to return the stub. Proxy object that looks like a Stub Proxyreturnnew com.example.administrator.aidlmessagetest.IPersonManager.Stub.Proxy(obj); } // Return the current object @override public android.os.ibinderasBinder() {
returnthis; } /** * this method is only called when communicating across processes, not the same process. * * The first thing we need to understand is that this method usually returnstrueOf, * also returns onlytrueIt only makes sense if you returnfalseSo we usually use this method to do permission authentication, in fact, it is very easy to understand, since it is multi-process communication, * then our server process of course do not want anyone to come over to call, so permission authentication is necessary, permission authentication first skipped * * in addition, The onTransact method runs in a Binder thread pool. The client initiates a request, and the underlying Android code wraps the request into three parameters to call the onTransact method. The first parameter code represents the flag bit that the client wants to invoke the server method. * There may be n methods on the server and each method has a corresponding int value. * The code is the int value that identifies the server method that the client wants to call. * Data is the method parameter and reply is the method return value. This method runs in the binder thread pool, so server methods called in this method also run in the binder thread pool. Remember that if your server application is connected to multiple clients, * The parameters used in your method must be asynchronous, otherwise the values will be distorted! * Remember that! The conclusion is that the Binder approach must be synchronous !!!!!! */ @Override public boolean onTransact(int code, android.os.Parcel data, android.os.Parcel reply, int flags) throws android.os.RemoteException { switch (code) {case INTERFACE_TRANSACTION: {
reply.writeString(DESCRIPTOR);
return true;
}
case TRANSACTION_getPersonList: {
data.enforceInterface(DESCRIPTOR);
java.util.List<com.example.administrator.aidlmessagetest.Person> _result = this.getPersonList();
reply.writeNoException();
reply.writeTypedList(_result);
return true;
}
case TRANSACTION_addPerson: {
data.enforceInterface(DESCRIPTOR);
com.example.administrator.aidlmessagetest.Person _arg0;
if ((0 != data.readInt())) {
_arg0 = com.example.administrator.aidlmessagetest.Person.CREATOR.createFromParcel(data);
} else {
_arg0 = null;
}
this.addPerson(_arg0);
reply.writeNoException();
return true; }}returnsuper.onTransact(code, data, reply, flags); } // Private static class Proxy implements is returned only in the case of cross-process communication com.example.administrator.aidlmessagetest.IPersonManager { private android.os.IBinder mRemote; Proxy(android.os.IBinder remote) { mRemote = remote; } @Override public android.os.IBinderasBinder() {
return mRemote;
}
public java.lang.String getInterfaceDescriptor() {
returnDESCRIPTOR; } /** * There are two methods, one is getPersonList and one is addPerson, so we can analyze just one method ** First create 3 objects, * _data input object, _reply output object, _result return value object, Write the parameter information to _data, * then the transact method is called to send the RPC request, and then the current thread hangs, * the onTransace method on the server is called, and the current thread continues executing after the call ends. * until the return result of RPC is retrieved from _reply and the data of _reply is returned. So we need to be careful here. * When the client invokes the remote request, the thread on the current client will be suspended. * The client must not initiate the RPC request in the UI main thread, otherwise it will be anR. * * Note: These two methods run on the client side !!!!!!!!!!!!!!!! * note: If the method returns no value, , no _result return value object * / @ Override public Java. Util. List < com. Example. Administrator. Aidlmessagetest. Person > getPersonList () throws android.os.RemoteException { android.os.Parcel _data = android.os.Parcel.obtain(); android.os.Parcel _reply = android.os.Parcel.obtain(); java.util.List<com.example.administrator.aidlmessagetest.Person> _result; try { _data.writeInterfaceToken(DESCRIPTOR); mRemote.transact(Stub.TRANSACTION_getPersonList, _data, _reply, 0); _reply.readException(); _result = _reply.createTypedArrayList(com.example.administrator.aidlmessagetest.Person.CREATOR); } finally { _reply.recycle(); _data.recycle(); }return _result;
}
@Override
public void addPerson(com.example.administrator.aidlmessagetest.Person person) throws android.os.RemoteException {
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
try {
_data.writeInterfaceToken(DESCRIPTOR);
if((person ! = null)) { _data.writeInt(1); person.writeToParcel(_data, 0); }else {
_data.writeInt(0);
}
mRemote.transact(Stub.TRANSACTION_addPerson, _data, _reply, 0);
_reply.readException();
} finally {
_reply.recycle();
_data.recycle();
}
}
}
static final int TRANSACTION_getPersonList = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
static final int TRANSACTION_addPerson = (android.os.IBinder.FIRST_CALL_TRANSACTION + 1);
}
public java.util.List<com.example.administrator.aidlmessagetest.Person> getPersonList() throws android.os.RemoteException;
public void addPerson(com.example.administrator.aidlmessagetest.Person person) throws android.os.RemoteException;
}
Copy the code
In the bindService sequence diagram, service binding operations are performed using IBinder. How is IBinder obtained and when is it initialized? The Stub() constructor will also be called when it calls its parent class constructor. The code for the parent class constructor is as follows:
public Binder() { // private static native long getNativeBBinderHolder(); // getNativeBBinderHolder is a native method mObject = getNativeBBinderHolder(); NoImagePreloadHolder.sRegistry.registerNativeAllocation(this, mObject);if (FIND_POTENTIAL_LEAKS) {
final Class<? extends Binder> klass = getClass();
if((klass.isAnonymousClass() || klass.isMemberClass() || klass.isLocalClass()) && (klass.getModifiers() & Modifier.STATIC) == 0) { Log.w(TAG,"The following Binder class should be static or leaks might occur: "+klass.getCanonicalName()); }}}Copy the code
As you can see from the Binder constructor, the Binder constructor is called whenever a Stub is initialized, which initializes an IBinder using native layer C/C ++ code and registers it with the kernel Binder driver.
Moving on to the aiDL-generated code above, it is important to note that the Proxy class is on the client side (i.e. the calling side), so all methods are called on the client side, while onTransact methods are on the server side (i.e. the target side, cross-process access side). The method call procedure is: The Proxy method (the client) calls mremote.transact (), which triggers the target to execute Binder::onTransact()(the onTransact method in the code above), We can look at the transact() method and onTransact() method parameter comparison:
/* Argument one: Identifies the instruction, that is, what method is called. The client and server must agree on the code. Parameter 2: Data packet from the sender. Parameter 3: Receives a packet from the sender and writes data to the packet to return data to the sender. Parameter 4: Identifies a special operation. */ public boolean transact(int code, Parcel data, Parcel reply, int flags){} public boolean onTransact(int code, Parcel data, Parcel reply, int flags){}Copy the code
As you can see, these are paired operations. The mremote.transact () operation is a blocking operation, meaning that the data read directly from reply after the method is successfully executed is the data that the remote side populates with Binder::onTransact(). OnTransact (), which the compiler automatically generates for us, reads data from data and calls the corresponding method. The general call situation is shown as follows:
So far, over.
Finally steal graph, ha ha
Reference blog:
- Blog.csdn.net/carson_ho/a…
- www.cnblogs.com/punkisnotde…
- .
Want to study more underlying Binder mechanism may refer to: www.jianshu.com/p/fe816777f…
Please kindly advise