Android IPC 相关

Android 上的多进程情景

Android 中每个应用的进程都 fork 自 Zygote 进程, Zygote 进程在启动时自己会创建一个虚拟机,从而也就让每个应用拥有了自己的虚拟机。 当应用涉及多进程时,想当于启动了多个虚拟机,在单进程情况下的一些情景将会失效:

  • 静态变量: 由于静态变量是位于虚拟机内存的方法区,每个虚拟机彼此独立,多个进程访问的也就不会是同一个变量
  • 单利模式:单利也是基于一个虚拟机的,多个虚拟机将失效
  • 线程同步机制:同步锁基于同一进程
  • SharedPerfrence 不再可靠: SP 内部是以读写 xml 文件的方式,且只有线程锁。多进程环境下,文件同时读写将不可靠。
  • Application 类的 onCreate 会在每个进程启动时被调用: 在含有多进程的应用里,需要在 Application 类的 onCreate 里区分当前的进程,避免多个进程都执行了重复的代码。

如何开启多进程

AndroidManifest 中,给四大组件设置单独的 android:process 属性。 这种方式,有两种情况:

  1. 当前应用私有的进程,声明 process 属性带 : 号,其他应用的组件不能运行在该进程中。

    <activity android:name=".AbcActivity" android:process=":remote"/>
    
  2. 不带 : 号的全局进程。其他应用可以通过 SharedUID 方式跑在该进程中。

    <activity android:name=".AbcActivity" android:process="remote"/>
    

序列化和反序列化

Java Serializable 接口

让对象支持序列化,只需实现 Serializable接口,并声明一个serialVersionUIDserialVersionUID不是必需的,但是如果不声明会对反序列化过程产生影响。序列化后的数据中的serialVersionUID只有和当前类的serialVersionUID相同时,才能被正常地反序列化。

  • 静态属性属于类,不会被序列化
  • transitent 声明的属性不会被序列化

Android Parcelable 接口

Android 提供的序列化接口,相比 Serializable 性能更好,因为它主要用于在内存上序列化和反序列化。实现方式就是类实现 Parcelable 接口,并实现 createFromParcelwriteToParcel 等方法。

Binder

  • 从 IPC 角度,Binder 是 Android 的一种跨进程通信方式
  • 从 Android Framework 角度,Binder 是 ServiceManager 连接各种 Manager 和相应 ManagerService 的桥梁
  • 从 Android 应用层,Binder 是客户端和服务端进行通信的媒介,当 bindService 时,服务端会返回一个包含了服务端业务调用的 Binder 对象,通过这个 Binder 对象,客户端就可以像调用客户端本地方法一样调用服务端的方法。
  • 普通 Service 中的 Binder 不涉及进程间通信
  • 多进程的应用会较多涉及 Binder,Binder 也主要用在 Service 上

Android 提供了 AIDL 描述语言来方便开发者创建 Binder 类,也可以自己手写实现 Binder 类。

模拟一个数据类 User,并实现 Parcelable 接口,使用跨进程的方式,从远程 Service 中获取 User

public class User implements Parcelable {
    String name;
    int age;

    protected User(Parcel in) {
        name = in.readString();
        age = in.readInt();
    }

    public static final Creator<User> CREATOR = new Creator<User>() {
        @Override
        public User createFromParcel(Parcel in) {
            // 从 Parcel 中构造 User 对象
            return new User(in);
        }

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

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

    @Override
    public void writeToParcel(Parcel dest, int flags) {
        // 将属性写入 Parcel 对象中
        dest.writeString(name);
        dest.writeInt(age);
    }
}

使用 AIDL,自动生成 Binder 类

  1. 创建 User.aidl

    // User.aidl
    package com.jy.app2;
    
    parcelable User;
    
  2. 创建 IUserManagerInterface.aidl

    // IUserManagerInterface.aidl
    package com.jy.app2;
    
    import com.jy.app2.User;
    
    interface IUserManagerInterface {
    User getUser();
    void setUser(in User user);
    }
    

需要注意,User.aidl 的文件名和内部声明的 pracelable 都要和 Java 类 User一致,且 User.aidl 的包路径也要和 Java User类一致。

