One, foreword

In the Framework source code parsing knowledge summary (1) – application process and AMS communication implementation and Framework source code parsing knowledge summary (2) – application process and WMS communication implementation of these two articles, we introduce the application process and AMS and WMS communication implementation, But the logic is still quite convoluting, in order to facilitate you better understand, we introduce you see more inter-process communication implementation.

Second, the example

When it comes to communication between application processes, we are familiar with AIDL. The most common way to communicate between application processes is AIDL. Let’s first demonstrate a simple example of AIDL.

2.1 the service side

2.1.1 Writing AIDL files

The first thing is that the server needs to declare what it can do for the client, and this declaration needs to be done in an. Aidl file. Let’s take a brief look at the aiDL file:

  • AIDLThe file suffix is*.aidl
  • forAIDLDefault supported data types, which do not require package import, include:
  • Basic data types:byte/short/int/long/float/double/boolean/char
  • String/CharSequence
  • List<T>:TIt must beAIDLSupported types, or other typesAIDLGenerated interface, or implementedParcelableObject of the interface,ListSupports generics
  • Map: It requires andListSimilar, but without support for generics
  • TagTags: For parameters in interface methods, we need to usein/out/inoutThree key words to modify:
  • in: indicates that the server receives the complete object from the client, but changes made to the object on the server are not synchronized to the client
  • out: indicates that the server receives an empty object from the client and its changes to this object are synchronized to the client
  • inout: indicates that the server receives the complete object from the client and its changes to this object are synchronized to the client

Having said that, the most common requirements are two: to have complex objects implement the Parcelable interface for transport and to define interface methods.

(1) The implementation of Parcelable

We can easily use the AS plug-in to make an object implement the Parcelable interface and automatically complete the methods to implement:

public class InObject {

    private int inData;

    public int getInData() {
        return inData;
    }

    public void setInData(int inData) {
        this.inData = inData; }}Copy the code

In the margin of the file, right-click Generate -> Parcelable:

Parcelable

public class InObject implements Parcelable {

    private int inData;

    public int getInData() {
        return inData;
    }

    public void setInData(int inData) {
        this.inData = inData;
    }

    public InObject() {}

    @Override
    public int describeContents() {
        return 0;
    }

    @Override
    public void writeToParcel(Parcel dest, int flags) {
        dest.writeInt(this.inData);
    }

    protected InObject(Parcel in) {
        this.inData = in.readInt();
    }

    public static final Parcelable.Creator<InObject> CREATOR = new Parcelable.Creator<InObject>() {
        @Override
        public InObject createFromParcel(Parcel source) {
            return new InObject(source);
        }

        @Override
        public InObject[] newArray(int size) {
            returnnew InObject[size]; }}; }Copy the code

(2) Write AIDL files

After going to File -> New -> AIDL -> AIDL File, there will be a New folder named AIDL, where all the AIDL files we need will be stored:

AS
AIDL

// AIDLInterface.aidl
package com.demo.lizejun.binderdemoclient;

// Declare any non-default types here with import statements

interface AIDLInterface {
    /**
     * Demonstrates some basic types that you can use as parameters
     * and return values in AIDL.
     */
    void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat,
            double aDouble, String aString);
}
Copy the code

When we compile, a Java file is generated in the following path:

/* * This file is auto-generated. DO NOT MODIFY. * Original file: /home/lizejun/Repository/RepoGithub/BinderDemo/app/src/main/aidl/com/demo/lizejun/binderdemoclient/AIDLInterface.aidl */  package com.demo.lizejun.binderdemoclient; // Declare any non-default types here with import statements public interface AIDLInterface extends android.os.IInterface { /** * Local-side IPC implementation stub class. */ public static abstract class Stub extends android.os.Binder implements com.demo.lizejun.binderdemoclient.AIDLInterface { private static final java.lang.String DESCRIPTOR ="com.demo.lizejun.binderdemoclient.AIDLInterface";

        /**
         * Construct the stub at attach it to the interface.
         */
        public Stub() {
            this.attachInterface(this, DESCRIPTOR);
        }

        /**
         * Cast an IBinder object into an com.demo.lizejun.binderdemoclient.AIDLInterface interface,
         * generating a proxy if needed.
         */
        public static com.demo.lizejun.binderdemoclient.AIDLInterface asInterface(android.os.IBinder obj) {
            if ((obj == null)) {
                return null;
            }
            android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
            if(((iin ! = null) && (iin instanceof com.demo.lizejun.binderdemoclient.AIDLInterface))) {return ((com.demo.lizejun.binderdemoclient.AIDLInterface) iin);
            }
            return new com.demo.lizejun.binderdemoclient.AIDLInterface.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 {
            switch (code) {
                case INTERFACE_TRANSACTION: {
                    reply.writeString(DESCRIPTOR);
                    return true;
                }
                caseTRANSACTION_basicTypes: { data.enforceInterface(DESCRIPTOR); int _arg0; _arg0 = data.readInt(); long _arg1; _arg1 = data.readLong(); boolean _arg2; _arg2 = (0 ! = data.readInt());float _arg3;
                    _arg3 = data.readFloat();
                    double _arg4;
                    _arg4 = data.readDouble();
                    java.lang.String _arg5;
                    _arg5 = data.readString();
                    this.basicTypes(_arg0, _arg1, _arg2, _arg3, _arg4, _arg5);
                    reply.writeNoException();
                    return true; }}return super.onTransact(code, data, reply, flags);
        }

        private static class Proxy implements com.demo.lizejun.binderdemoclient.AIDLInterface {
            private android.os.IBinder mRemote;

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

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

            public java.lang.String getInterfaceDescriptor() {
                return DESCRIPTOR;
            }

            /**
             * Demonstrates some basic types that you can use as parameters
             * and return values in AIDL.
             */
            @Override
            public void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat, double aDouble, java.lang.String aString) throws android.os.RemoteException {
                android.os.Parcel _data = android.os.Parcel.obtain();
                android.os.Parcel _reply = android.os.Parcel.obtain();
                try {
                    _data.writeInterfaceToken(DESCRIPTOR);
                    _data.writeInt(anInt);
                    _data.writeLong(aLong);
                    _data.writeInt(((aBoolean) ? (1) : (0)));
                    _data.writeFloat(aFloat);
                    _data.writeDouble(aDouble);
                    _data.writeString(aString);
                    mRemote.transact(Stub.TRANSACTION_basicTypes, _data, _reply, 0);
                    _reply.readException();
                } finally {
                    _reply.recycle();
                    _data.recycle();
                }
            }
        }

        static final int TRANSACTION_basicTypes = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
    }

    /**
     * Demonstrates some basic types that you can use as parameters
     * and return values in AIDL.
     */
    public void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat, double aDouble, java.lang.String aString) throws android.os.RemoteException;
}
Copy the code

The overall structure diagram is as follows:

Framework source code analysis knowledge comb (1) – application process and AMS communication implementation
Application process and WMS communication implementation
asInterface/asBinder/transact/onTransact/Stub/Proxy...

2.1.2 writing Service

Now that the server has defined the interfaces, it is up to the server to implement them:

public class AIDLService extends Service {

    private final AIDLInterface.Stub mBinder = new AIDLInterface.Stub() {
        @Override
        public void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat, double aDouble, String aString) throws RemoteException {
            Log.d("basicTypes"."basicTypes"); }}; @Nullable @Override public IBinder onBind(Intent intent) {returnmBinder; }}Copy the code

In this Service, we just need to implement the basicTypes interface in the AIDLInterface.Stub interface, which is the interface defined in the AIDL file, and return this object through the onBinde method.

2.1.3 statement Service

As a final step, declare the Service in the androidmanifest.xml file:

        <service
            android:name=".server.AIDLService"
            android:enabled="true"
            android:exported="true">
        </service>
Copy the code

2.2 the client

2.2.1 Writing AIDL files

Just like the server, the client needs to generate the same AIDL file as the server, without further details:

// AIDLInterface.aidl
package com.demo.lizejun.binderdemoclient;

// Declare any non-default types here with import statements

interface AIDLInterface {
    /**
     * Demonstrates some basic types that you can use as parameters
     * and return values in AIDL.
     */
    void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat,
            double aDouble, String aString);
}
Copy the code

