进程间通信之aidl流程浅析

1. 铺垫
正常情况下,一个apk启动后只会运行在一个进程中,其进程名为apk的包名,所有的组件都会在这个进程中运行。
若要将某些组件(如Service,Activity等)运行在单独的进程中,就要在清单文件中对该组件设置android:process属性。

2. 代码

1) 清单文件:Service 和 Activity在同一个apk,但所处不同进程中

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

2) 实现了Parcelable接口的自定义类Student

public class Student implements Parcelable {
    private static final String TAG = "Student lyl123";
    private int id;

    public Student(int id) {
        this.id = id;
    }

    public static final Creator<Student> CREATOR = new Creator<Student>() {
        @Override
        public Student createFromParcel(Parcel in) {
            int arg0 = in.readInt();
            Log.e(TAG, "解包 - createFromParcel: arg0 = " + arg0);
            //log中调用一次in.readInt(),值为id的值,参数中再调用一次in.readInt(),值就不是id的值了!
            return new Student(arg0);
        }

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

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

    @Override
    public void writeToParcel(Parcel parcel, int i) {
        Log.e(TAG, "打包 - writeToParcel : id = " + id);
        parcel.writeInt(id);
    }

    @Override
    public String toString() {
        return "Student{" +
                "id=" + id +
                '}';
    }

    public int getId() {
        return id;
    }
    public void setId(int id) {
        this.id = id;
    }
}

3) Student类对应的Student.aidl文件

package com.lyl.dialservice;
parcelable Student;

4) IStudentInterface.aidl文件

package com.lyl.dialservice;
import com.lyl.dialservice.Student;

interface IStudentInterface {
    Student getStudentById(int id);
}

5) 对应的服务MyService(服务端)

public class MyService extends Service {
    private static final String TAG = "MyService lyl123";

    @Override
    public IBinder onBind(Intent intent) {
        return mBinder;
    }

    private IBinder mBinder = new IStudentInterface.Stub(){
        @Override
        public Student getStudentById(int id) throws RemoteException {
            Log.e(TAG, "getStudentById: " + id);
            return new Student(id);//此处可能要操作数据库,查询数据等 -- 此处的数据要返回给客户端
        }
    };
}

6) Activity(客户端)

public class MainActivity extends Activity {
    private static final String TAG = "MainActivity lyl123";
    IStudentInterface iStudentInterface = null;
    private ServiceConnection conn = null;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        bindMyService();
    }

    private void bindMyService() {
        if (null == conn) {
            Log.e(TAG, "bind service!");
            conn = new ServiceConnection() {
                @Override
                public void onServiceConnected(ComponentName name, IBinder service) {
                    iStudentInterface = IStudentInterface.Stub.asInterface(service);
                    Log.e(TAG, "iStudentInterface = " + iStudentInterface);

                    try {
                        Log.e(TAG, "student = " + iStudentInterface.getStudentById(2));
                    } catch (RemoteException e) {
                        e.printStackTrace();
                    }
                }

                @Override
                public void onServiceDisconnected(ComponentName name) {
                }
            };
            //intent要显性声明,负责会报错Service Intent must be explicit: Intent {}
            Intent service = new Intent(this, MyService.class);
            bindService(service, conn, BIND_AUTO_CREATE);
        } else {
            Log.e(TAG, "have bind service!");
        }
    }

    @Override
    protected void onDestroy() {
        unbindMyService();
        super.onDestroy();
    }

    private void unbindMyService() {
        if (null != conn){
            Log.e(TAG, "unbind service!");
            unbindService(conn);
            conn = null;
        } else {
            Log.e(TAG, "have unbind service!");
        }
    }
}

7) 各个文件的关系

8) 运行结果:

08-17 16:53:55.514 18381 18381 E MainActivity lyl123: bind service!
08-17 16:53:56.156 18381 18381 E MainActivity lyl123: iStudentInterface = com.lyl.dialservice.IStudentInterface$Stub$Proxy@1cf22e7
08-17 16:53:56.158 18432 18445 E MyService lyl123: getStudentById: 2
08-17 16:53:56.161 18432 18445 E Student lyl123: 打包 - writeToParcel : id = 2
08-17 16:53:56.166 18381 18381 E Student lyl123: 解包 - createFromParcel: arg0 = 2
08-17 16:53:56.166 18381 18381 E MainActivity lyl123: student = Student{id=2}

