Framework 源码解析知识梳理(3) 应用进程之间的通讯实现

1、前言

Framework 源码解析知识梳理(1) - 应用进程与 AMS 的通讯实现Framework 源码解析知识梳理(2) - 应用进程与 WMS 的通讯实现 这两篇文章中,咱们介绍了应用进程与AMS以及WMS之间的通讯实现,可是逻辑仍是比较绕的,为了方便你们更好地理解,咱们介绍一下你们见得比较多的应用进程间通讯的实现。java

2、例子

提及应用进程之间的通讯,相信你们都不陌生,应用进程之间通讯最经常使用的方式就是AIDL,下面,咱们先演示一个AIDL的简单例子,接下来,咱们再分析它的内部实现。android

2.1 服务端

2.1.1 编写 AIDL 文件

第一件事,就是服务端须要声明本身能够为客户端提供什么功能,而这一声明则须要经过一个.aidl文件来实现,咱们先简要介绍介绍一下AIDL文件:bash

  • AIDL文件的后缀为*.aidl
  • 对于AIDL默认支持的数据类型,是不须要导入包的,这些默认的数据类型包括:
  • 基本数据类型:byte/short/int/long/float/double/boolean/char
  • String/CharSequence
  • List<T>:其中T必须是AIDL支持的类型,或者是其它AIDL生成的接口,或者是实现了Parcelable接口的对象,List支持泛型
  • Map:它的要求和List相似,可是不支持泛型
  • Tag标签:对于接口方法中的形参,咱们须要用in/out/inout三种关键词去修饰:
  • in:表示服务端会收到客户端的完整对象,可是在服务端对这个对象的修改不会同步给客户端
  • out:表示服务端会收到客户端的空对象,它对这个对象的修改会同步到客户端
  • inout:表示服务端会收到客户端的完整对象,它对这个对象的修改会同步到客户端

说了这么多,咱们最多见的需求无非就是两个:让复杂对象实现Parcelable接口实现传输以及定义接口方法。app

(1) Parcelable 的实现ide

咱们能够很方便地经过AS插件来让某个对象实现Parcelable接口,并自动补全要实现的方法: 函数

安装完以后重启,咱们本来的对象为:

public class InObject {

    private int inData;

    public int getInData() {
        return inData;
    }

    public void setInData(int inData) {
        this.inData = inData;
    }
    
}
复制代码

在文件的空白处,点击右键Generate -> Parcelableui

它就会帮咱们实现 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) {
            return new InObject[size];
        }
    };
}
复制代码

(2) 编写 AIDL 文件this

点击File -> New -> AIDL -> AIDL File以后,会多出一个名叫aidl的文件夹,以后咱们全部须要用到的aidl文件都被存放在这里: spa

初始时候, 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);
}
复制代码

当咱们编译以后,就会在下面这个路径中生成一个Java文件:插件

