Android-IPC机制之AIDL(接口定义语言 )

此文针对以下问题进行说明

1 为什么需要AIDL? 不同进程之间或者说不同应用之间为什么不能直接进行通信和交换数据?

2 实用AIDL通信的原理是什么? 服务端如何与客户端进行通信和交换数据?

3 AIDL通信时,如何自定义类型?

4 如何进行AIDL通信,其步骤是什么?

此文将针对上述问题进行解释,先去理解一些概念,然后自己去写一个多进程使用AIDL交互的Demo,再去看看android中对AIDL的实际运用。


1 基本概念

 在 Android 上,由于进程都在自己的应用沙盒内运行,一个进程通常无法访问另一个进程的内存。尽管如此,进程需要将其对象分解成操作系统能够识别的原语,并将对象编组成跨越边界的对象。 编写执行这一编组操作的代码是一项繁琐的工作,因此 Android 会使用 AIDL 来处理。

1.1 首先我们来理解下什么是IBinder?

此处是官网说明地址 https://developer.android.google.cn/reference/android/os/IBinder.html(英文版的)

中文翻译是参考的这篇博客: https://blog.csdn.net/sergeycao/article/details/52585411

IBinder是远程对象的基本接口,是为高性能而设计的轻量级远程调用机制的核心部分。但它不仅用于远程调用,也用于进程内调用。这个接口定义了与远程对象交互的协议。不要直接实现这个接口,而应该从Binder派生。
IBinder的主要API是transact(),与它对应另一方法是Binder.onTransact()。transact()使你可以向远端的IBinder对象发送发出调用,onTransact()方法使你自己的远程对象能够响应接收到的调用。
IBinder的API都是同步执行的,比如transact()直到对方的Binder.onTransact()方法调用完成后才返回。调用发生在进程内时无疑是这样的,而在进程间时,在IPC的帮助下,也是同样的效果。
通过transact()发送的数据是Parcel,Parcel是一种一般的缓冲区,除了有数据外还带有一些描述它内容的元数据。元数据用于管理IBinder对象的引用,这样就能在缓冲区从一个进程移动到另一个进程时保存这些引用。这样就保证了当一个IBinder被写入到Parcel并发送到另一个进程中,如果另一个进程把同一个IBinder的引用回发到原来的进程,那么这个原来的进程就能接收到发出的那个IBinder的引用。这种机制使IBinder和Binder像唯一标志符那样在进程间管理。
系统为每个进程维护一个存放交互线程的线程池。这些交互线程用于派送所有从另外进程发来的IPC调用。例如:当一个IPC从进程A发到进程B,A中那个发出调用的线程(这个应该不在线程池中)就阻塞在transact()中了。进程B中的交互线程池中的一个线程接收了这个调用,它调用Binder.onTransact(),完成后用一个Parcel来做为结果返回。然后进程A中的那个等待的线程在收到返回的Parcel后得以继续执行。实际上,另一个进程看起来就像是当前进程的一个线程,但不是当前进程创建的。
Binder机制还支持进程间的递归调用。例如,进程A执行自己的IBinder的transact()调用进程B的Binder,而进程B在其Binder.onTransact()中又用transact()向进程A发起调用,那么进程A在等待它发出的调用返回的同时,还会用Binder.onTransact()响应进程B的transact()。总之Binder造成的结果就是让我们感觉到跨进程的调用与进程内的调用没什么区别。
当操作远程对象时,你经常需要查看它们是否有效,有三种方法可以使用:
1 transact()方法将在IBinder所在的进程不存在时抛出RemoteException异常。
2 如果目标进程不存在,那么调用pingBinder()时返回false。
3 可以用linkToDeath()方法向IBinder注册一个IBinder.DeathRecipient,在IBinder代表的进程退出时被调用。
要实现IBinder来支持远程调用,应从Binder类派生一个类。Binder实现了IBinder接口。但是一般不需要直接实现此类,而是跟据你的需要由开发包中的工具生成,这个工具叫AIDL。你通过AIDL语言定义远程对象的方法,然后用AIDL工具生成Binder的派生类,然后就可使用之。然而,可是,但是,当然,你也可以直接从Binder类派生以实现自定义的RPC调用,或只是实例化一个原始的Binder对象直接作为进程间共享的令牌来使用。


2 多进程使用AIDL交互的Demo

我们先来看看大体步骤,然后在Android Studio上使用AIDL实现不同应用之间如何进行交互(此处我采取了两个项目,一个是服务端(提供服务的一方)项目,一个是用户端(使用服务的一方)项目)

