Android IPC机制(三)在Android Studio中使用AIDL实现跨进程方法调用

  1. 什么是AIDL服务

    ​ 一般创建的服务并不能被其他的应用程序访问。为了使其他的应用程序也可以访问本应用程序提供的服务,Android系统采用了远程过程调用(Remote Procedure Call,RPC)方式来实现。与很多其他的基于RPC的解决方案一样,Android使用一种接口定义语言(Interface Definition Language,IDL)来公开服务的接口。因此,可以将这种可以跨进程访问的服务称为AIDL(Android Interface Definition Language)服务。

    服务端创建过程
    1. 新建一个扩展名为IMyService.aidl的文件,并写下需要的接口。重新编译,会自动生成一个IMyService.java文件。
    2. 建立一个服务类(Service的子类)。并在创建的服务类中创建一个内部类,实现由aidl文件生成的Java接口。在服务类的onBind方法返回时,将实现aidl接口的内部类对象返回出去。
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    package com.um.aidlservicedemo;

    import android.app.Service;
    import android.content.Intent;
    import android.os.IBinder;
    import android.os.RemoteException;

    public class MyService extends Service {
    public MyService() {
    }

    // 创建一个继承自IMyService.Stub的内部类
    public class MyServiceImpl extends IMyService.Stub {

    // 必须实现AIDL文件中的接口
    @Override
    public int add(int arg1, int arg2) throws RemoteException {
    return arg1 + arg2;
    }
    }

    @Override
    public IBinder onBind(Intent intent) {
    // TODO: Return the communication channel to the service.
    return new MyServiceImpl();
    }
    }
    1. 在AndroidManifest.xml文件中配置AIDL服务,尤其要注意的是,标签中android:name的属性值就是客户端要引用该服务的ID,也就是Intent类的参数值。
    1
    2
    3
    4
    5
    6
    <service
    android:name="com.um.aidlservicedemo.MyService"
    android:enabled="true"
    android:exported="true"
    android:process=":remote">
    </service>
    客户端使用
    1. 新建一个AidlClientTestActivity,并将刚才远程服务端自动生成的app/build/目录下面的IMyService.java文件连同包目录一起复制到src/main目录中。

    1540976120606

    1. 调用AIDL服务首先要绑定服务,然后才能获得服务对象。
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    package com.um.aidlservicedemo;

    import android.content.ComponentName;
    import android.content.Intent;
    import android.content.ServiceConnection;
    import android.os.IBinder;
    import android.os.RemoteException;
    import android.support.v7.app.AppCompatActivity;
    import android.os.Bundle;
    import android.util.Log;
    import android.widget.TextView;

    public class AidlClientTestActivity extends AppCompatActivity {
    private static String TAG = "AidlClientTestActivity";
    TextView textView;
    IMyService iMyService;
    private ServiceConnection serviceConnection = new ServiceConnection() {
    @Override
    public void onServiceConnected(ComponentName name, IBinder service) {
    Log.d(TAG, "onServiceConnected: ");
    iMyService = IMyService.Stub.asInterface(service);
    try {
    textView.setText("" + iMyService.add(1,2));
    } catch (RemoteException e) {
    e.printStackTrace();
    }
    }

    @Override
    public void onServiceDisconnected(ComponentName name) {
    Log.d(TAG, "onServiceDisconnected: ");
    iMyService = null;
    }
    };

    @Override
    protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    Log.d(TAG, "onCreate: ");
    setContentView(R.layout.activity_main);
    textView = findViewById(R.id.text);
    Intent serviceIntent = new Intent();
    serviceIntent.setComponent(new ComponentName("com.um.aidlservicedemo","com.um.aidlservicedemo.MyService"));
    bindService(serviceIntent, serviceConnection, BIND_AUTO_CREATE);

    }

    @Override
    protected void onDestroy() {
    Log.d(TAG, "onDestroy: ");
    super.onDestroy();
    unbindService(serviceConnection);
    }
    }
    整体结构

    ​ 前面有提到Stub、asInterface等关键字,在这一节中我们主要针对AIDL内部机制的分析来深入理解AIDL调用的过程。打开IMyService.Java文件查看,发现他的结构是这样的:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    package com.um.aidlservicedemo;
    public interface IMyService extends android.os.IInterface {
    /**
    * Local-side IPC implementation stub class.
    * 可以看出,Stub其实是一个实现了IMyService的Binder抽象类。
    */
    public static abstract class Stub extends android.os.Binder implements com.um.aidlservicedemo.IMyService {
    private static final java.lang.String DESCRIPTOR = "com.um.aidlservicedemo.IMyService";

    /**
    * Construct the stub at attach it to the interface.
    */
    public Stub() {
    this.attachInterface(this, DESCRIPTOR);
    }

    //把远程Service的Binder对象传递进去,得到的是远程服务的本地代理
    public static com.um.aidlservicedemo.IMyService asInterface(android.os.IBinder obj) {
    if ((obj == null)) {
    return null;
    }
    android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
    if (((iin != null) && (iin instanceof com.um.aidlservicedemo.IMyService))) {
    return ((com.um.aidlservicedemo.IMyService) iin);
    }
    return new com.um.aidlservicedemo.IMyService.Stub.Proxy(obj);
    }

    @Override
    public android.os.IBinder asBinder() {
    return this;
    }
    //两个不同进程之间传递是通过onTransact接口完成的
    @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);
    }
    //远程服务的本地代理,当然也会继承自IMyService
    private static class Proxy implements com.um.aidlservicedemo.IMyService {
    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;
    }
    //调用的add方法其实就是这里。
    @Override
    public int add(int arg1, int arg2) 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(arg1);
    _data.writeInt(arg2);
    mRemote.transact(Stub.TRANSACTION_add, _data, _reply, 0);
    _reply.readException();
    _result = _reply.readInt();
    } finally {
    _reply.recycle();
    _data.recycle();
    }
    return _result;
    }
    }
    //可以看到,系统将我们的方法转换成以FIRST_CALL_TRANSACTION为基准的数字。
    static final int TRANSACTION_add = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
    }

    public int add(int arg1, int arg2) throws android.os.RemoteException;
    }

    下面时一个结构图:

    img

    ​ 整体上来看,IMyService.java文件中的IMyService类有一个Stub的内部类,还有一个getValue方法。这个方法就是我们在aidl中定义的方法。同时我们看到,生成的IMyService继承自IInterface,说明这也是一个接口,并没有对getValue进行实现。

    ​ 对于add这个方法,当我们在服务端的内部类中继承了IMyService.Stub抽象类以后,就需要对未曾实现的getValue方法进行定义。
    ​ 再来看IMyService的内部类Stub。这个内部类是一个实现了IMyService接口的Binder抽象类。内部有3个方法asInterface、asBinder、onTransact,还有一个内部类Proxy。

    得到的服务端Service对象

    ​ 现在我们来看一下客户端当初得到的服务端对象的情况,当我们连接上服务端后,会被动调用onServiceConnected方法:

    1
    2
    3
    public void onServiceConnected(ComponentName name, IBinder service) {
    iMyService = IMyService.Stub.asInterface(service);
    }

    在这里得到的service对象其实就是MyServiceImpl对象,他是实现了IMyService.Stub具体接口的IBinder。再来看一下asInterface:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    public static com.um.aidlservicedemo.IMyService asInterface(android.os.IBinder obj) {
    if ((obj == null)) {
    return null;
    }
    android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
    if (((iin != null) && (iin instanceof com.um.aidlservicedemo.IMyService))) {
    return ((com.um.aidlservicedemo.IMyService) iin);
    }
    //通过asInterface得到的其实是远程Service的本地“代理”,而代理对象的参数是远程的服务端“obj”
    return new com.um.aidlservicedemo.IMyService.Stub.Proxy(obj);
    }

    可以看出,onServiceConnected参数是远程服务端的IBinder对象,返回值是IMyService.Stub.Proxy(obj),这里的Proxy是Stub的内部类:

    1
    private static class Proxy implements com.um.aidlservicedemo.IMyService {}

    内部除了构造方法以外,只有add方法:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    @Override
    public int add(int arg1, int arg2) 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(arg1);
    _data.writeInt(arg2);
    mRemote.transact(Stub.TRANSACTION_add, _data, _reply, 0);
    _reply.readException();
    _result = _reply.readInt();
    } finally {
    _reply.recycle();
    _data.recycle();
    }
    return _result;
    }

    ​ 到这里我们看到,Proxy内部确实拥有服务端的各个方法,但这些方法并不是真实的实现,而只是通过mRemote.transact传输出去。
    也就是说,在客户端通过mIMyService = IMyService.Stub.asInterface(service)得到的就是Proxy对象,可以通过这个对象间接的调用服务端的各个方法,而具体调用过程就是经过mRemote.transact传输给真正的Service(也就是MyService.MyServiceImpl类)
    ​ 那么,具体来说,我们是如何通过这个代理对象调用到真实的getValue呢?