9) IStudentInterface.aidl文件生成的java文件

public interface IStudentInterface extends android.os.IInterface
{
    /** Local-side IPC implementation stub class. */
    public static abstract class Stub extends android.os.Binder implements com.lyl.dialservice.IStudentInterface
    {
        private static final java.lang.String DESCRIPTOR = "com.lyl.dialservice.IStudentInterface";
        /** Construct the stub at attach it to the interface. */
        public Stub()
        {
            this.attachInterface(this, DESCRIPTOR);
        }
        /**
         * Cast an IBinder object into an com.lyl.dialservice.IStudentInterface interface,
         * generating a proxy if needed.
         */
        public static com.lyl.dialservice.IStudentInterface asInterface(android.os.IBinder obj)
        {
            if ((obj==null)) {
                return null;
            }
            android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
            if (((iin!=null)&&(iin instanceof com.lyl.dialservice.IStudentInterface))) {
                return ((com.lyl.dialservice.IStudentInterface)iin);
            }
            return new com.lyl.dialservice.IStudentInterface.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_getStudentById:
                {
                    data.enforceInterface(DESCRIPTOR);
                    int _arg0;
                    _arg0 = data.readInt();
                    com.lyl.dialservice.Student _result = this.getStudentById(_arg0);
                    reply.writeNoException();
                    if ((_result!=null)) {
		                reply.writeInt(1);
		                _result.writeToParcel(reply, android.os.Parcelable.PARCELABLE_WRITE_RETURN_VALUE);
		            }
		            else {
		                reply.writeInt(0);
		            }
                    return true;
                }
            }
            return super.onTransact(code, data, reply, flags);
        }
    
        private static class Proxy implements com.lyl.dialservice.IStudentInterface
        {
            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 com.lyl.dialservice.Student getStudentById(int id) throws android.os.RemoteException
            {
                android.os.Parcel _data = android.os.Parcel.obtain();
                android.os.Parcel _reply = android.os.Parcel.obtain();
                com.lyl.dialservice.Student _result;
                try {
                    _data.writeInterfaceToken(DESCRIPTOR);
                    _data.writeInt(id);
                    mRemote.transact(Stub.TRANSACTION_getStudentById, _data, _reply, 0);
                    _reply.readException();
                    if ((0!=_reply.readInt())) {
                        _result = com.lyl.dialservice.Student.CREATOR.createFromParcel(_reply);
                    }
                    else {
                        _result = null;
                    }
                }
                finally {
                    _reply.recycle();
                    _data.recycle();
                }
                return _result;
            }
        }
        static final int TRANSACTION_getStudentById = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
    }
    /**
     * Demonstrates some basic types that you can use as parameters
     * and return values in AIDL.
     */
    public com.lyl.dialservice.Student getStudentById(int id) throws android.os.RemoteException;
}

3. IStudentInterface.java文件分析
1) 它包含了两个静态内部类:
    (a) 抽象类Stub,继承自Binder,其实就是一个Binder类,实现了IStudentInterface,但没有实现IStudentInterface的两个方法
    (b) Stub的内部代理类Proxy,实现了IStudentInterface,且实现了IStudentInterface的两个方法。

2) IStudentInterface.Stub.asInterface(service);
客户端onServiceConnected()方法绑定的IBinder类型的service,是服务端返回的mBinder,二者是同一个对象。

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

通过以上代码可以看出,服务端客户端若在同一个进程中,则返回服务端的Stub对象本身。
若在不同进程,则是进程间通信,会调用return new com.lyl.dialservice.IStudentInterface.Stub.Proxy(obj);        

Proxy(android.os.IBinder remote)
{
    mRemote = remote;
}

即会把mBinder赋值给mRemote。