然后我们再从源码中去讲解他的实现方式是什么? 先要会用,然后再去分析,这样会更加易懂一些。

2.1 使用AIDL语言定义远程服务的接口

a 首先,首先,创建另外一个项目MyTestAidl1作为服务端(即提供服务的一端),然后创建一个AIDL目录



b 然后在该AIDL目录下,创建AIDL文件,也就是我们需要跟客户端进行通信所需要的接口


c 创建之后,就可以开始定义和编写我们的AIDL接口了

在此需要说明一下支持的数据类型

默认情况下,AIDL 支持下列数据类型:

  • Java 编程语言中的所有原语类型(如 intlongcharboolean 等等)
  • String
  • CharSequence
  • List

    List 中的所有元素都必须是以上列表中支持的数据类型、其他 AIDL 生成的接口或您声明的可打包类型。 可选择将 List 用作“通用”类(例如,List<String>)。另一端实际接收的具体类始终是 ArrayList,但生成的方法使用的是 List 接口。

  • Map

    Map 中的所有元素都必须是以上列表中支持的数据类型、其他 AIDL 生成的接口或您声明的可打包类型。 不支持通用 Map(如 Map<String,Integer>形式的 Map)。 另一端实际接收的具体类始终是 HashMap,但生成的方法使用的是 Map 接口。

您必须为以上未列出的每个附加类型加入一个 import 语句,即使这些类型是在与您的接口相同的软件包中定义。

定义服务接口时,请注意:

  • 方法可带零个或多个参数,返回值或空值。
  • 所有非原语参数都需要指示数据走向的方向标记。可以是 inout 或 inout(见以下示例)。

    原语默认为 in,不能是其他方向。

    注意:您应该将方向限定为真正需要的方向,因为编组参数的开销极大。

  • .aidl 文件中包括的所有代码注释都包含在生成的 IBinder 接口中(import 和 package 语句之前的注释除外)
  • 只支持方法;您不能公开 AIDL 中的静态字段。

首先来定义IRemoteService.aidl

// IRemoteService.aidl
package com.didibiaoche.presentation.mytestaidl1;
import com.didibiaoche.presentation.mytestaidl1.Person; //必须手动导入,并且创建对应的aidl文件
// Declare any non-default types here with import statements