  1. 编译生成的 IUserManagerInterface Java 接口

    package com.jy.app2;
    
    // 所有可以在 Binder 中传输的接口,都需要继承 IInterface
    public interface IUserManagerInterface extends android.os.IInterface {
    
    // 一个 Binder 类,当客户端和服务端在同一个进程时不会走 onTransact 过程
    // 当客户端和服务端不在同一个进程时,会走 onTransact 过程,并且逻辑有内部类 Proxy 完成
    public static abstract class Stub extends android.os.Binder implements com.jy.app2.IUserManagerInterface {
        // Binder 的唯一标识
        private static final java.lang.String DESCRIPTOR = "com.jy.app2.IUserManagerInterface";
    
        public Stub() {
            this.attachInterface(this, DESCRIPTOR);
        }
    
        /*
         * 用于将服务端的 Binder 对象,转换成客户端所需的 IInterface 接口对象(使用 AIDL 生成的)。
         * 这个过程是区分进程的:如果客户端和服务端在同一个进程,此方法返回服务端的 Stub 对象本身;否则
         * 就返回 Stub 的内部类 Proxy 对象
        */
        public static com.jy.app2.IUserManagerInterface asInterface(android.os.IBinder obj) {
            if ((obj == null)) {
                return null;
            }
            android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
            if (((iin != null) && (iin instanceof com.jy.app2.IUserManagerInterface))) {
                return ((com.jy.app2.IUserManagerInterface) iin);
            }
            return new com.jy.app2.IUserManagerInterface.Stub.Proxy(obj);
        }
    
        // 此方法返回当前的 Binder 对象
        @Override
        public android.os.IBinder asBinder() {
            return this;
        }
    
        // 此方法运行在服务端的 Binder 线程池中,当客户端发起跨进程请求时,远程请求会通过系统底层封装之后,交给该方法执行
        // 该方法中会服务端根据 code 参数确定应该执行的目标方法,接着从 data 中取出目标方法需要的参数(如果目标参数需要传入参数),目标方法执行完成后,将结果写入 reply 中(如果目标方法有返回值)。
        // 如果该方法返回 false,代表客户端请求失败。所以可以在这里面加自己的业务,比如权限验证,当不通过时直接返回 false
        @Override
        public boolean onTransact(int code, android.os.Parcel data, android.os.Parcel reply, int flags) throws android.os.RemoteException {
            java.lang.String descriptor = DESCRIPTOR;
            switch (code) {
                case INTERFACE_TRANSACTION: {
                    reply.writeString(descriptor);
                    return true;
                }
                case TRANSACTION_getUser: {
                    data.enforceInterface(descriptor);
                    com.jy.app2.User _result = this.getUser();
                    reply.writeNoException();
                    if ((_result != null)) {
                        reply.writeInt(1);
                        _result.writeToParcel(reply, android.os.Parcelable.PARCELABLE_WRITE_RETURN_VALUE);
                    } else {
                        reply.writeInt(0);
                    }
                    return true;
                }
                case TRANSACTION_setUser: {
                    data.enforceInterface(descriptor);
                    com.jy.app2.User _arg0;
                    if ((0 != data.readInt())) {
                        _arg0 = com.jy.app2.User.CREATOR.createFromParcel(data);
                    } else {
                        _arg0 = null;
                    }
                    this.setUser(_arg0);
                    reply.writeNoException();
                    return true;
                }
                default: {
                    return super.onTransact(code, data, reply, flags);
                }
            }
        }
    
        private static class Proxy implements com.jy.app2.IUserManagerInterface {
            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;
            }
    
            // 此方法运行在客户端,当客户端远程调用此方法时,先创建输入和输出 Parcel _data 和 _reply
            // 然后调用 transact 发起 RPC 远程调用,同时线程挂起;然后服务端的 onTransact 被调用,直到
            // RPC 结果返回,客户端线程继续运行,并从 _reply 中取出 RPC 的返回结果,最后返回结果
            @Override
            public com.jy.app2.User getUser() throws android.os.RemoteException {
                android.os.Parcel _data = android.os.Parcel.obtain();
                android.os.Parcel _reply = android.os.Parcel.obtain();
                com.jy.app2.User _result;
                try {
                    _data.writeInterfaceToken(DESCRIPTOR);
                    mRemote.transact(Stub.TRANSACTION_getUser, _data, _reply, 0);
                    _reply.readException();
                    if ((0 != _reply.readInt())) {
                        _result = com.jy.app2.User.CREATOR.createFromParcel(_reply);
                    } else {
                        _result = null;
                    }
                } finally {
                    _reply.recycle();
                    _data.recycle();
                }
                return _result;
            }
    
