android-跨进程通信-AIDL解析

前言
在Android的系统中有几种跨进程通信方式,但我们使用最常见的就是Aidl,Aidl底层调用使用的是Binder,Binder通信又是Android四大组件跨进程通信的根本,所以学习Aidl有助于我们理解Binder,也助于我们后续理解四大组件。这里我会详细讲解Aidl的实现方式,以及生成的Aidl Java文件

  • Aidl是什么

  • Aidl支持哪些数据类型

  • Aidl的使用

  • Aidl生成文件的讲解

    Aidl(Android接口定义语言)是Android提供的一种ICP通信机制。 首先学习Aidl我们心中要有Clien端和Server端的概念,并且在Clien和Server通信过程中涉及到Binder的相关知识。

    Aidl支持的数据类型一共有4种

  1. Java的基本数据类型
  2. List和map: 集合中数据必须是Aidl支持的数据类型, 并且在Server端必须是ArrayList和HashMap
  3. 其他Aidl生成的接口
  4. 实现Parcelable的实体

Aidl的使用一般是两个App一个作为客户端一个作为服务端,以及一个 客户端和服务端通信的媒介Aidl,客户端的Aidl和服务端的Aidl 需要定义成一样的,在客户端序列化后要在服务端反序列换查询。
在这里我为了方便就讲客户端和服务端写在一起,实际的效果就是在使用Aidl与Service通讯。

新建Aidl

  • 在main目录新建Aidl,文件名叫StudentAidl, 并添加方法 getStudent(int no)方法;然后会在main目录下生成aidl文件夹,文件下会有一个和package相同的包,包下有一个 StudentAidl.aidl文件。
    在这里插入图片描述
    StudentAidl.aidl文件代码如下