interface IRemoteService {
    /**
     * 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);

    String showToast(String msg);

    String addPersion(in Person s);
}

注意一点的是当我们需要传递自定义类型的时候,需要创建对应的AIDL文件,如下是Person类的AIDL文件:

// Person.aidl
package com.didibiaoche.presentation.mytestaidl1; //必须手动导入
parcelable Person;

如下是创建的Person类,直接去实现Parcalable接口,as会自动帮我们生成实现方法!

public class Person implements Parcelable {

    public String name;

    protected Person(Parcel in) {
        name = in.readString();
    }

    public static final Creator<Person> CREATOR = new Creator<Person>() {
        @Override
        public Person createFromParcel(Parcel in) {
            return new Person(in);
        }

        @Override
        public Person[] newArray(int size) {
            return new Person[size];
        }
    };

    @Override
    public int describeContents() {
        return 0;
    }

    @Override
    public void writeToParcel(Parcel dest, int flags) {

        dest.writeString(name);
    }
}

当然,可能在创建的过程中有很多报错,as都给我们很明显的显示出来了,所以需要我们自己去处理

当我们build一下之后,as会自动帮我们生成对应的接口文件,此文件非常重要,他是服务端与客户端通信的桥梁

此时,文件目录如下:


此处说明下:

.aidl文件始终是生成目标.java文件的声明标记。大多数情况下,对android.os.IInterfaceandroid.os.Binder的继承及实现不应当手动操作。所有的生成行为应当由.aidl声明来完成。此外,所生成的目标文件位于app/build/generated/source目录下。

其次,android.os.Binder实现于android.os.IBinderandroid.os.Binder实现了大多数进程状态所必要的功能,以及所有必要的功能调度。

.aidl存在的目的就是为了剔除大量重复性的工作。这其中包括,所生成目标文件下的类种类Stub方法:onTransact(int, android.os.Parcel, android.os.Parcel, int)。此方法重写自android.os.Binder。此外,.aidl是为对外暴露接口而设计的。但是自己也是可以去自定义这些aidl接口的哦。

参考自 链接:https://www.jianshu.com/p/b260051237fe

2.2 实现接口

2.2.1 创建RemoteService.java文件,同时在清单文件中需要声明service如下:

<service
    android:name=".RemoteService"
    android:exported="true"/>


RemoteService中通过onBind()方法返回IBinder对象,这样调用者(客户端)使用获取的IBinder对象就可以访问远程服务,,而至于远程服务是如何与客服端进行交互,后面会分析源码分析出来

以下是代码部分:

public class RemoteService extends Service {
    @Override
    public void onCreate() {
        super.onCreate();
    }

    @Override
    public IBinder onBind(Intent intent) {
        // Return the interface
        return mBinder;
    }

    private final IRemoteService.Stub mBinder = new IRemoteService.Stub() {

        public void basicTypes(int anInt, long aLong, boolean aBoolean,
                               float aFloat, double aDouble, String aString) {
            // Does nothing
        }

        @Override
        public String showToast(String msg) throws RemoteException {
            return "服务器 显示RemoteService" + msg;
        }

        @Override
        public String addPersion(Person s) throws RemoteException {
            return "服务器 显示Person的" + s.name;
        }
    };
}

定义好了之后,在代码中直接启动服务即可.同时必须注意的是,必须设置对应的包名,和service的路径,如下:

private void startService() {
    Intent intent = new Intent();

    intent.setComponent(new ComponentName("com.didibiaoche.presentation.mytestaidl1", "com.didibiaoche.presentation.mytestaidl1.RemoteService"));

    startService(intent);

}

以上是服务端的配置,就配置完了.再让我们来看看客户端的配置吧! 有一点需要注意的是,此处的service最好定义自定义权限,防止不正当程序访问.同时当我们设置AIDL文件的时候,需要AIDL双方涉及的类所在的包名要一直!不然无法调用起来.

2.3 客户端(使用服务的一端)接口实现

注意:存放AIDL的包名要一致,以及包名中引用的类,最好放在一块

1 首先,创建另外一个项目MyTestAidl2,将服务端的AIDL原封不动的copy过来,项目如下:


注意:此处Person也需要同时copy过来

2 客户端调用服务端的代码如下,需要build一下才能调用对应的接口方法:

public class MainActivity extends AppCompatActivity {

    private IRemoteService mIRemoteService;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        final Button mbutton = findViewById(R.id.txt_aidl);
        final EditText editText = findViewById(R.id.edit_input);

        bindService1();

        mbutton.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                try {
                    String s = mIRemoteService.showToast(editText.getText().toString());
                    mbutton.setText(s);

                } catch (RemoteException e) {
                    e.printStackTrace();
                }
            }
        });
    }

    private void bindService1() {
        Intent intent = new Intent();

        intent.setComponent(new ComponentName("com.didibiaoche.presentation.mytestaidl1", "com.didibiaoche.presentation.mytestaidl1.RemoteService"));

        bindService(intent, mServiceConnection, Service.BIND_AUTO_CREATE);
    }

    
/**
 * Class for interacting with the main interface of the service.
 */
ServiceConnection mServiceConnection = new ServiceConnection() {
    // This is called when the connection with the service has been
    // established, giving us the service object we can use to
    // interact with the service.  We are communicating with our
    // service through an IDL interface, so get a client-side
    // representation of that from the raw service object.
    @Override
    public void onServiceConnected(ComponentName name, IBinder service) {

        mIRemoteService = IRemoteService.Stub.asInterface(service);
    }

    // This is called when the connection with the service has been
    // unexpectedly disconnected -- that is, its process crashed.
    @Override
    public void onServiceDisconnected(ComponentName name) {
        mIRemoteService = null;
    }
};

xml文件如下:

<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context="com.didibiaoche.presentation.mytestaidl1.MainActivity">

    <Button
        android:id="@+id/txt_aidl"
        android:layout_width="100dp"
        android:layout_height="wrap_content"
        android:text="Hello World!"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toTopOf="parent"/>