4. 客户端调用getStudentById方法,是为了获取Student类型的对象。发起调用后会执行

mRemote.transact(Stub.TRANSACTION_getStudentById, _data, _reply, 0);

即调用transact方法发起RPC(远程过程调用)请求,此时当前线程会挂起;然后服务端的onTransact方法会被调用,直到RPC过程返回,当前线程继续执行。

@Override public com.lyl.dialservice.Student getStudentById(int id) throws android.os.RemoteException
{
    android.os.Parcel _data = android.os.Parcel.obtain();
    android.os.Parcel _reply = android.os.Parcel.obtain();
    com.lyl.dialservice.Student _result;
    try {
        _data.writeInterfaceToken(DESCRIPTOR);
        _data.writeInt(id);
        mRemote.transact(Stub.TRANSACTION_getStudentById, _data, _reply, 0);
        _reply.readException();
        if ((0!=_reply.readInt())) {
            _result = com.lyl.dialservice.Student.CREATOR.createFromParcel(_reply);
        }
        else {
            _result = null;
        }
    }
    finally {
        _reply.recycle();
        _data.recycle();
    }
    return _result;
}
@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_getStudentById:
        {
            data.enforceInterface(DESCRIPTOR);
            int _arg0;
            _arg0 = data.readInt();
            com.lyl.dialservice.Student _result = this.getStudentById(_arg0);
            reply.writeNoException();
            if ((_result!=null)) {
                reply.writeInt(1);
                _result.writeToParcel(reply, android.os.Parcelable.PARCELABLE_WRITE_RETURN_VALUE);
            }
            else {
                reply.writeInt(0);
            }
            return true;
        }
    }
    return super.onTransact(code, data, reply, flags);
}

其中getStudentById方法运行在客户端,onTransact方法运行在服务端的Binder线程池中。

在getStudentById方法中,首先声明两个Parcel对象,一个用于传递数据给服务端,一个用于接收服务端返回的数据。
其中Parcel类是一个容器,它主要用于存储序列化数据。可以把看成是一个流,依次往流里写数据,依次从流中读数据,并且写和读的顺序必须一致。

_data写入DESCRIPTOR和id后,作为参数传入transact方法,进入onTransact方法的case TRANSACTION_getStudentById中,
首先读处DESCRIPTOR和id, 并把id赋给_arg0,同时调用服务端mBinder的getStudentById方法,构建student对象,同时把_arg0(id)的值赋给其成员变量。

com.lyl.dialservice.Student _result = this.getStudentById(_arg0);
private IBinder mbinder = new IStudentInterface.Stub(){//MyService.java
    @Override
    public Student getStudentById(int id) throws RemoteException {
        Log.e("lyl", "getStudentById: " + id);
        return new Student(id);//此处可能要操作数据库,查询数据等 -- 此处的数据要返回给客户端
    }    
};
public Student(int id) {//Student.java
    this.id = id;
}    

构建student对象不为空时,则向reply中写入1,之后执行writeToParcel方法,即student的writeToParcel方法。此时再次向reply中写值(id写入)。

@Override
public void writeToParcel(Parcel parcel, int i) {
    Log.e("lyl", "service : 打包 - writeToParcel");
    parcel.writeInt(id);
}

服务端的onTransact方法执行完,远程过程调用返回,_reply先从数据流中读取数值1,而后执行

_result = com.lyl.dialservice.Student.CREATOR.createFromParcel(_reply);

即调用Student的CREATOR.createFromParcel方法。

public static final Creator<Student> CREATOR = new Creator<Student>() {//Student.java
    @Override
    public Student createFromParcel(Parcel in) {
        Log.e("lyl", "service : 解包 - createFromParcel");
        return new Student(in.readInt());
    }

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

_reply再次从数据流中读取id,构建Student对象,并把对象返回。
此时getStudentById方法获取对象成功!aidl流程分析也就分析完毕了。

5. 参考:

《Android开发艺术探索》

Android Binder机制完全解析

猜你喜欢

转载自blog.csdn.net/lyl0530/article/details/81809973
今日推荐