/*
 * 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;
                }
                case TRANSACTION_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;
}
复制代码

整个结构图为:

有没有感受在 Framework 源码解析知识梳理(1) - 应用进程与 AMS 的通讯实现Framework 源码解析知识梳理(2) - 应用进程与 WMS 的通讯实现 中也见到过相似的东西 asInterface/asBinder/transact/onTransact/Stub/Proxy...,这个咱们以后再来解释,下面咱们介绍服务端的第二步操做。

2.1.2 编写 Service

既然服务端已经定义好了接口,那么接下来就服务端就须要实现这些接口:

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) {
        return mBinder;
    }
}
复制代码

在这个Service中,咱们只须要实现AIDLInterface.Stub接口中的basicTypes接口就能够了,它就是咱们在AIDL文件中定义的接口,再把这个对象经过onBinde方法返回。

2.1.3 声明 Service

最后一步,在AndroidManifest.xml文件中声明这个Service

<service
            android:name=".server.AIDLService"
            android:enabled="true"
            android:exported="true">
        </service>
复制代码

2.2 客户端

2.2.1 编写 AIDL 文件

和服务端相似,客户端也须要和服务端同样,生成一个相同的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);
}
复制代码

和服务端相似,咱们也会获得一个由aidl生成的Java接口文件。

2.2.2 绑定服务,并调用

(1) 绑定服务

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;
        }
    };
复制代码

(2) 调用接口

public void sayHello(View view) {
        try {
            if (mBinder != null) {
                mBinder.basicTypes(0, 0, false, 0, 0, "aaa");
            }
        } catch (RemoteException e) {
            e.printStackTrace();
        }
    }
复制代码

以后咱们打印出响应的log

3、实现原理

下面,咱们就一块儿来分析一下经过AIDL实现的进程间通讯的原理。

**(1) 客户端得到服务端的远程代理对象 IBinder **

咱们从客户端提及,当咱们在客户端调用了bindService方法以后,就会启动服务端实现的AIDLService,而在该AIDLServiceonBind()方法中返回了AIDLInterface.Stub的实现类,绑定成功以后,客户端就经过onServiceConnected的回调,获得了它在客户端进程的远程代理对象IBinder

(2) AIDLInterface.Stub.asInterface(IBinder)

在拿到这个IBinder对象以后,咱们经过AIDLInterface.Stub.asInterface(IBinder)方法,对这个IBinder进行了一层包装,转换成为AIDLInterface接口类,那么这个AIDLInterface是怎么来的呢,它就是经过咱们在客户端定义的aidl文件在编译时生成的,能够看到,最终asInterface方法会返回给咱们一个AIDLInterface的实现类ProxyIBinder则被保存为它内部的一个成员变量mRemote

也就是说,咱们在客户端中保存的 mBinder其实是一个由 AIDL文件所生成的 Proxy对象。

(3) 调用 AIDLInterface 的接口方法

Proxy实现了AIDLInterface中定义的接口方法,当咱们调用它的接口方法时:

其实是经过它内部的 mRemote对象的 transact方法发送消息:

(4) 服务端接收消息

那么客户端发送的这一消息去哪里了呢,回想一下在服务端中经过onBind()放回的IBinder,它实际上是由AIDL文件生成的AIDLInterface.java中的内部类Stub的实现,而在Stub中有一个回调函数onTransact,当咱们经过客户端发送消息以后,那么服务端的Stub对象就会经过onTransact收到这一发送的消息:

(5) 调用子类的处理逻辑

onTransact方法中,又会去调用AIDLInterface定义的接口:

而咱们在服务端AIDLService中实现了该接口,所以,最终就打印出了咱们在上面所看到的文字:

4、进一步讨论

如今,咱们经过这一进程间的通讯过程来复习一下前面两篇文章中讨论的应用进程与AMSWMS之间的通讯实现。

Framework 源码解析知识梳理(1) - 应用进程与 AMS 的通讯实现 中,AMS所在进程在收到消息以后,进行了下面的处理逻辑:

这里面的处理逻辑就至关于咱们在客户端中 onServiceConnected中作的工做同样, IApplicationThread其实是一个 ApplicaionThreadProxy对象,和咱们上面 AIDLInterface其实是一个 AIDLInterface.Stub.Proxy的原理是同样的。当咱们调用了它的接口方法时,就是经过内部的 mRemote发送了消息。

AMS通讯中,接收消息的是应用进程中的ApplicationThread,而咱们上面例子中接收消息的是服务端进程的AIDLInterface.Stub的实现类mBinder,一样是在onTransact()方法中接收消息,再由子类去实现处理的逻辑。

Framework 源码解析知识梳理(2) - 应用进程与 WMS 的通讯实现 中的原理就更好解释了,由于它就是经过AIDL来实现的,咱们从客户端向WMS所在进程创建会话时,是经过IWindowSession来实现的,会话过程当中IWindowSession就对应于AIDLInterface,而WMS中的IWindowSession.Stub的实现类Session,就对应于上面咱们在AIDLService中定义的AIDLInterface.Stub的实现类mBinder

5、小结

其实AIDL并无什么神秘的东西,它的本质就是Binder通讯,咱们定义aidl文件的目的,主要有两个:

  • 为了让应用进程间通讯的实现者不用再去编写transact/onTransact里面的代码,由于这些东西和业务逻辑是无关的,只不过是简单的发送消息、接收消息。
  • 让服务端、客户端只关心接口的定义。

若是咱们明白了AIDL的原理,那么咱们彻底能够不用定义AIDL文件,本身去参考由AIDL文件所生成的Java文件的逻辑,进行消息的发送和处理。


更多文章,欢迎访问个人 Android 知识梳理系列:

相关文章
相关标签/搜索