    <EditText
        android:id="@+id/edit_input"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"/>

</android.support.constraint.ConstraintLayout>


在分析重要的通信接口之前,首先我们需要了解一些概念:

onTransact和Transact方法

下面用Binder-SYS表示安卓系统中运行的Binder系统,Binder-IPC表示Binder实现IPC的机制。

Binder-SYS将通信的双方分为Server和Client,即C/S架构。
两端进程均使用一个接口IBinder的实例进行通信,它定义了方法IBinder.transact(),方法原型:

/**
 * Perform a generic operation with the object.
 *
 * @param code The action to perform.  This should
 * be a number between {@link #FIRST_CALL_TRANSACTION} and
 * {@link #LAST_CALL_TRANSACTION}.
 * @param data Marshalled data to send to the target.  Must not be null.
 * If you are not sending any data, you must create an empty Parcel
 * that is given here.
 * @param reply Marshalled data to be received from the target.  May be
 * null if you are not interested in the return value.
 * @param flags Additional operation flags.  Either 0 for a normal
 * RPC, or {@link #FLAG_ONEWAY} for a one-way RPC.
 */
public boolean transact(int code, Parcel data, Parcel reply, int flags)
    throws RemoteException;
  • code
    表示要执行的动作,类似Handler发送的Message的what。
    code指示了当前远程操作的命令,IBinder定义了像INTERFACE_TRANSACTION、PING_TRANSACTION这样的几个通用命令。自己使用的命令的标识值需要在FIRST_CALL_TRANSACTION和LAST_CALL_TRANSACTION之间,仅仅是整数范围的一个约定,很好理解。

  • data和reply
    data和reply参数相当于普通java方法里的调用参数和返回值。Parcel类型是可以跨进程的数据。

  • flags
    参数flags只有0和FLAG_ONEWAY两种,默认的跨进程操作是同步的,所以transact()方法的执行会阻塞,调用以同步的形式传递到远程的transact(),等待远端的transact()返回后继续执行——最好理解的方式就是把两端的transact()看作一个方法,Binder机制的目标也就是这样。指定FLAG_ONEWAY时,表示Client的transact()是单向调用,执行后立即返回,无需等待Server端transact()返回。

Server和Client利用IBinder跨进程通信的原理是:

Client调用其IBinder实例的transact()发起操作,Binder-SYS使得方法调用传递到Server端,以相同的参数执行Server端IBinder实例的transact()方法——这就是Binder-SYS实现的跨进程操作。

而onTransact就是匹配并接受来自远程另一端的信息

onTransact(int code, android.os.Parcel data, android.os.Parcel reply, int flags) 

此方法运行在服务端中的线程池中,当客户端发起跨进程请求时,远程请求会通过系统底层封装后交给此方法来处理。

服务端通过code确定请求的目标方法是什么,然后从data中取出目标方法需要的参数,然后调用服务方的方法,再将服务方返回的参数通过reply返回给客户端,如果此时耗时比较长,客户端在调用服务方的方法时是被挂起的,所以耗时操作最好不要在主线程操作。以上就是onTransact的执行过程,没明白不要紧,下面就去仔细分析。


理解完概念之后,我们来看看用户端和服务端通信的重要类ADIL接口

如下是目录:



然后再来看看代码:

public interface IRemoteService extends android.os.IInterface {
    /**
     * Local-side IPC implementation stub class.
     */
    public static abstract class Stub extends android.os.Binder implements com.didibiaoche.presentation.mytestaidl1.IRemoteService {
        private static final java.lang.String DESCRIPTOR = "com.didibiaoche.presentation.mytestaidl1.IRemoteService";

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

        /**
         * Cast an IBinder object into an com.didibiaoche.presentation.mytestaidl1.IRemoteService interface,
         * generating a proxy if needed.
         */
        public static com.didibiaoche.presentation.mytestaidl1.IRemoteService asInterface(android.os.IBinder obj) {
            if ((obj == null)) {
                return null;
            }
            android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
            if (((iin != null) && (iin instanceof com.didibiaoche.presentation.mytestaidl1.IRemoteService))) {
                return ((com.didibiaoche.presentation.mytestaidl1.IRemoteService) iin);
            }
            return new com.didibiaoche.presentation.mytestaidl1.IRemoteService.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_basicTypes: {
                    data.enforceInterface(DESCRIPTOR);
                    int _arg0;
                    _arg0 = data.readInt();
                    long _arg1;
                    _arg1 = data.readLong();
                    boolean _arg2;
                    _arg2 = (0 != data.readInt());
                    float _arg3;
                    _arg3 = data.readFloat();
                    double _arg4;
                    _arg4 = data.readDouble();
                    java.lang.String _arg5;
                    _arg5 = data.readString();
                    this.basicTypes(_arg0, _arg1, _arg2, _arg3, _arg4, _arg5);
                    reply.writeNoException();
                    return true;
                }
                case TRANSACTION_showToast: {
                    data.enforceInterface(DESCRIPTOR);
                    java.lang.String _arg0;
                    _arg0 = data.readString();
                    java.lang.String _result = this.showToast(_arg0);
                    reply.writeNoException();
                    reply.writeString(_result);
                    return true;
                }
                case TRANSACTION_addPersion: {
                    data.enforceInterface(DESCRIPTOR);
                    com.didibiaoche.presentation.mytestaidl1.Person _arg0;
                    if ((0 != data.readInt())) {
                        _arg0 = com.didibiaoche.presentation.mytestaidl1.Person.CREATOR.createFromParcel(data);
                    } else {
                        _arg0 = null;
                    }
                    java.lang.String _result = this.addPersion(_arg0);
                    reply.writeNoException();
                    reply.writeString(_result);
                    return true;
                }
            }
            return super.onTransact(code, data, reply, flags);
        }