如何通过代理对象调用远端Service方法

​ 在代理类的add方法中看到,他是调用了mRemote.transact的方法。

​ 而mRemote.transact的方法通过底层的Binder通讯,将数据传输给服务端进程,并调用服务端的onTransact方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
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);
}

因为当初得到的远程服务对象是MyServiceImpl的对象,因此这里的this就指向了MyServiceImpl类。因此getValue方法就进入到了MyServiceImpl的内部,也就是远程服务端的内部。由此完成了一次完整的调用过程。

AIDL总结
  1. AIDL要是实现的最终目标是跨进程访问,简单的说就是得到另一个进程的对象,并调用其方法。
  2. AIDL与接口类似,本质属性都是一个Interface(AIDL文件是IInterface,而Interface是继承自Interface的),而且都只定义了抽象方法,没有具体的实现,需要子类去实现。
  3. 与接口不同的是:由AIDL生成的stub类本质上是一个Binder!这个类所生成的对象有两种方式可以传递给另外一个进程:
    • 一种是通过bindService的方式,绑定一个服务,而在绑定后,服务将会返回给客户端一个Binder的对象,此时可以把继承自stub的Binder传递给客户端。
    • 另外一种就是把继承自stub的类提升为系统服务,此时,我们通过ServiceManager去得到当前的系统服务,ServiceManager就会把目标Service的Binder对象传递给客户端。
  4. 经过上面两种方法得到的Binder对象,就像得到了本地的某个对象一样,可以调用其远程的方法。