This page looks best with JavaScript enabled

Android IPC 相关

 ·  ☕ 8 min read

Android 上的多进程情景

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

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

如何开启多进程

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

  1. 当前应用私有的进程,声明 process 属性带 : 号,其他应用的组件不能运行在该进程中。
1
<activity android:name=".AbcActivity" android:process=":remote"/>
  1. 不带 : 号的全局进程。其他应用可以通过 SharedUID 方式跑在该进程中。
1
<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

 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
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;
  1. 创建 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 接口
  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
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
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的接口

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
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

  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
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
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

 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
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

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
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 中的逻辑无论耗时都应该使用同步实现
Support the author with
alipay QR Code
wechat QR Code

Yang
WRITTEN BY
Yang
Developer