        private static class Proxy implements com.didibiaoche.presentation.mytestaidl1.IRemoteService {
            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;
            }

            /**
             * Demonstrates some basic types that you can use as parameters
             * and return values in AIDL.
             */
            @Override
            public void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat, double aDouble, java.lang.String aString) throws android.os.RemoteException {
                android.os.Parcel _data = android.os.Parcel.obtain();
                android.os.Parcel _reply = android.os.Parcel.obtain();
                try {
                    _data.writeInterfaceToken(DESCRIPTOR);
                    _data.writeInt(anInt);
                    _data.writeLong(aLong);
                    _data.writeInt(((aBoolean) ? (1) : (0)));
                    _data.writeFloat(aFloat);
                    _data.writeDouble(aDouble);
                    _data.writeString(aString);
                    mRemote.transact(Stub.TRANSACTION_basicTypes, _data, _reply, 0);
                    _reply.readException();
                } finally {
                    _reply.recycle();
                    _data.recycle();
                }
            }

            @Override
            public java.lang.String showToast(java.lang.String msg) throws android.os.RemoteException {
                android.os.Parcel _data = android.os.Parcel.obtain();
                android.os.Parcel _reply = android.os.Parcel.obtain();
                java.lang.String _result;
                try {
                    _data.writeInterfaceToken(DESCRIPTOR);
                    _data.writeString(msg);
                    mRemote.transact(Stub.TRANSACTION_showToast, _data, _reply, 0);
                    _reply.readException();
                    _result = _reply.readString();
                } finally {
                    _reply.recycle();
                    _data.recycle();
                }
                return _result;
            }

            @Override
            public java.lang.String addPersion(com.didibiaoche.presentation.mytestaidl1.Person s) throws android.os.RemoteException {
                android.os.Parcel _data = android.os.Parcel.obtain();
                android.os.Parcel _reply = android.os.Parcel.obtain();
                java.lang.String _result;
                try {
                    _data.writeInterfaceToken(DESCRIPTOR);
                    if ((s != null)) {
                        _data.writeInt(1);
                        s.writeToParcel(_data, 0);
                    } else {
                        _data.writeInt(0);
                    }
                    mRemote.transact(Stub.TRANSACTION_addPersion, _data, _reply, 0);
                    _reply.readException();
                    _result = _reply.readString();
                } finally {
                    _reply.recycle();
                    _data.recycle();
                }
                return _result;
            }
        }

        static final int TRANSACTION_basicTypes = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
        static final int TRANSACTION_showToast = (android.os.IBinder.FIRST_CALL_TRANSACTION + 1);
        static final int TRANSACTION_addPersion = (android.os.IBinder.FIRST_CALL_TRANSACTION + 2);
    }

    /**
     * Demonstrates some basic types that you can use as parameters
     * and return values in AIDL.
     */
    public void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat, double aDouble, java.lang.String aString) throws android.os.RemoteException;

    public java.lang.String showToast(java.lang.String msg) throws android.os.RemoteException;

    public java.lang.String addPersion(com.didibiaoche.presentation.mytestaidl1.Person s) throws android.os.RemoteException;
}

stub是IRemoteService的内部类,进程间或者本地的交互就是通过此类来完成的。

proxy的stub的内部类,也实现了IRemoteService,远程进程的间的交互实际上是通过次来完成的。

asInterface方法

客户端或者服务端都有IRemoteService接口,当客户端或者服务端在绑定service的时候,再回调的serviceconnection得到的IBinder实现类,调用“远程方法”,那么实际是不是调用的远程方法呢?

先来看看这里,由于service调用是异步的所以,在绑定的时候需要传入ServiceConnection实现回调,里面包含两个方法,分别是在绑定服务后,返回IBinder对象,此处需要注意此方法:

mIRemoteService = IRemoteService.Stub.asInterface(service);

点击进入源码之后发现:

/**
 * Cast an IBinder object into an com.didibiaoche.presentation.mytestaidl1.IRemoteService interface,
 * generating a proxy if needed.
 */
