天天看点

Binder应用层架构--AIDL从自动生成到手动编写AIDL 的自动生成但是(Binder架构理论):手动编写AIDL:

说到Binder,它是Android系统进程间通信的一种方式,他的结构就是C/S结构的。(C/S即Client/Server,客户端与服务端结构,不懂的可以参考Scoket)。这里就从浅层(应用的角度)来写一下Binder的架构。

首先说一下,AIDL的简单应用,在分析一下Binder的应用层架构。

最常见的AIDL的使用就是Service的跨进程通信了,这里就写一个Activity和Service的跨进程通信吧。

AIDL 的自动生成

首先在AS上点击File -> New -> AIDL -> AIDL File ,出现对话框输入,然后输入名称,就会在src/main/aidl/下找到新建的AIDL文件,对该文件进行修改,使其符合我们的要求,如下:

// MyAIDL.aidl
package com.demo.ln.eaxm;

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

interface MyAIDL {
    //这就是进行简单的加法运算
    int add(int a,int b);
}
           

然后 点击sync project,编辑一下项目,就可以在app/generated/source/aidl/debug/aidl里面发现由aidl文件生成的java文件。这个文件里边给人的感觉好复杂,什么乱七八糟的(确实乱七八糟,后面我们手动写的时候会把他分开,就比较清晰了)。生成的文件这里就不写了。

然后我们新建一个Service,在里边调用ALDL生成的Java类,并获取IBinder对象,进行返回。

Service代码如下:

public class MyService extends Service {
    public final static String TAG = "MyService";
    IBinder binder = new MyAIDL.Stub() {
        @Override
        public int add(int a, int b) throws RemoteException {
            return a + b;
        }
    };

    @Override
    public void onCreate() {
        super.onCreate();
        Log.i(TAG, "onCreate");
    }

    @Nullable
    @Override
    public IBinder onBind(Intent intent) {
        return binder;
    }
}
           

在这里可以发现在IMyInterface.Stub()的内部我们重写add(int a, int b) 方法,没错这就是我们 aidl文件中定义的接口。

因为我们希望看到的是跨进程通信,所以我们把MyService定义成新启动一个进程,在AndroidManifest.xml注册一下该Service:

<service android:name=".MyService"
            android:process=":remote"/>
<!--android:process=":remote" 定义该service运行在   包名:remote 进程中-->
           

接着就是activity了:

public class SecondActivity extends AppCompatActivity {
    private MyAIDL myAIDL;
    private ServiceConnection connection = new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            myAIDL = MyAIDL.Stub.asInterface(service);
            try {
                int a = myAIDL.add(, );
                Toast.makeText(SecondActivity.this, "这就是我们从Service获取的结果:" + a, Toast.LENGTH_SHORT).show();
            } catch (RemoteException e) {
                e.printStackTrace();
            }
        }

        @Override
        public void onServiceDisconnected(ComponentName name) {

        }
    };

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


        Intent intent = new Intent(this, MyService.class);
        bindService(intent, connection, Context.BIND_AUTO_CREATE);
    }
}
           

然后看运行结果:

Binder应用层架构--AIDL从自动生成到手动编写AIDL 的自动生成但是(Binder架构理论):手动编写AIDL:
Binder应用层架构--AIDL从自动生成到手动编写AIDL 的自动生成但是(Binder架构理论):手动编写AIDL:

从这结果里可以看到activity和service不在同一个进程里。

到这里AIDL的自动生成已经结束了,现在我们来分析一下,因为Binder架构是C/S结构的,我们可以把activity看成客户端(Client),把MyService看成服务端(Server)。当activity调用bindService()方法,可以把它看成请求连接服务器。ServiceConnection中的方法就是链接的结果。当ServiceConnection中的方法onServiceConnected()就说明连接成功,onServiceConnected()中的参数IBinder service就可以看做是MyService返回的,在MyService中只有一个地方有返回,就是onBind()方法。

简单来说就是,MyService将处理的操作、结果封装成对象,返回给activity,然后activity来调用。这样就比较好理解了。

但是(Binder架构理论):

这只是流程上的理解,它的实现要复杂的多,不然ALDL生成的Java类也就没有那么复杂了。这里就讲到了Binder的架构,Binder的架构图如下:

Binder应用层架构--AIDL从自动生成到手动编写AIDL 的自动生成但是(Binder架构理论):手动编写AIDL:

Binder架构从组件视角来说,包含Client、Server、ServiceManager以及binder驱动四部分。图中的虚线只是一种逻辑的关系并没有直接的代码关系,我们又只是提供应用层的Binder架构,ServiceManager又是管理binder驱动的,所以我们在某些方面可以把ServiceManager和binder驱动看成是一起的。

现在我们就分析服务端接口、Binder驱动、客户端接口三个模块。

服务端接口:一个Binder服务端实际上就是一个Binder类的对象。它接下来会接收Binder驱动发送的消息,收到消息后,会执行到Binder对象中的onTransact()函数,并按照该函数的参数执行不同的服务代码。

Binder驱动:任意一个服务端Binder对象被创建时,同时会在Binder驱动中创建一个mRemote对象,该对象的类型也是Binder类。客户端要访问远程服务时,都是通过mRemote对象,它其实是服务端Binder的代理。

客户端:客户端要想访问远程服务,必须获取远程服务在Binder对象中对应的mRemote引用,这就是activity中的ServiceConnection.onServiceConnected()的参数IBinder service。

这个地方比较难,可以这样理解:activity (下边表示为A)和MyService的通过程信,就是MyService把处理问题的办法给了Binder(下边表示为B),让B和A协商解决问题的过程。

A要和B通信来解决问题,A和B通信要统筹一下吧。谁来统筹?怎么统筹的?A和B的通信是通过Binder驱动来统筹。但是Binder驱动没有让A和B见面,而是找了另外一个IBinder(下边表示为C)做为B的代理来和A谈,A和C见面了就开始谈,但是B没有将处理问题的办法告诉C,A和C就瞎谈,解决不了问题,一看没办法C就说A你把你的数据给我,我带给B,B他有处理办法,他处理完了我再把处理的结果给你。A一听,这个方法不错,就这样办吧。这就是A和B通信的过程。

手动编写AIDL:

根据这个过程,我们动手来编写一下AIDL:

第一步:

定义解决问题的办法(Service要有解决问题的办法,这个我们自己来定义。)

系统定义了这个办法叫IInterface,因为Binder有个方法binder.attachIInterface(plus,”PLUS”);来携带处理问题的办法。

定义的办法:Iplus.java

public interface Iplus extends IInterface {
    int add(int a,int b) throws RemoteException;
}
           

第二步:

定义Binder。(找一个能带着解决方法的人B,这个人要能接收C的信息,并能给C返回信息。找不到就自己造一个)

Stub.java

public class Stub extends Binder {
    private final static String DESCRIPTER = "AidlService";

    //这个方法的参数对应PlusProxy类add()方法中binder.transact(0, data, reply, 0)的参数。
    //code对应第一个0,用来选择要执行的代码,用switch来筛选。
    //data对应data,表示客户端传过来的数据。
    //reply对应reply,表示要给客户端传回的数据。
    //flags对应第二个0,同过该值判断是否要返回数据给客户端。这里我比较懒,用的return true。表示要给客户端返回数据。
    @Override
    protected boolean onTransact(int code, Parcel data, Parcel reply, int flags) throws RemoteException {
        switch (code) {
            case :
                //该方法对应PlusProxy类add()方法中data.writeInterfaceToken( DESCRIPTER)这两个方法的参数是一致的。可以理解为
//一个允许读,一个允许写。
                data.enforceInterface(DESCRIPTER);

                int a, b;

                //读取代理传过来的信息,这个地方读出数据的顺序就是该方法对应PlusProxy类add()方法中data.writeInt写入的顺序。
                //因为是先写的a,后写的b,所以这里先读的a,后读的b.
                a = data.readInt();
                b = data.readInt();

                //用携带的方法处理数据,
                //this.queryLocalInterface(DESCRIPTER)就是找出携带的方法
                //和AidlService类onBind中stub.attachInterface(iplus, DESCRIPTER)对应
                //stub.attachInterface可以看成是把方法存起来
                //this.queryLocalInterface就是把方法找出来
                //其中的参数DESCRIPTER可以看成key值
                int result = ((Iplus) this.queryLocalInterface(DESCRIPTER)).add(a, b);

                //和PlusProxy类add()方法中reply.readException()相对应,看字面意思,一个读一个写。
                reply.writeNoException();

                //将处理的结果返回给代理
                //和PlusProxy类add()方法中result = reply.readInt();对应一个写一个读。
                reply.writeInt(result);
                Log.e("aaa", "result:" + result);
                break;
        }
        //表示将数据返回给客户端
        return true;
    }
}
           

第三步:

将处理问题的方法教给自定义的Binder,并让他去通信。也就是Service.

AidlService.java

public class AidlService extends Service {
    private final static String DESCRIPTER = "AidlService";

    Binder stub = new Stub();
    Iplus iplus = new Iplus() {
        @Override
        public int add(int a, int b) {
            Log.e("aaa", "add");
            return a + b;
        }

        @Override
        public IBinder asBinder() {
            return stub;
        }
    };

    @Nullable
    @Override
    public IBinder onBind(Intent intent) {
        Log.e("aaa", "onBind");
        //binder携带方法,DESCRIPTER是key值
        stub.attachInterface(iplus, DESCRIPTER);
        return stub;
    }
}
           

第四步:

在activity中找到代理,能向他发数据,也能向他接收数据。这里有个问题:怎么没有去找一个或新建一个IBinder代理呢?因为当Service执行onBind的时候,Binder驱动就已经找好代理了。(你也可以去自己新建一个代理,那你在去底层看源码,我的底层是渣,所以就不在这里误导别人了)。

MainActivity.java

public class MainActivity extends AppCompatActivity {

    private ServiceConnection connection = new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {

            //这个IBinder service就是服务端Binder的“代理”。
            PlusProxy proxy = new PlusProxy(service);

            //接收和发送数据的封装方法
            int a = proxy.add(, );

            Toast.makeText(MainActivity.this, a + "", Toast.LENGTH_SHORT).show();

        }

        @Override
        public void onServiceDisconnected(ComponentName name) {

        }
    };

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        startAndBindService();
    }

    private void startAndBindService() {
        Intent intent = new Intent(this, AidlService.class);
        startService(intent);
        bindService(intent, connection, BIND_AUTO_CREATE);
    }
}
           

接收和发送数据的封装方法:PlusProxy .java

public class PlusProxy implements Iplus {
    private final static String DESCRIPTER = "AidlService";
    private IBinder binder;

    public PlusProxy(IBinder binder) {
        this.binder = binder;
    }

    @Override
    public int add(int a, int b) {
        Parcel data = Parcel.obtain();
        Parcel reply = Parcel.obtain();
        int result = ;
        try {
            //和Stub类onTransact方法中的data.enforceInterface(DESCRIPTER)对应,参数一直,可以看成一个允许读,一个允许写。
            data.writeInterfaceToken(DESCRIPTER);

            //把数据交给代理,这个地方是按什么顺序写入的,在Stub类中就按什么顺序读出。
            //这里先写的a,后写的b,读出的时候就是先读出a,后读出b。
            data.writeInt(a);
            data.writeInt(b);

            //这个方法的参数对应Stub类onTransact(int code, Parcel data, Parcel reply, int flags)方法的参数。
            //第一个0对应code,用来选择要执行的代码,在Stub类中用switch来筛选。
            //data对应data,表示传给服务端的数据。
            //reply对应reply,表示服务端要返回的数据。
            //第二个0对应flags,通过该值来判断是否要服务端返回数据。我在Stub类中写的return true,该值就没用了,数据一定返回。
            //这个方法是告诉“代理”你可以去通信了
            binder.transact(, data, reply, );

            //和Stub类onTransact方法中reply.writeNoException()相对应,一个读一个写。
            reply.readException();

            //接收代理返回的数据
            result = reply.readInt();
        } catch (RemoteException e) {
            e.printStackTrace();
        } finally {
            data.recycle();
            reply.recycle();
        }

        return result;
    }

    @Override
    public IBinder asBinder() {
        return binder;
    }
}
           

这里有人要问你为什么要继承处理问题方法的接口Iplus呢?必须吗?

不必须,因为他们之间有着一个对应的关系。只在activity所在的进程中看PlusProxy 这个类其实就是处理问题的办法,当然你也可以不继承Iplus,但add()方法中的代码必须运行。

到这里就结束了,不知道你是否明白了。这里可以将手动写的代码和AIDL自动生成的代码作比较,其实结构是一样的。该有的类都有了,只不过AIDL自动生成的代码来了一个大杂烩,把上边的类写到了一个类里边,看上去乱七八糟的。

注意:我源码中的注释没有很详细,和这篇文章结合起来看。

在这里我参考的文章:

【简书】Android的进阶学习(四)–AIDL的使用与理解

【SegmentFault】彻底理解ANDROID BINDER通信架构

【简书】可能是讲解Binder机制最好的文章(文章名有点不要脸)

手动编写的源码