Android开发之进程间通信AIDL的探究和学习

来源:互联网 发布:蔡琴 杨德昌 知乎 编辑:程序博客网 时间:2024/05/06 03:01

转载请注明出处:http://blog.csdn.net/li0978/article/details/52075558

AIDL (Android Interface Definition Language) 顾名思义就是android接口定义语言,用于生成可以在Android设备上两个进程之间进行进程间通信(interprocess communication, IPC)的代码。

我们知道android应用程序语言的许多思想来自java,而且又基于linux内核,android系统的中每一个应用程序都是一个虚拟机,android在定义之初便规定进程内的线程之间是可以通信的,而进程与进程之间是封闭的,这样做的目的是为了保证每个应用程序内部数据的稳定性和安全性,也不至于一个程序挂掉而另外一个程序也挂掉。但是非要程序与程序之间进行通信怎么办呢,这种需求又很常见,因此google便定义了AIDL这种语言规范,用来解决这个问题。


Note: Using AIDL is necessary only if you allow clients from different applications to access your service for IPC and want to handle multithreading in your service. If you do not need to perform concurrent IPC across different applications, you should create your interface by implementing a Binder or, if you want to perform IPC, but do not need to handle multithreading, implement your interface using a Messenger. Regardless, be sure that you understand Bound Services before implementing an AIDL.

以上英文是官方文档,我们根据文档说明可以得到以下信息:

1.条件:有IPC、有多线程、有多个应用程序。方式:AIDL。

2.条件:没有IPC、没有多线程、有多个应用程序。方式:Binder。

3.条件:有IPC、没有多线程。方式:Messenger。

补充:如果使用AIDL,在此之前一定要理解服务,因为一般是在服务中实现aidl中的方法的。

所以针对进程间通信,我们要视情况而取合适的方式去解决。这样才会不耽误功夫。

下面我就以一个计算器的例子来看AIDL是如何使用的,在这里我要建两个应用程序。一个作为客户端,一个作为服务端。客户端用于显示输入的数据和结果,服务端用于数据的计算。


1.创建服务端,并建立添加aidl文件。androidstudio与eclipse建立aidl稍有不同,eclipse中sdk中没有集成相关快速建立aidl文件的插件,需要我们手动新建aidl包,手动添加aidl文件,androidstudio中可直接在相应model右键菜单新建文件下寻找到创建aidl文件。

// IMyAidlInterface.aidlpackage com.example.aidltest;// Declare any non-default types here with import statementsinterface IMyAidlInterface {    /**     * 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格式中有一个basicTypes()方法,这个方法只是告诉你aidl支持哪些参数传入,除了支持这几种类型外,还支持序列化parcelable的传递。另外说明一下,我们知道aidl从表面上看是只是一个接口文件,后缀名是.aidl,这在编译的过程中adt是不认的,只有编译成.java的java文件才能使用。eclipse中支持自动编译。但是androidstudio中是不支持自动编译的,所以我们要手动编译一下(Rebuild)。

// IMyAidlInterface.aidlpackage com.example.aidltest;// Declare any non-default types here with import statementsinterface IMyAidlInterface {   //计算两个int数值的和;   int add(int num1,int num2);}
编译过后,我们就开一个service,在service中去实现aidl中的方法,别忘了配置文件中注册此服务。

public class IMyService extends Service {    /**     * 当客户端绑定到该服务的时候,执行此方法。     * @param intent     * @return     */    @Override    public IBinder onBind(Intent intent) {        return iBinder;    }    private IBinder iBinder = new IMyAidlInterface.Stub() {        @Override        public int add(int num1, int num2) throws RemoteException {            Log.e("服务端","收到了客户端的请求,输入的参数是:"+num1+"和"+num2);            return num1+num2;        }    };}
2.创建客户端
因为AIDL作为客户端和服务端通信的中间桥梁,所以我们必须保证客户端和服务端的AIDL一模一样,所以我在创建客户端后将服务端的AIDL所在的包和类全部拷贝到客户端来。

界面这里就不提了,放张图:

客户端的逻辑处理:

a.先绑定服务端的service。

b.拿到服务端的service。

c.调用服务端的service,并获取结果,显示结果。

d.客户端退出时结束服务端service的绑定。