public static com.didibiaoche.presentation.mytestaidl1.IRemoteService asInterface(android.os.IBinder obj) {
    if ((obj == null)) {
        return null;
    }
    android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
    if (((iin != null) && (iin instanceof com.didibiaoche.presentation.mytestaidl1.IRemoteService))) {
        return ((com.didibiaoche.presentation.mytestaidl1.IRemoteService) iin);
    }
    return new com.didibiaoche.presentation.mytestaidl1.IRemoteService.Stub.Proxy(obj);
}


如果得到的不是本地的IRemoteService接口,则直接返回一个代理类.所以当我们调用如下代码的时候:

String s = mIRemoteService.showToast(editText.getText().toString());
mbutton.setText(s);

其实是代理处理的,而此处代理执行,否则也就是说是本地的IRemoteService接口存在于本地,则直接返回,此时不存在进程间的通信,transact就不会被执行。

@Override
public java.lang.String showToast(java.lang.String msg) throws android.os.RemoteException {
    android.os.Parcel _data = android.os.Parcel.obtain();
    android.os.Parcel _reply = android.os.Parcel.obtain();
    java.lang.String _result;
    try {
        _data.writeInterfaceToken(DESCRIPTOR);
        _data.writeString(msg);
        mRemote.transact(Stub.TRANSACTION_showToast, _data, _reply, 0);
        _reply.readException();
        _result = _reply.readString();
    } finally {
        _reply.recycle();
        _data.recycle();
    }
    return _result;
}

如果是远程间通信的话,transact直接调用的是原生系统的方法,当调用此方法后,底层会通过一系列标志与服务端进行通信

那么再让我们来看看服务端的代码:


远程服务的方法调用,是stub里面执行的,那么怎么被执行的呢?

也就是在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_basicTypes: {
            data.enforceInterface(DESCRIPTOR);
            int _arg0;
            _arg0 = data.readInt();
            long _arg1;
            _arg1 = data.readLong();
            boolean _arg2;
            _arg2 = (0 != data.readInt());
            float _arg3;
            _arg3 = data.readFloat();
            double _arg4;
            _arg4 = data.readDouble();
            java.lang.String _arg5;
            _arg5 = data.readString();
            this.basicTypes(_arg0, _arg1, _arg2, _arg3, _arg4, _arg5);
            reply.writeNoException();
            return true;
        }
        case TRANSACTION_showToast: {
            data.enforceInterface(DESCRIPTOR);
            java.lang.String _arg0;
            _arg0 = data.readString();
            java.lang.String _result = this.showToast(_arg0);
            reply.writeNoException();
            reply.writeString(_result);
            return true;
        }
        case TRANSACTION_addPersion: {
            data.enforceInterface(DESCRIPTOR);
            com.didibiaoche.presentation.mytestaidl1.Person _arg0;
            if ((0 != data.readInt())) {
                _arg0 = com.didibiaoche.presentation.mytestaidl1.Person.CREATOR.createFromParcel(data);
            } else {
                _arg0 = null;
            }
            java.lang.String _result = this.addPersion(_arg0);
            reply.writeNoException();
            reply.writeString(_result);
            return true;
        }
    }
    return super.onTransact(code, data, reply, flags);
}

此处说明下上面的代码:

服务端通过code确定请求的目标方法是什么,比如addPerson,然后从data中取出目标方法需要的参数,然后调用服务方的方法this.addPerson(arg0),再将服务方返回的参数通过reply返回给客户端,如果此时耗时比较长,客户端在调用服务方的方法时是被挂起的,所以耗时操作最好不要在主线程操作。以上就是onTransact的执行过程.

到此处就实现了服务端与客户端的通信,通信流程图如下:


客户端通过得到的Ibinder实例调用服务端的方法,存在远程调用的时候,asInterface返回的Ibinder实例就是stub内部的代理类,此时内部是通过stub内部的代理类proxy调用transact,自己被挂起等待服务端的方法执行完返回,请求远程调用的类都实现了Binder接口,所以底层进入跨进程通信。

服务端通过一系列标志,在底层得到客户端的请求后,onTransact就被被响应,执行服务端的方法。也就是stub方法内部onTransact的被调用,然后返回。

那么,让我们看下最后的结果:

首先执行MyTestAidl1 启动服务,然后启动MyTestAidl2 绑定服务,执行结果如下:

 


android中对AIDL的实际运用

在Activity的启动流程中,进程之间的交互特别明显。

先上图