            // 此方法同上面,只是多了将参数写入到 _data ,由于该方法没有返回值,所以不会从 _reply 中取结果
            @Override
            public void setUser(com.jy.app2.User user) throws android.os.RemoteException {
                android.os.Parcel _data = android.os.Parcel.obtain();
                android.os.Parcel _reply = android.os.Parcel.obtain();
                try {
                    _data.writeInterfaceToken(DESCRIPTOR);
                    if ((user != null)) {
                        _data.writeInt(1);
                        user.writeToParcel(_data, 0);
                    } else {
                        _data.writeInt(0);
                    }
                    mRemote.transact(Stub.TRANSACTION_setUser, _data, _reply, 0);
                    _reply.readException();
                } finally {
                    _reply.recycle();
                    _data.recycle();
                }
            }
        }
    
        //两个整形,用于标识客户端请求的方法
        static final int TRANSACTION_getUser = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
        static final int TRANSACTION_setUser = (android.os.IBinder.FIRST_CALL_TRANSACTION + 1);
    }
    
    // 服务端 Binder 需要实现的方法
    public com.jy.app2.User getUser() throws android.os.RemoteException;
    public void setUser(com.jy.app2.User user) throws android.os.RemoteException;
    }
    

手写实现 Binder 类

先定义一个继承了IInterface的接口

public interface IUserManagerInterface extends IInterface {

    public String DESCRIPTION = "com.jy.app2.IUserManagerInterface";

    public void setUser(String token, User user) throws RemoteException;

    public User getUser(String token) throws RemoteException;

    public int Method_setUser = IBinder.FIRST_CALL_TRANSACTION;
    public int Method_getUser = IBinder.FIRST_CALL_TRANSACTION + 1;
}

实现接口,并继承Binder

package com.jy.app2;

import android.os.*;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.text.TextUtils;

abstract class IUserManagerInterfaceImpl extends Binder implements IUserManagerInterface {

    IUserManagerInterfaceImpl() {
        attachInterface(this, DESCRIPTION);
    }
    
    @Override
    public IBinder asBinder() {
        return this;
    }

    // 当不是跨进程时,直接返回服务端本身的 Binder
    // 当是跨进程时,返回代理对象
    public static IUserManagerInterface asInterface(IBinder object) {
        if (object == null) {
            return null;
        }
        IInterface iin = object.queryLocalInterface(DESCRIPTION);
        if ((iin != null) && (iin instanceof IUserManagerInterface)) {
            return (IUserManagerInterface) iin;
        }
        return new Proxy(object);
    }

    @Override
    protected boolean onTransact(int code, @NonNull Parcel data, @Nullable Parcel reply, int flags) throws RemoteException {

        switch (code) {
            case Method_getUser:
                if (!auth(data)) {
                    return false;
                }
                User user = this.getUser(data.readString());
                reply.writeNoException();
                if (user != null) {
                    reply.writeInt(1);
                    user.writeToParcel(reply, Parcelable.PARCELABLE_WRITE_RETURN_VALUE);
                } else {
                    reply.writeInt(0);
                }
                return true;
            case Method_setUser:
                if (!auth(data)) {
                    return false;
                }
                String token = data.readString();
                User arg1 = null;
                if ((0 != data.readInt())) {
                    arg1 = User.CREATOR.createFromParcel(data);
                }
                this.setUser(token, arg1);
                reply.writeNoException();
                return true;
        }
        return super.onTransact(code, data, reply, flags);
    }

    private boolean auth(Parcel data) {
        data.enforceInterface(DESCRIPTION);
        // 模拟权限验证
        String token = data.readString();
        return !TextUtils.equals(token, "123");
    }


