Android IPC 之 AIDL(一)

来源:互联网 发布:遗传算法 排课系统 编辑:程序博客网 时间:2024/05/16 05:50

IPC是Inter-Process Communication的缩写,即跨进程通信。Android中跨进程通信有多种方式,如文件共享、使用ContentProviderBroadcast、和Socket等。比较复杂的情况下,常用的两种方式为MessengerAIDL,而Messenger的底层实现又是AIDL。

首先不看别的,先来看一下AIDL是如何使用的。

假设我们现在有一个两数相加的任务,客户端没办法完成(别问我它为什么完不成==,咱举栗子简单点哈~),需要将任务交给另一个进程中的服务端完成,再从服务端获取到该任务的结果。

我们首先如下图方式创建一个AIDL接口:

新建文件,选择AIDL

Android Studio会自动为它生成一个路径,如下图:

这里写图片描述

在该文件中声明一个接口以及一个我们想让服务端实现的接口方法。如下:

package com.vera.aidltest;interface IMyAdd {   int myAdd(int num_a,int num_b);}

注意,并不是所有数据类型都能在AIDL文件中使用,AIDL文件只支持以下几种数据类型:

  • Java 中的基本数据类型
  • String 和CharSequence
  • List 和 Map ,且List和Map 对象的元素必须是AIDL支持的数据类型
  • AIDL 自动生成的接口 ,需要导入(import,即使同处于一个包中)
  • 实现android.os.Parcelable 接口的类的对象. 需要导入(import,即使同处于一个包中),且必须新建一个与其同名的AIDL文件,并在文件中声明该类为Parcelable

在接口定义好后,系统将为我们生成一个Java文件,AS下是在app\build\generated\source\aidl\debug目录下,生成的代码如下:

package com.vera.aidltest;public interface IMyAdd extends android.os.IInterface{/** Local-side IPC implementation stub class. */public static abstract class Stub extends android.os.Binder implements com.vera.aidltest.IMyAdd{private static final java.lang.String DESCRIPTOR = "com.vera.aidltest.IMyAdd";/** Construct the stub at attach it to the interface. */public Stub(){this.attachInterface(this, DESCRIPTOR);}/** * Cast an IBinder object into an com.vera.aidltest.IMyAdd interface, * generating a proxy if needed. */public static com.vera.aidltest.IMyAdd asInterface(android.os.IBinder obj){if ((obj==null)) {return null;}android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);if (((iin!=null)&&(iin instanceof com.vera.aidltest.IMyAdd))) {return ((com.vera.aidltest.IMyAdd)iin);}return new com.vera.aidltest.IMyAdd.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_myAdd:{data.enforceInterface(DESCRIPTOR);int _arg0;_arg0 = data.readInt();int _arg1;_arg1 = data.readInt();int _result = this.myAdd(_arg0, _arg1);reply.writeNoException();reply.writeInt(_result);return true;}}return super.onTransact(code, data, reply, flags);}private static class Proxy implements com.vera.aidltest.IMyAdd{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;}@Override public int myAdd(int num_a, int num_b) 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(num_a);_data.writeInt(num_b);mRemote.transact(Stub.TRANSACTION_myAdd, _data, _reply, 0);_reply.readException();_result = _reply.readInt();}finally {_reply.recycle();_data.recycle();}return _result;}}static final int TRANSACTION_myAdd = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);}public int myAdd(int num_a, int num_b) throws android.os.RemoteException;}

代码很长很凌乱的样子……嗯,本节我们先不看它,只需要知道它是根据AIDL文件生成的一个接口IMyAdd,包含一个继承自Binder的静态内部抽象类Stub(咦,这么连起来说总有哪里怪怪的……),并且声明了myAdd()方法。为什么是抽象类呢,因为它实现了IMyAdd接口却并没有真正实现,那放到哪里实现呢?当然是我们的服务端咯。

接下来,我们可以就可以来写客户端和服务端的代码了,那么,我们这里的客户端和服务端指的是什么呢?就本例来说,它们分别是一个Activity和一个Service,这里我们将它们放在了同一个应用中,只不过通过某种方法使其运行在不同的进程。更多情况下它们并不运行在同一个应用中,这时候我们需要将整个aidl文件夹的内容复制一份,这是因为客户端和服务端的AIDL包结构需要保持一致,否则将会出现反序列化不成功的结果,那么跨进程通信将无法进行。

那么我们现在开始写服务端的代码,新建一个Service名为ServerService如下:

package com.vera.aidltest;import android.app.Service;import android.content.Intent;import android.os.Binder;import android.os.IBinder;import android.os.RemoteException;import android.util.Log;public class ServerService extends Service {   private Binder mBinder=new IMyAdd.Stub(){       @Override       public int myAdd(int num_a, int num_b) throws RemoteException {           int result=num_a+num_b;           Log.d("ServerService","the result of "+num_a+" and "+num_b+" is "+result);           return result;       }   };    @Override    public IBinder onBind(Intent intent) {        return mBinder;    }}

ServerService很简单,它只是创建了一个Binder对象并在onBinder()方法中将其返回。该Binder对象就是我们实现了接口方法的Stub对象。好啦,现在我们可以在myAdd()方法中愉快地进行我们的操作啦。在这里我们只是得到num_a和num_b的和并将其打印,最后再返回结果。

另外,我们需要将该Service设置在一个独立的进程中,不然还怎么玩跨进程通信~
更改AndroidManifest.xml如下:

<service            android:name=".ServerService"            android:process=":remote"></service>

好了,服务端的创建已经完成啦,我们现在来看客户端。客户端要做些什么呢?,我们来看代码:

package com.vera.aidltest;import android.content.ComponentName;import android.content.Context;import android.content.Intent;import android.content.ServiceConnection;import android.os.Bundle;import android.os.IBinder;import android.os.RemoteException;import android.support.v7.app.AppCompatActivity;import android.util.Log;import android.view.View;import android.widget.Button;public class ClientActivity extends AppCompatActivity {    Button mButton;    private ServiceConnection myConnection=new ServiceConnection() {        @Override        public void onServiceConnected(ComponentName componentName, IBinder iBinder) {            IMyAdd mMyAdd=IMyAdd.Stub.asInterface(iBinder);            try {                int result=mMyAdd.myAdd(1,2);                Log.d("ClientActivity","get the result is "+result);            } catch (RemoteException e) {                e.printStackTrace();            }        }        @Override        public void onServiceDisconnected(ComponentName componentName) {        }    };    @Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.activity_client);        mButton=(Button)findViewById(R.id.activity_client_mbutton);        mButton.setOnClickListener(new View.OnClickListener() {            @Override            public void onClick(View view) {                Intent intent=new Intent(ClientActivity.this,ServerService.class);                bindService(intent,myConnection, Context.BIND_AUTO_CREATE);            }        });    }    @Override    protected void onDestroy(){        unbindService(myConnection);        super.onDestroy();    }}

首先,我们创建了一个ServiceConnection的匿名类并实例化一个对象,然后,我们又设置了在Button点击之后通过bindService()方法绑定服务。bindService()方法接收三个参数,第一个是在这之前我们创建的Intent对象,第二个是ServiceConnection的实例对象,第三个参数是一个标志位,BIND_AUTO_CREATE表示在Activity和Service进行绑定后,服务将自动创建。然后我们重写了onDestroy()方法,将Activity和Service解除绑定。

重点是在创建的ServiceConnection匿名类里!在这个类里我们重写了onServiceConnected()方法和onServiceDisconnected()方法,这两个方法分别会在Activity与Service成功绑定和解除绑定的时候调用。在onServiceConnected()方法里,我们调用了Stub()的asInterface()方法,该方法返回一个Binder代理对象,并向上转型成为客户端接受的AIDL接口类型的对象!

好啦,拿到了这个对象,现在只差一步调用方法的事啦,我们来试一试1+2等于多少叭~

运行程序,在点击Button之后,查看日志打印信息,如下图:

这里写图片描述

这里写图片描述

在com.vera.aidltest进程中,客户端打印出得到的结果为3,在com.vera.aidltest:remote进程中,服务端打印出1+2等于3(我英语不好……),通信成功啦!

嗯好,那我们来总结一下,具体的步骤吧:

  • 首先建立一个AIDL文件,在其中定义一个接口,接口中应包含我们希望服务端实现的方法的声明。
  • 其次,在服务端中将实现好方法的Stub对象通过onBind()方法返回
  • 最后,在客户端中将服务绑定,并重写ServiceConnection类中的方法,在其中获得Binder对象,调用服务端的接口方法。

这其中我们屏蔽了很多细节,只谈了使用方法,下一节婷子会把更具体的部分再贴出来,第一篇技术博客,有什么不对的地方还请留言哦,么么哒~

3 0