ApplicationThreadProxy是IApplicationThread的代理,也就是说ApplicationThreadNative实现了IApplicationThread,最终方法实现是在ApplicationThreadProxy里面,ApplicationThreadNative继承了Binder,复写了onTransact,当远程服务ActivityManagerServiced调用ApplicationThread中的方法的时候,onTransact方法就会被调用。然后根据相应的code调用IApplicationThread的方法。

先来看看ApplicationThreadNative类

  1. public abstract class ApplicationThreadNative extends Binder  
  2.         implements IApplicationThread {  
  3.     /** 
  4.      * Cast a Binder object into an application thread interface, generating 
  5.      * a proxy if needed. 
  6.      */  
  7.     static public IApplicationThread asInterface(IBinder obj) {  
  8.         if (obj == null) {  
  9.             return null;  
  10.         }  
  11.         IApplicationThread in =  
  12.             (IApplicationThread)obj.queryLocalInterface(descriptor);  
  13.         if (in != null) {  
  14.             return in;  
  15.         }  
  16.           
  17.         return new ApplicationThreadProxy(obj);  
  18.     }  
  19.       
  20.     public ApplicationThreadNative() {  
  21.         attachInterface(this, descriptor);  
  22.     }  
  23.       
  24.     @Override  
  25.     public boolean onTransact(int code, Parcel data, Parcel reply, int flags)  
  26.             throws RemoteException {  
  27.         switch (code) {  
  28.         case SCHEDULE_PAUSE_ACTIVITY_TRANSACTION:  
  29.         {  
  30.             data.enforceInterface(IApplicationThread.descriptor);  
  31.             IBinder b = data.readStrongBinder();  
  32.             boolean finished = data.readInt() != 0;  
  33.             boolean userLeaving = data.readInt() != 0;  
  34.             int configChanges = data.readInt();  
  35.             schedulePauseActivity(b, finished, userLeaving, configChanges);  
  36.             return true;  
  37.         }  
  38.      省略很多。。。  
  39.         case SCHEDULE_STOP_ACTIVITY_TRANSACTION:  
  40.         {  
  41.             data.enforceInterface(IApplicationThread.descriptor);  
  42.             IBinder b = data.readStrongBinder();  
  43.             boolean show = data.readInt() != 0;  
  44.             int configChanges = data.readInt();  
  45.             scheduleStopActivity(b, show, configChanges);  
  46.             return true;  
  47.         }  
  48.           
  49.          
  50.         case SCHEDULE_STOP_SERVICE_TRANSACTION:  
  51.         {  
  52.             data.enforceInterface(IApplicationThread.descriptor);  
  53.             IBinder token = data.readStrongBinder();  
  54.             scheduleStopService(token);  
  55.             return true;  
  56.         }  

此处是流程图:


这个是老罗的Android系统源码情景分析(第三版)的书籍摘抄。

ApplicationThreadProxy通过ontransact调用schedulePauseActivity,这个方法是在ApplicationThread里面执行的,因为ApplicationThread是ActivityThread的内部类,同时继承ApplicationThreadNative 。

ApplicationThread主要的作用是控制activity的生命周期,看一下列出来的部分的方法:



我们以schedulePauseActivity方法为例子看:

[java]  view plain  copy
  1. public final void schedulePauseActivity(IBinder token, boolean finished,  
  2.                boolean userLeaving, int configChanges) {  
  3.            sendMessage(  
  4.                    finished ? <span style="color:#ff0000;">H.PAUSE_ACTIVITY_FINISHING</span> : H.PAUSE_ACTIVITY,  
  5.                    token,  
  6.                    (userLeaving ? 1 : 0),  
  7.                    configChanges);  
  8.        }  

sendMessage方法如下:


[java]  view plain  copy
  1. private void sendMessage(int what, Object obj, int arg1, int arg2) {  
  2.        sendMessage(what, obj, arg1, arg2, false);  
  3.    }  
  4.   
  5.    private void sendMessage(int what, Object obj, int arg1, int arg2, boolean async) {  
  6.        if (DEBUG_MESSAGES) Slog.v(  
  7.            TAG, "SCHEDULE " + what + " " + mH.codeToString(what)  
  8.            + ": " + arg1 + " / " + obj);  
  9.        Message msg = Message.obtain();  
  10.        msg.what = what;  
  11.        msg.obj = obj;  
  12.        msg.arg1 = arg1;  
  13.        msg.arg2 = arg2;  
  14.        if (async) {  
  15.            msg.setAsynchronous(true);  
  16.        }  
  17.        mH.sendMessage(msg);  
  18.    }  

看到了,最终调用到mH,mH是一个hander对象:

[java]  view plain  copy
  1. </pre></p><pre name="code" class="java">private class H extends Handler {  
  2.         public static final int LAUNCH_ACTIVITY         = 100;  
  3.         public static final int PAUSE_ACTIVITY          = 101;  
  4.         public static final int PAUSE_ACTIVITY_FINISHING= 102;  
  5.        省略若干、、、、  
  6.         public static final int TRIM_MEMORY             = 140;  
  7.         public static final int DUMP_PROVIDER           = 141;  
  8.         public static final int UNSTABLE_PROVIDER_DIED  = 142;  
  9.         public static final int REQUEST_ASSIST_CONTEXT_EXTRAS = 143;  
  10.         public static final int TRANSLUCENT_CONVERSION_COMPLETE = 144;  
  11.         public static final int INSTALL_PROVIDER        = 145;  
  12.         、、、  
  13.         public void handleMessage(Message msg) {  
  14.             if (DEBUG_MESSAGES) Slog.v(TAG, ">>> handling: " + codeToString(msg.what));  
  15.             switch (msg.what) {  
  16.                 case LAUNCH_ACTIVITY: {  
  17.                     Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "activityStart");  
  18.                     ActivityClientRecord r = (ActivityClientRecord)msg.obj;  
  19.   
  20.                     r.packageInfo = getPackageInfoNoCheck(  
  21.                             r.activityInfo.applicationInfo, r.compatInfo);  
  22.                     handleLaunchActivity(r, null);  
  23.                     Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);  
  24.                 } break;  
  25.                 case RELAUNCH_ACTIVITY: {  
  26.                     Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "activityRestart");  
  27.                     ActivityClientRecord r = (ActivityClientRecord)msg.obj;  
  28.                     handleRelaunchActivity(r);  
  29.                     Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);  
  30.                 } break;  
  31.                 <span style="color:#ff0000;">case PAUSE_ACTIVITY:  
  32.                     Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "activityPause");  
  33.                     handlePauseActivity((IBinder)msg.obj, false, msg.arg1 != 0, msg.arg2);  
  34.                     maybeSnapshot();  
  35.                     Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);  
  36.                     break;</span>  
  37.                  
  38.                 、、、、  
  39.                 case INSTALL_PROVIDER:  
  40.                     handleInstallProvider((ProviderInfo) msg.obj);  
  41.                     break;  
  42.             }  
  43.             if (DEBUG_MESSAGES) Slog.v(TAG, "<<< done: " + codeToString(msg.what));  
  44.         }  

