Android进程间通信(二):AIDL

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.aidl
package 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.aidl
package android.graphics;

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

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

// Rect.java
package 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 = new
Parcelable.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.aidl
package 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;

// 客户端的Activity
public 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

你可能感兴趣的:(aidl)