interface StudentAidl {
/**
* 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);

String getStudent(int no);
}
  • 然后Build 一下项目,会在build/generated/source/aidl 下面看到生成了一个和你项目目录一下的StudentAidl的Java文件,这就是clien 和 server通信的媒介,代码结构如下
public interface StudentAidl extends android.os.IInterface
{
  public static abstract class Stub extends android.os.Binder implements com.example.aidl.StudentAidl{
	 ...
	 }
  public void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat, double aDouble, java.lang.String aString) throws android.os.RemoteException;
  public java.lang.String getStudent(int no) throws android.os.RemoteException;	 
}

新建server
StudentService 继承Service,实现Service中的抽象方法onBinder();在onCreate()中创建一个IBinder对象,在onBinder()中返回 IBinder对象。这里我们要知道 StudentAidl.Stub的Stub对象是继承于Binder,而Binder又实现了IBinder接口

public class StudentService extends Service {

    private String[] studentNo = {"张三", "李四", "王五"};
    private IBinder binder;


    @Override
    public void onCreate() {
        super.onCreate();
        Log.e("LOG", "-------StudentService---onCreate--");
        binder = new StudentQueryBinder();
    }


    @Override
    public void unbindService(ServiceConnection conn) {
        Log.e("LOG", "-------StudentService---unbindService--");
        super.unbindService(conn);
    }

    @Nullable
    @Override
    public IBinder onBind(Intent intent) {
        Log.e("LOG", "-------StudentService---onBind--");
        return binder;
    }


    @Override
    public void onRebind(Intent intent) {
        Log.e("LOG", "-------StudentService---onRebind--");
        super.onRebind(intent);
    }


    class StudentQueryBinder extends StudentAidl.Stub {

        @Override
        public void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat, double aDouble, String aString) throws RemoteException {

        }

        @Override
        public String getStudent(int no) throws RemoteException {
            int l = studentNo.length;
            if (l < 0) {
                no = 0;
            }
            if (no >= l) {
                no = l - 1;
            }
            return studentNo[no];
        }
    }


    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        Log.e("LOG", "-------StudentService---onStartCommand--");
        return super.onStartCommand(intent, flags, startId);
    }


    @Override
    public boolean onUnbind(Intent intent) {
        Log.e("LOG", "-------StudentService---onUnbind--");
        return super.onUnbind(intent);
    }

    @Override
    public boolean stopService(Intent name) {
        Log.e("LOG", "-------StudentService---stopService--");
        return super.stopService(name);
    }

    @Override
    public void onDestroy() {
        Log.e("LOG", "-------StudentService---onDestroy--");
        super.onDestroy();
    }
}

Service需要在Manifest中注册,注册如下:
在注册中需要设置 android:process,其规则如下
android:process=":xxx"与android:process=“XXX"不仅仅是用来定义当前进程的名字,一般情况各组件的进程名均为当前应用的包名。那有”:"开头意味着:

  • 1、进程名字是在当前进程名(即包名)下追加命名,如当前包名为com.hongliang.demo,那么第一种情况下的进程名就是com.hongliang.demo:xxx而第二种就直接以"xxx"来命名;
  • 2、":"表示当前新进程为主进程(进程名为包名)的私有子进程,其他应用的组件不可以和它跑在同一个进程中。而后者则是全局的进程,其他应用通过设置相同的ShareUID可以和它跑在同一个进程。
  • 3、另外一种方式就是android:process=“com.aidl.student.service”,直接指定不带冒号,这样在控制台中就会直接显示显示一个名字为com.aidl.student.service的进程

两种指定方式的区别:

1.以冒号设置的为私有进程: android:process为“com.hongliang.demo:xxx”。这种设置形式表示该进程为当前应用的私有进程,其他应用的组件不可以和它跑在同一个进程中
2. 没有冒号设置的为全局进程:如android:process=“com.aidl.student.service”,以小写字母开头,表示运行在一个以这个名字命名的全局进程中,其他应用通过设置相同的ShareUID可以和它跑在同一个进程。

因为我们是在同一个App中启动Service所以不需要设置 android:exported=“true”,如果是用两个App去实现service和Clien,那么service端就徐需要添加该属性

  <service
            android:name=".StudentService"
            android:process="com.aidl.student.service">
            <intent-filter>
                <action android:name="com.hongliang.demo.StudentService" />
                <category android:name="android.intent.category.DEFAULT"></category>

            </intent-filter>
        </service>

接下来Client端实现

  • Client 是以一个Activity来实现,首先使用Intent绑定Service, 绑定的时候,会需要传递一个ServiceConnection对象
  • ServiceConnectionImpl 内部类实现ServiceConnection接口,生成一个ServiceConnection对象
  • ServiceConnection对象需要实现onServiceConnectedonServiceDisconnected方法。onServiceConnected会返回一个IBinder,这个IBinder就是bindService注册后在StudentService类onBind方法里返回的
  • 然后调用会在onServiceConnected 中调用StudentAidl.Stud.asInterface(IBinder obj)方法,返回一个StudentAidl,而StudentAidl对象就是我们后面调用之前Aidl中定义的相应方法所需要的对象
public class AidlActivity extends Activity implements View.OnClickListener {
    private ServiceConnectionImpl sci;
    private StudentAidl student;
    /**
     * 发起通信
     */
    private Button mBtStart;
    /**
     * 绑定Server
     */
    private Button mBtBind;
    /**
     * 解除Server绑定
     */
    private Button mBtUnBind;


    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_aidl);
        initView();


    }



    public void initView() {
        mBtStart = (Button) findViewById(R.id.bt_start);
        mBtStart.setOnClickListener(this);
        mBtBind = (Button) findViewById(R.id.bt_bind);
        mBtBind.setOnClickListener(this);
        mBtUnBind = (Button) findViewById(R.id.bt_unBind);
        mBtUnBind.setOnClickListener(this);
    }


    @Override
    public void onClick(View v) {
        switch (v.getId()) {
            case R.id.bt_start:
                queryOnClick();
                break;
            case R.id.bt_bind:
                onBind();
                break;
            case R.id.bt_unBind:
                unbindService();
                break;
        }
    }


    /**
     * onBind() 后执行onCreate  onBind
     * 后再绑定不在再执行
     */
    private void onBind() {
        Intent intent = new Intent("com.hongliang.demo.StudentService");//Service所设置的action
        intent.setPackage("com.hongliang.demo");Service所在的package名称
        sci = new ServiceConnectionImpl();
        bindService(intent, sci, Service.BIND_AUTO_CREATE);

    }

    class ServiceConnectionImpl implements ServiceConnection {

        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            //将其转换为service实现类的对象
            student = StudentAidl.Stub.asInterface(service);
        }

        @Override
        public void onServiceDisconnected(ComponentName name) {
            student = null;
        }
    }


    /**
     * unbindService() 后执行onUnbind onDestroy
     */
    private void unbindService() {
        if (sci != null) {
            unbindService(sci);
            sci = null;
        }
    }


    public void queryOnClick() {

        String name;
        try {
            if (student == null) {
                return;
            }
            name = student.getStudent(1);
            Toast.makeText(this, "sname" + name, Toast.LENGTH_SHORT).show();
        } catch (NumberFormatException e) {
            e.printStackTrace();
        } catch (RemoteException e) {
            e.printStackTrace();
        }

    }


    @Override
    protected void onDestroy() {
        super.onDestroy();
        unbindService();
    }


