Android进程间通信(二):AIDL

来源:互联网 发布:mysql必知必会 pdf 编辑:程序博客网 时间:2024/05/17 04:33

AIDL (Android Interface Definition Language)即Android接口定义语言。若需多线程同时处理其他应用进程的请求才有必要使用AIDL。如果不需多线程交互,则可使用Messenger(请看《Android进程间通信(一):Messenger》);而如果不需跨进程通信,使用Binder即可。
AIDL接口通常与Service联合使用。客户端通过bindService()与服务器建立连接。每当客户端调用服务器的AIDL接口时,系统就会从服务器进程的线程池中派发一个新线程进行处理,因此AIDL接口的实现必须做到线程安全。注意检查客户端传过来的参数是否合法。服务器端抛出的异常不会发回给客户端。

一、定义AIDL接口

1、创建.aidl文件

在Android Studio中创建.aidl文件:点击Android Studio最左侧的Project竖选项卡,然后在其展栏的左上角点选Project,接着在app/src/main/aidl/目录(若aidl目录不存在则新建)上右键,New > AIDL > AIDL File,在最后打开的对话框输入.aidl文件名并按Finish按钮即可。.aidl文件路径示例:app/src/main/aidl/*com/company/app*/IRemoteService.aidl。创建或修改好文件后,点击Android Studio顶部工具栏的Tools > Android > Sync Project with Gradle Files,就会在app/build/generated/source/aidl/debug/目录下生成或更新*com/company/app*/IRemoteService.java文件。

如果客户端与服务器处在不同的应用中,那么客户端也需要拷贝一份.aidl文件以便生成相应的.java文件。

.aidl文件中只能定义一个接口及其若干个方法。

AIDL默认支持的数据类型:
Java语言的基本类型,如int、long、char、boolean、String、CharSequence等等;
还有List 、Map。
List、Map的元素数据类型为上述列表中的类型,或者其他AIDL生成的接口,或者可打包(parcelable)类型。List可以当泛型类使用(例如List),而Map则不支持。
其他类型需要import(具体请看下文的第二节:在进程间传递对象)。

所有非基本类型在作为参数时需要有一个方向标签(in、out、inout)来表明数据的流向。跨进程传递参数的过程包括数据序列化、传输、接收和反序列化等。方向标签有助于系统优化数据传输过程,提高效率。

           ———— in ———— 》远程客户端                         本地服务器          《———— out ———— // inout是最耗费资源的:系统需要先把客户端的实参对象序列化,// 传到服务器,然后反序列化成形参对象供服务器使用。// 接口方法调用完返回时,系统又需要把服务器的形参对象序列化,// 传到客户端,再反序列化后修改客户端的实参对象。// 若是在同一个进程中,修改形参对象就能直接影响实参对象,// 就像C/C++中的指针那样。void charsToUpper(inout char[] lowerChars);

AIDL有一个关键字oneway,远程客户端调用其修饰的方法时,不阻塞等待结果而是立即返回;本地调用则是同步等待(如同未修饰一样)。它所修饰的方法返回值必须是void,否则生成的.java文件会报错。

// IRemoteService.aidlpackage com.company.app;// 这个oneway修饰接口中所有方法oneway interface IRemoteService{    // 这个oneway只修饰此方法    oneway void doSomething();}

2、实现接口

自动生成的IRemoteService.java文件有一个抽象子类:Stub,它继承了Binder类并实现了.aidl文件中的接口。