Just like on the server side, we get a Java interface file generated by AIDL.

2.2.2 Bind the Service and invoke it

(1) Binding service

    private void bind() {
        Intent intent = new Intent();
        intent.setComponent(new ComponentName("com.demo.lizejun.binderdemo"."com.demo.lizejun.binderdemo.server.AIDLService"));
        boolean result = bindService(intent, mServiceConnection, BIND_AUTO_CREATE);
        Log.d("bind"."result=" + result);
    }

    private ServiceConnection mServiceConnection = new ServiceConnection() { @Override public void onServiceConnected(ComponentName name, IBinder service) { mBinder = AIDLInterface.Stub.asInterface(service); } @Override public void onServiceDisconnected(ComponentName name) { mBinder = null; }};Copy the code

(2) Call interface

    public void sayHello(View view) {
        try {
            if(mBinder ! = null) { mBinder.basicTypes(0, 0,false, 0, 0, "aaa"); } } catch (RemoteException e) { e.printStackTrace(); }}Copy the code

Then we print out the log of the response:

Three, the implementation principle

Let’s take a look at the principles of interprocess communication through AIDL.

**(1) The client obtains the remote proxy object IBinder ** from the server

The onBind() method of AIDLService returns the aidlInterface.stub implementation class. After the bindService method is called, the AIDLService implementation is started. The client gets its IBinder, a remote proxy object, from the onServiceConnected ected callback.

(2) AIDLInterface.Stub.asInterface(IBinder)

After he got the IBinder object, we through AIDLInterface. The Stub. AsInterface (IBinder) method, a layer of packaging for the IBinder into AIDLInterface interface class, So where does AIDLInterface come from? It’s generated at compile time through the aiDL file that we define on the client side, and as you can see, the asInterface method will eventually return us an AIDLInterface implementation class Proxy, IBinder is stored as an internal member variable, mRemote.

mBinder
AIDL
Proxy

(3) Call the AIDLInterface interface method

Proxy implements the interface methods defined in AIDLInterface, and when we call its interface methods:

mRemote
transact

(4) The server receives the message

Where did the message go? Recall that the IBinder put back by onBind() on the server is an implementation of the Stub class in aidlInterface.java generated by the AIDL file, which has a callback function called onTransact, When we send a message via the client, the Stub object on the server receives the message via onTransact:

(5) Invoke the processing logic of the subclass

In the onTransact method, the interface defined by AIDLInterface is called:

We implement this interface in the server AIDLService, so we end up printing the text we see above:

Fourth, further discussion

Now, let’s use this interprocess communication process to review the implementation of communication between application processes and AMS and WMS discussed in the previous two articles.

In the implementation of communication between application process and AMS, the process where AMS resides performs the following processing logic after receiving the message:

onServiceConnected
IApplicationThread
ApplicaionThreadProxy
AIDLInterface
AIDLInterface.Stub.Proxy
mRemote

In AMS communication, the message is received by the ApplicationThread in the application process. In the example above, the message is received by the mBinder of aidlInterface.Stub of the server process, also in the onTransact() method. The subclass implements the processing logic.

Application process and WMS communication in the implementation of the principle is better explained, because it is through AIDL to achieve, we from the client to WMS process to establish a session, is through IWindowSession to achieve, IWindowSession corresponds to AIDLInterface, while iWindowSession. Stub implements Session in WMS. This corresponds to the aidlInterface.stub implementation class mBinder defined above in AIDLService.

Five, the summary

There is nothing mysterious about AIDL. The essence of AIDL is Binder communication. We define AIDL files for two main purposes:

  • To keep implementers of application interprocess communication from having to writetransact/onTransactThe code inside, because these things have nothing to do with the business logic, is simply to send and receive messages.
  • Let the server and client only care about the definition of the interface.

If we understand the principle of AIDL, then we can refer to the logic of Java files generated by AIDL files for message sending and processing without defining AIDL files.


For more articles, please visit mineAndroidKnowledge combing series:

  • Android knowledge comb directory: www.jianshu.com/p/fd82d1899…
  • Personal homepage: lizejun.cn
  • Personal knowledge summary directory: lizejun.cn/categories/