    static class Proxy implements IUserManagerInterface {

        IBinder remote;

        Proxy(IBinder remote) {
            this.remote = remote;
        }

        @Override
        public User getUser(String token) throws RemoteException {
            Parcel data = Parcel.obtain();
            Parcel reply = Parcel.obtain();
            User result = null;
            try {
                data.writeInterfaceToken(DESCRIPTION);
                data.writeString(token);
                boolean success = remote.transact(IUserManagerInterface.Method_getUser, data, reply, 0);
                reply.readException();
                if ((0 != reply.readInt())) {
                    result = User.CREATOR.createFromParcel(reply);
                }
            } finally {
                data.recycle();
                reply.recycle();
            }
            return result;
        }

        @Override
        public void setUser(String token, User user) throws RemoteException {
            Parcel data = Parcel.obtain();
            Parcel reply = Parcel.obtain();
            try {
                data.writeInterfaceToken(DESCRIPTION);
                data.writeString(token);
                if (user != null) {
                    data.writeInt(1);
                    user.writeToParcel(data, 0);
                } else {
                    data.writeInt(0);
                }
                boolean success = remote.transact(IUserManagerInterface.Method_getUser, data, reply, 0);
                reply.readException();
            } finally {
                data.recycle();
                reply.recycle();
            }
        }

        @Override
        public IBinder asBinder() {
            return remote;
        }
    }
}

分析 Binder 的调用过程

创建一个 Service

public class UserService extends Service {
    User mUser;

    public UserService() {
        mUser = new User();
        mUser.name = "Stefan";
        mUser.age = 13;
    }

    @Nullable
    @Override
    public IBinder onBind(Intent intent) {
        return new IUserManagerInterfaceImpl() {
            @Override
            public void setUser(String token, User user) throws RemoteException {
                mUser = user;
            }

            @Override
            public User getUser(String token) throws RemoteException {
                return mUser;
            }
        };
    }
}

然后bindService

IUserManagerInterface userManagerInterface;
ServiceConnection connection = new ServiceConnection() {
    @Override
    public void onServiceConnected(ComponentName name, IBinder service) {
        userManagerInterface = IUserManagerInterfaceImpl.asInterface(service);
        onServiceConnect();
    }
    @Override
    public void onServiceDisconnected(ComponentName name) {}
    };
    bindService(new Intent(this, UserService.class), connection, BIND_AUTO_CREATE);
}

private void onServiceConnect() {
	try {
    	User user = userManagerInterface.getUser("token_1234");
    } catch (RemoteException e) {
        e.printStackTrace();
    }
}

bindService成功之后,会首先调用asInterface方法获得Binder对象,所以在 asInterface方法处断点看下

不跨进程分析

Service 不单独声明 process属性

binder-local

可以看到,调用直接返回了 queryLocalInterface返回的IInterface对象,该对象其实就是在上面 Service 的onBind方法中创建的 IUserManagerInterfaceImpl匿名内部类。客户端调用的直接是那个onBind返回的对象的方法。

跨进程分析

Service 单独声明 process属性

binder-remote

这时候就返回了代理对象,然后接着就是调用getUser方法。

binder-proxy

走到了 Proxy 的 getUser,这是还没有发生跨进程的调用,下一行remote.transact就会发起跨进程请求,将我们请求的方法编号、输入数据、输出数据作为参数传入,接下来的调用就会走到另一个进程里,同时客户端这边线程会被挂起等待。Debug 也需要 attach 到另一个进程上。onTransact将执行在服务端进程上:

binder-onTransact

onTransact里根据方法编号调用对应的方法,这里的this是在 Service 的 onBind中返回的对象。在这里会将结果写到 replay中,然后结束,程序执行切换会客户端进程。

proxy-getUser

Proxy 继续执行,从 reply中取出结果,最后返回。

总结

  • Proxy 中的逻辑是运行在客户端进程的,且默认在主线程,需要注意阻塞问题,所以bindService成功之后,可以通过单开线程来做 IPC 调用
  • onTransact 运行在服务端进程中,且运行在 Binder 线程池中,Binder 中的逻辑无论耗时都应该使用同步实现