private final IRemoteService.Stub mBinder = new IRemoteService.Stub() {    public void doSomething() {        // Do something here    }};

3、暴露接口给客户端

// 服务器的Service类public class RemoteService extends Service {    // 当客户端调用bindService()时,系统调用此方法。    // 客户端调用的详情请看下文的第三节:跨进程调用一个方法。    @Override    public IBinder onBind(Intent intent) {        // Return the interface        return mBinder;    }    private final IRemoteService.Stub mBinder = new IRemoteService.Stub() {        public void doSomething() {            // Do something here        }    };    ...}

二、在进程间传递对象

如果需要跨进程传递一个对象,那么它的类必须做到以下几点,使得Android在跨进程传递数据时可以分解(decompose)、重组(marshalled)它:
1、实现Parcelable接口;
2、实现writeToParcel()方法,它负责把类的状态保存到Parcel中;
3、在类内创建一个名为CREATOR的静态成员,它是一个实现Parcelable.Creator接口的对象;
4、创建一个.aidl文件来声明这个可打包(parcelable)的类。
以下为Android的Rect类示例:

// Rect.aidlpackage android.graphics;// 定义Rect类,以便AIDL能找到它并知道它实现了可打包协议。parcelable Rect;

此时.java文件需要自己实现:

// Rect.javapackage android.graphics;import android.os.Parcel;import android.os.Parcelable;public final class Rect implements Parcelable {    public int left;    public int top;    public int right;    public int bottom;    public static final Parcelable.Creator<Rect> CREATOR = newParcelable.Creator<Rect>() {        public Rect createFromParcel(Parcel in) {            return new Rect(in);        }        public Rect[] newArray(int size) {            return new Rect[size];        }    };    public Rect() {    }    private Rect(Parcel in) {        readFromParcel(in);    }    @Override    public void writeToParcel(Parcel out, int flags) {        out.writeInt(left);        out.writeInt(top);        out.writeInt(right);        out.writeInt(bottom);    }    public void readFromParcel(Parcel in) {        left = in.readInt();        top = in.readInt();        right = in.readInt();        bottom = in.readInt();    }    @Override    public int describeContents() {        return 0;    }    ...}

然后就可以在.aidl文件中使用这个类:

```// IAnotherRemoteService.aidlpackage com.company.app;import android.graphics.Rect;interface IAnotherRemoteService{    void setRect(in Rect rect);}

三、跨进程调用一个方法

1、包含.aidl文件到上述路径(请看上文第一节《定义AIDL接口》的第1小节《创建.aidl文件》);
2、定义一个IBinder接口的实例;
3、实现ServiceConnection;
4、调用Context.bindService(),传递参数ServiceConnection;
5、在ServiceConnection.onServiceConnected()的实现中,会有一个IBinder接口参数名为service,调用YourInterfaceName.Stub.asInterface((IBinder)service)来把它转换成之前定义的AIDL接口类型。
6、调用在AIDL接口中声明的方法。需要注意连接出错时抛出的DeadObjectException,它是调用远程方法时可能出现的唯一异常。
7、调用Context.unbindService()来断开连接。

对象作为跨进程方法参数时,其引用计数会增加。匿名对象可以作为参数。

package com.company.app;import android.app.Activity;import android.os.Bundle;import android.content.Intent;import android.content.ServiceConnection;import android.content.ComponentName;import android.os.IBinder;import android.util.Log;// 客户端的Activitypublic class ClientActivity extends Activity {    private final String LOG_TAG = "ClientActivity";    private IRemoteService mIRemoteService;    private boolean mIsBound;    @Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.activity_client);    }    @Override    protected void onStart() {        super.onStart();        Intent intent = new Intent(this, IRemoteService.class);        // 调用此方法与服务器连接        bindService(intent, mServiceConnection, BIND_AUTO_CREATE);    }    @Override    protected void onStop() {        super.onStop();        unbindService(mServiceConnection);        mIsBound = false;    }    private ServiceConnection mServiceConnection = new ServiceConnection() {        public void onServiceConnected(ComponentName className, IBinder service) {            // asInterface()是在Stub中自动生成的helper方法            mIRemoteService = IRemoteService.Stub.asInterface(service);            mIsBound = true;        }        public void onServiceDisconnected(ComponentName className) {            Log.e(LOG_TAG, "Service has unexpectedly disconnected");            mIRemoteService = null;            mIsBound = false;        }    };    // 在适当的时机调用远程服务器接口。    // 注意应当避免在Activity主线程中调用。    // mIRemoteService.doSomething();    ...}

参考资料:
Android > Develop > API Guides > Android Interface Definition Language (AIDL)
https://developer.android.com/intl/zh-cn/guide/components/aidl.html#PassingObjects

0 0
原创粉丝点击