分析Aidl生成的Java文件

  • IInterface 类如下,返回一个IBinder, IBinder是一个接口,Binder类实现IBinder 接口
public interface IInterface
{
    /**
     * Retrieve the Binder object associated with this interface.
     * You must use this instead of a plain cast, so that proxy objects
     * can return the correct result.
     */
    public IBinder asBinder();
}
public class Binder implements IBinder {
}

查看首先我们看整个类的大结构,我们会返现整个StudentAidl接口外层只有三个东西,

  1. StudentAidl是一个接口并继承了IInterface接口,IInterface里面有一个asBinder方法,返回一个IBinder
  2. 该接口里面有一个静态抽象类Stub,并继承了Binder类,实现了外层的StudentAidl接口,那么继承Stub类就必须实现StudentAidl类的抽象方法getStudent
  3. 系统生成的方法basicTypes和getStudent方法。

**接下来我们看看 静态抽象类Stub **
4. 该类实现了StudentAidl继承于IInterface的接口asBinder;
5. 该类同时提供了一个可以将Binder转化为StudentAidl实现类的方法,asInterface。
6. 该类继承了android.os.Binder类,重写了Binder类的onTransact方法。该方法的作用是,接收客户端发来的请求,并将请求分发至各个接口。
7. 该类中又自定义了一个静态类Proxy,实现了IMyService。该类在asInterface中被调用。
8. 该类为每个接口方法定义了一个整型的唯一标识。

再看Proxy,该类的作用是作为一个代理,将接受到的请求转发给服务器
9. 该类实现了IMyService继承自IInterface的方法asBinder
10. 该类的构造函数,以Binder作为传参,保存为成员变量。
11. 该类实现了IMyService中定义的两个接口
具体的调用时机是,在client端中返回IBinder的时候,会调用asInterface,在这个地方会在本地查询返回Interface,如果不为空且为当前接口,则为同一进程那么就返回Stub,否则Stub.Proxy,返回结果将作为Service实现类的实例

public interface StudentAidl extends android.os.IInterface {
    /**
     * Local-side IPC implementation stub class.
     */
    public static abstract class Stub extends android.os.Binder implements com.hongliang.demo.StudentAidl {
        private static final java.lang.String DESCRIPTOR = "com.hongliang.demo.StudentAidl";

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

        /**
         * Cast an IBinder object into an com.hongliang.demo.StudentAidl interface,
         * generating a proxy if needed.
         */
        public static com.hongliang.demo.StudentAidl asInterface(android.os.IBinder obj) {
            if ((obj == null)) {
                return null;
            }
            android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
            if (((iin != null) && (iin instanceof com.hongliang.demo.StudentAidl))) {
                return ((com.hongliang.demo.StudentAidl) iin);
            }
            return new com.hongliang.demo.StudentAidl.Stub.Proxy(obj);
        }