public class MainActivity extends AppCompatActivity {    private EditText num1_et,num2_et;    private Button result_bt;    private TextView result_tv;    IMyAidlInterface iMyAidlInterface;    private ServiceConnection conn = new ServiceConnection() {        //绑定远程服务的时候        @Override        public void onServiceConnected(ComponentName name, IBinder service) {            //拿到了远程的服务            iMyAidlInterface = IMyAidlInterface.Stub.asInterface(service);        }        //断开远程服务的时候        @Override        public void onServiceDisconnected(ComponentName name) {            //回收资源            iMyAidlInterface = null;        }    };    @Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.activity_main);        initView();        result_bt.setOnClickListener(new View.OnClickListener() {            @Override            public void onClick(View v) {                String num1 = num1_et.getText().toString().trim();                String num2 = num2_et.getText().toString().trim();                if (TextUtils.isEmpty(num1)||TextUtils.isEmpty(num2)){                    Toast.makeText(MainActivity.this,"参数不能为空",Toast.LENGTH_SHORT).show();                }else {                    try {                        //调用远程服务获取结果                        int res = iMyAidlInterface.add(Integer.valueOf(num1),Integer.valueOf(num2));                        result_tv.setText(res+"");                    } catch (RemoteException e) {                        e.printStackTrace();                        Toast.makeText(MainActivity.this,"获取结果失败,检查参数格式是否正确。",Toast.LENGTH_SHORT).show();                    }                }            }        });        //绑定服务端的service        bindService();    }    private void initView() {        num1_et = (EditText) findViewById(R.id.num1_et);        num2_et = (EditText) findViewById(R.id.num2_et);        result_bt = (Button) findViewById(R.id.result_bt);        result_tv = (TextView) findViewById(R.id.result_tv);    }    private void bindService() {        //绑定服务端的服务,由于服务端和客户端不在一个进程,所以我们需要直接指定服务端的包名和类名,注意类名要写全。        Intent intent = new Intent();        intent.setComponent(new ComponentName("com.example.aidltest","com.example.aidltest.IMyService"));        bindService(intent,conn, Context.BIND_AUTO_CREATE);    }    @Override    protected void onDestroy() {        super.onDestroy();        //断开服务端的service        unbindService(conn);    }}
3.验证AIDL,查看进程间通信是否成功。

先后开启服务端和客户端,输入相关参数。

我们服务端控制台中能够获取服务端service接受到的两个参数:


客户端界面展示结果:



分析AIDL文件编译后的java文件(本质):

package com.example.aidltest;// Declare any non-default types here with import statementspublic interface IMyAidlInterface extends android.os.IInterface{/** Local-side IPC implementation stub class. */public static abstract class Stub extends android.os.Binder implements com.example.aidltest.IMyAidlInterface{private static final java.lang.String DESCRIPTOR = "com.example.aidltest.IMyAidlInterface";/** Construct the stub at attach it to the interface. */public Stub(){this.attachInterface(this, DESCRIPTOR);}/** * Cast an IBinder object into an com.example.aidltest.IMyAidlInterface interface, * generating a proxy if needed. */public static com.example.aidltest.IMyAidlInterface asInterface(android.os.IBinder obj){if ((obj==null)) {return null;}android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);if (((iin!=null)&&(iin instanceof com.example.aidltest.IMyAidlInterface))) {return ((com.example.aidltest.IMyAidlInterface)iin);}return new com.example.aidltest.IMyAidlInterface.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_add:{data.enforceInterface(DESCRIPTOR);int _arg0;_arg0 = data.readInt();int _arg1;_arg1 = data.readInt();int _result = this.add(_arg0, _arg1);reply.writeNoException();reply.writeInt(_result);return true;}}return super.onTransact(code, data, reply, flags);}private static class Proxy implements com.example.aidltest.IMyAidlInterface{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;}//计算两个int数值的和;@Override public int add(int num1, int num2) throws android.os.RemoteException{android.os.Parcel _data = android.os.Parcel.obtain();android.os.Parcel _reply = android.os.Parcel.obtain();int _result;try {_data.writeInterfaceToken(DESCRIPTOR);_data.writeInt(num1);_data.writeInt(num2);mRemote.transact(Stub.TRANSACTION_add, _data, _reply, 0);_reply.readException();_result = _reply.readInt();}finally {_reply.recycle();_data.recycle();}return _result;}}static final int TRANSACTION_add = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);}//计算两个int数值的和;public int add(int num1, int num2) throws android.os.RemoteException;}
AIDL文件从表面上看是一个接口,编译过后形成java文件还是一个接口。这个文件看起来很繁杂,我们将其相关代码合并一下,从整体来看就一个抽象类Stub和一个将要实现的方法add。这个add两个作用:一个就是在服务端service里面进行实现,另一个就是客户端进行传值。接着看抽象类Stub,继承于系统的Binder却又实现了aidl接口中方法,其构造方法就是客户端绑定服务端service时返回的Binder。内部asInterface()方法目的就是客户端根据上一步返回的Bandler从而拿到服务端的service(通过内部类Proxy获取)。接下来onTransact()方法目的就是服务端service实现add()方法是对参数的限制和结果接收。


demo下载链接:http://download.csdn.net/detail/li0978/9591199











0 0
原创粉丝点击