1. 开启多进程的方法

Android 应用启动时会自动创建一个新的进程, 其进程名默认为包名, 为四大组件(Activity , Service, ContentProvider, BroadcastReceiver)启动一个新的进程, 最简单的是在Manifest.xml中为其配置android:process进程名称. 有两种写法.

一是指定进程的全称:android:process="info.ivicel.github.android_ipc.another_process", 此时进程为一个全局进程, 其他应用可以通过ShareUID方式和它跑在同一进程中

二是简写: android:process=":remote". 这种写法其完整的进程名为包名:remote, 并且此时进程为一个私有进程, 其他应用的组件不可以同时跑在该进程中

由于 Android 中为不同进程单独分配一个虚拟机来运行, 所以 Android 中不能通过共享内存来进行通信.

在 Android 中使用共享内存, 便会造成:

  1. 静态成员和单例模式完全失效
  2. 线程的同步机制完全失效, 通过共享内存来synchronized, 锁对象/锁全局类都不是同一个了
  3. SharedPreferences的可靠性下降. 这是因为SharedPreferences底层是通过xml文件的来实现的, 需要对文件的读写进行同步
  4. Application会多次创建. 多进程会为每一个进程启动一Application, 相当多次启动应用. 可以在ApplicationonCreate()里打印出进程id证实启动了多次应用

2. Android 中 IPC 的几种方式

在 Android 中的 IPC 大致有以下几种:

  • 通过Intent的附加extras来传递, 其本质是通过Bundle来实现的
  • 通过共享文件
  • 通过Binder, 其底层是通过AIDL来实现
  • 通过ContentProvider
  • 通过网络Socket
  • 直接使用AIDL
  • 通过Messenger, 注意这不是消息载体Message
  1. 使用 Bundle

    Bundle的使用很是简单, 其实现了Pacelable接口, 可以直接传递各种可序列化的数据.

    这种方式虽然简单易用, 但如果需要传输的数据不支持Bundle, 那只能通过其他绕路方式. 比如需要在 A 进程中计算出某个结果, 然后启动进程 B, 同时把结果传给 B. 但计算结果不支持传输. 可以启动一个 B 进程里的后台 Service, 在其中计算出结果, 再传到前台 B 进程了. Service 和 B 是同一个进程.

  2. 使用共享文件

    可以在进程A中写入文件, 在进程B中读出文件内容, 要注意的是读出的对象和写入时的对象其内容数据虽然一样, 但本质上是两个对象. 并且使用共享文件进行通信时, 如果要求的并发量过高, 其同步就越困难, 有可能出并发读/写时数据不一致的情况. 其适合使用在对数据同步要求不高的进程之间的通信. SharedPreferences其底层的实现也是一个.xml文件, 但是系统对其读/写时, 会维护一个在内存里的缓存, 这使得多进程模式下对SharedPreferences的读写非常不可靠. 所以不要使用其行 IPC

  3. 使用 AIDL 通信

    在 Android 中使用 AIDL 进行 IPC

  4. 使用 Messenger

    Messenger 是对 AIDL 的封装, 使用起来更加的方便. 服务端每一次只处理一个请求, 使用的 MessageQueue 队列, 这样在服务端可以不用考虑并发的问题.

    4.1 服务端

    由于是对 AIDL 的封装, 所以服务端也是创建一个 Service, 创建一个 Messenger 对象和一个 Handler 对象,

    Handler 处理客户端发送过来的数据. Service 的 Binder 对象可由 Messenger 对象返回.

    4.2 客户端

    客户端通过 bindService() 拿到服务端的代理. 通信的数据由 Message 类来封装. 这个代理对象传送数据的方向为 client -> server, 只能单向传输.

    如果客户端需要服务器传回数据, 只能在客户端创建一个 Messenger 对象和 Handler 对象, 并通过Message.replyTo 将这个对象传给服务器. 这样 server -> client 就可以传送数据了.

    Messenger 类是 final 的, 不可能通过继承 Messenger 来重写传送数据方式

    Messenger机制

 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
// server
public MessengerService extends Service {
    private static final String TAG = "MessengerService";

    static class MessengerHandler extends Handler {
     	@Override
        public void handleMessage(Message msg) {
         	switch (msg.what) {
                case Constants.MSG_HELLO_FROM_CLIENT:
                    Log.d(TAG, "Hello from client: " +
                          msg.getData().getString("msg"));
                    break;

                default:
                    super.handleMessage(msg);
            }
        }
    }

 	private Messenger mMessenger = new Messenger(new MessengerHandler());


    @Override
    public IBinder onBind(Intent intent) {
     	return mMessenger.getBinder();
    }
}


// client
public MessengerActivity extends AppCompatActivity {
 	private staitc final String TAG = "MessengerActivity";

    private Messenger mMessenger;

    private SerivceConnection connection = new ServiceConnection() {
     	@Override
        public void onServiceConnected(ComponentName name, IBinder service) {
         	mMessenger = new Messenger(service);
            sendHelloToServer();
        }

        @Override
        public void onServiceDisconnected(ComponentName name) {}
    }

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        Intent intent = new Intent(this, MessengerService.class);
        bindService(intent, connection, Context.BIND_AUTO_CREATE);
    }

    private void sendHelloToServer() {
     	Message msg = Message.obtain(null, Constants.MSG_HELLO_FROM_CLIENT);
        Bundle data = new Bundle();
        data.putString("msg", "this is hello from client.");
        msg.setData(data);
        try {
        	mMessenger.send(msg);
        } catch (RemoteException e) {
         	e.printStackTrace();
        }
    }

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

在 «Android 开发艺术探索» 中提到:

Message 中的另一个字段 object 在同一个进程中是很实用的,但是在进程间通信的时候,在 Android 2.2 以前 object 字段不支持跨进程传输,即便是 2.2 以后,也仅仅是系统提供的实现了 Parcelable 接口的对象才能通过它来传输。这就意味着我们自定义的 Parcelable 对象是无法通过 object 字段来传输的

测试例子: GitHub

测试环境:

  • Android Studio 3.1
  • Gradle 4.4
  • android gradle tool 3.1
  • target sdk 27
  • Build tool 27.0.2

MessengerService 使用设置 android:process=":remote" 单独一个进程后, 会发生 java.lang.ClassNotFoundException, 猜测是因为找到不 ClassLoader

Reference:

  1. «Android 开发艺术探索»