        @Override
        public android.os.IBinder asBinder() {
            return this;
        }
		//MyBinder继承了Binder类同时实现了IEthan接口中的方法。同时在onTransact中得到客户端发过来的数据,
// 并且解析,然后调用实现getAdd()和getminus();为什么要在onTransact执行方法,因为客户端拿到的是一个IBinder接口对象,
// 会调用IBinder中的Transact()方法,由于Transact()方法被IBinder的实现类重写了,
// 根据多态,客户端接口对象会调用Binder(实现类)中的Transact()方法,
// 而Binder类中的Transact()内部又会调用onTransact(),因此最终会执行到onTransact()方法中。

// 2 onTransact 根据标识调用对应的方法
        @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;
                }
                case TRANSACTION_getStudent: {
                    data.enforceInterface(DESCRIPTOR);
                    int _arg0;
                    _arg0 = data.readInt();
                    java.lang.String _result = this.getStudent(_arg0);
                    reply.writeNoException();
                    reply.writeString(_result);
                    return true;
                }
            }
            return super.onTransact(code, data, reply, flags);
        }
	//在Aidl跨进程通信,则调用`Proxy`的对应方法,根据`transact`方法将信息传递到服务端,此时客户端挂起等待响应

        private static class Proxy implements com.hongliang.demo.StudentAidl {
            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();
                }
            }

            @Override
            public java.lang.String getStudent(int no) throws android.os.RemoteException {
                android.os.Parcel _data = android.os.Parcel.obtain();
                android.os.Parcel _reply = android.os.Parcel.obtain();
                java.lang.String _result;
                try {
                    _data.writeInterfaceToken(DESCRIPTOR);
                    _data.writeInt(no);
                    mRemote.transact(Stub.TRANSACTION_getStudent, _data, _reply, 0);
                    _reply.readException();
                    _result = _reply.readString();
                } finally {
                    _reply.recycle();
                    _data.recycle();
                }
                return _result;
            }
        }

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

    /**
     * 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;

    public java.lang.String getStudent(int no) throws android.os.RemoteException;
}

在这个例子中,由于我将Service在注册的时候设置成了跨进程启动,那整个流程是什么样的呢

  1. 客户端调用bindService绑定服务时,将触发Service的onBind监听方法。该方法将调用asBinder方法,返回一个Binder对象。
  2. 客户端将通过onServiceConnected回调函数,获取到该Binder对象(以传参的形式传入)。
  3. 客户端获取到Binder对象后,可调用stub.asInterface方法,将其转换为service实现类的对象。
  4. 在asInterface方法中,将判断service与当前进程,是否在同一进程中。若是,则返回stub本身,否则返回stub.proxy。返回结果将作为Service实现类的实例。
  5. 在通过Service实现类的实例调用接口方法时,若为同一进程,则直接调用方法本身。若为跨进程,则调用stub.proxy的对应接口方法,通过Transact方法将信息传送到服务端。此时,客户端将挂起,等待结果返回。
  6. 服务端接收到信息,通过onTransact()方法,根据方法的唯一标识,将信息转发至各对应方法。
  7. 信息处理完成后,再由服务端onTransact返回结果。

在整个分析中还有没有实际去分析的和理解的

  • bindService注册的时候是怎么注册的,又怎么回传一个IBinder对象的
  • 跨进程中stub.proxy中调用接口方法mRemote.transact的时候是这么传递到服务端的
  • server处理后怎么回传到onTransact又怎么返回到client的
  • Adil中使用了代理模式,代理模式的理解,为什么Aidl使用代理模式
    server告诉你我提供哪些方法供你使用,可是server在onBinder()方法返回一个对象的时候,返回的是一个IBinder,这个对象并不能直接调用server提供的方法,所以Aidl引入了代理模式,重新封装的Proxy代理了server所有的方法。当在多进程调用的时候,那么Proxy中原先保存的原始IBinder就可以调用mRemote.transact方法,经过底层Binder的实现就将消息传递到Server,Server根据方法的code处理相应的逻辑,然后再经过底层onTransact将数据reply并写入到Bindle,Bindle返回并唤醒Client

后面有更加深入的理解再添加到之中来!

从android aidl理解Proxy/stub模式
Binder与AIDL的关系
Android应用程序绑定服务(bindService)的过程源代码分析
IPC(一)利用纯Binder通信(非aidl)
Binder学习指南
Android Binder之应用层总结与分析

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值