来看下handlePauseActivity这个类

[java]  view plain  copy
  1. private void handlePauseActivity(IBinder token, boolean finished,  
  2.             boolean userLeaving, int configChanges) {  
  3.         ActivityClientRecord r = mActivities.get(token);  
  4.         if (r != null) {  
  5.             //Slog.v(TAG, "userLeaving=" + userLeaving + " handling pause of " + r);  
  6.             if (userLeaving) {  
  7.                 performUserLeavingActivity(r);  
  8.             }  
  9.   
  10.             r.activity.mConfigChangeFlags |= configChanges;  
  11.             performPauseActivity(token, finished, r.isPreHoneycomb());
  12.   
  13.             // Make sure any pending writes are now committed.  
  14.             if (r.isPreHoneycomb()) {  
  15.                 QueuedWork.waitToFinish();  
  16.             }  
  17.   
  18.             // Tell the activity manager we have paused.  
  19.             try {  
  20.                 ActivityManagerNative.getDefault().activityPaused(token);  
  21.             } catch (RemoteException ex) {  
  22.             }  
  23.         }  
  24.     }  

执行到此处ActivityManagerNative.getDefault().activityPaused(token);是直接获取到ActivityManagerService的代理类然后执行ActivityManagerProxy,然后调用AMS的activityPaused方法,执行进程间的通信。

具体启动过程可以去看老罗的书。后面有时间我会总结和更新出来的。

正在持续更新中...


此处有大佬录制的视频,大家可以仔细参考,建议跟着去创建项目,然后不懂的地方多去看看源码!!

https://www.imooc.com/video/11196

任玉刚大神的Android技术开发探索也解释的比较清楚,大家也可以去查看,特别是在多进程和项目IBinder池这块解释的比较清楚,也比较实用

此处是google官网,大家也可以参考下:

https://developer.android.google.cn/guide/components/aidl.html#Defining



猜你喜欢

转载自blog.csdn.net/yang1349day/article/details/79931071