Android开发艺术探索读书笔记——进程间通信

 

1. 多进程使用场景

1) 应用某些模块因为特殊需求需要运行在单独进程中。如消息推送,使消息推送进程与应用进程能单独存活,消息推送进程不会因为应用程序进程crash而受影响。 
2) 为加大一个应用可使用的内存,需要多进程来获取多份内存空间。

2. 如何开启多进程

给四大组件(Activity、Service、Receiver、ContentProvider)在AndroidMainfest中指定Android:process属性指定。
如果进程以”:”开头的进程,代表应用的私有进程,其他应用的组件不可以和它跑在同一个进程中;
进程不以”:”开头的进程属于全局进程,其他应用可通过shareUID可以和它跑在同一个进程中。
若两个不同应用设置了相同的shareUID,它们之间想共享数据,还需要有相同的签名才可以。

3. 多进程会造成的问题

(1) 静态成员和单例失效,数据同步失败; 
Android会为每一个应用/每一个进程分配一个独立的虚拟机,不同虚拟机在内存分配上有不同的地址空间,这就导致不同虚拟机中访问同一个类对象会产生多个副本。 
(2) 线程同步机制失效; 
因为不同进程不是同一块内存,不同进程锁的不是同一对象。 
(3) SharedPreferences可靠性下降; 
SharedPreferences底层是通过读/写XML文件实现的,并发写可能会出问题,所以它不支持多个进程同时去执行写操作,否则会导致一定几率的数据丢失。 
(4) Application会创建多次; 
当一个组件跑在一个新进程中,系统会给它重新分配独立虚拟机,这其实就是启动一个应用的过程,故运行在不同进程中的组件属于不同的虚拟机和不同的Application。

4. 数据序列化

Intent和Binder传输数据时,或是对象持久化转存/通过网络传输给其他客户端时,需要使用Parcelable或Serializable将对象转换成可以传输的形式。 
(1)  Serializable接口 
Serializable是Java提供的一个序列化接口,它是一个空接口,想要某个类实现序列化,只需要相应类实现Serializable接口即可。Serializable是借助ObjectOutputStream和ObjectInputStream实现对象的序列化和反序列化 
一般在相应类实现Serializable类中还会定义一个final long型的serialVersionUID,不用它也能实现对象的序列化,它是用来辅助反序列化的。序列化时会把当前类的serialVersionUID写入序列化文件中,当反序列化系统会检测文件中的serialVersionUID,看它是否和当前类的serialVersionUID一致,如果一致说明序列化的类的版本和当前类的版本相同,这时可反序化成功;否则说明当前类和序列化的类相比发生了变换,如增加或减少某个成员变量,这时无法正常反序列化。 
(2)  Parcelable接口 
在序列化过程中需要实现的功能有: 
1) 序列化:由writeToParcel方法完成,最终通过Parcel中的一系列的write方法完成; 
2) 反序列化:由CREATOR完成,内部标识了如何创建序列化对象和数组,最终通过Parcel的一系列read方法来完成反序列化; 
3) 内容描述符:由describeContents方法来完成,几乎所有情况下该方法都返回0,仅当当前对象中存在文件描述符时返回1。 
(3)两者区别 
Serializable是Java中序列化接口,使用简单但开销大,序列和反序列化需要大量I/O操作; 
Parcelable是Android中特有的序列化接口,使用复杂但开销小,效率高,是Android推荐的序列化方法; 
Parcelable主要用在内存序列化上,如果将对象序列化到存储设备或将对象序列化后通过网络传输,过程会比较复杂,建议使用Serializable。

5. Binder

Binder是什么?
Binder是Android中的一个类,它继承了IBinder接口;
从IPC角度来说,Binder是Android中一种跨进程通信的方式;
Binder还可以理解为一种虚拟的物理设备,它的设备驱动是/dev/binder;
从AndroidFramework角度来说,Binder是ServiceManager连接各种Manager(ActivityManager, WindowManager)和相应ManagerService的桥梁;
从Android应用层来说,Binder是客户端和服务端进行通信的媒介,当bindService时,服务端会返回一个包含了服务端业务的Binder对象,通过这个Binder对象,客户端就可以获取服务端提供的服务或数据,这里的服务包括普通服务和基于AIDL的服务。

6.android中跨进程通信方式

6.1 结合Bundle使用Intent

在一个进程中启动另一个进程的Activity, Service, Receiver组件时,可以使用Bundle附加上需要传递的消息给远程进程,并通过Intent发送出去。

6.2 使用文件共享

两个进程通过读/写同一个文件来交换数据,还可以序列化一个对象到文件系统中,从另一个进程中恢复这个对象。它的实现原理是通过ObjectOutputStream将文件写入文件中,再通过ObjectInputSteam将文件恢复。通过文件共享的方式有一定局限性,如并发读/写,读出的内容可能不是最新的,并发写就可能导致数据混乱。因此尽量避免并发写这种操作,或考虑用线程同步来限制多个线程的写操作。文件共享适合在对数据要求不高的进程之间通信。 
SharedPreferences是Android提供的一种轻量级存储方案,它通过键值对方式存储数据,底层它是采用XML来存储键值对。由于系统对SharedPreferences的读写有一定的缓存策略,即在内存中会有一份SharedPreferences文件的缓存,在多进程模式下,系统对它的读/写就变得不可靠,当面对高并发读/写访问时,SharedPreferences会有很大几率会丢失数据。因此不建议在进程间通信中使用SharedPreferences。

6.3 使用Messenger

Messenager可以在不同进程中传递Message对象,在Message中放入我们要传递的数据。它的底层实现是AIDL,下面是Messenger的两个构造方法:

public Messenger(Handler target) {
        mTarget = target.getIMessenger();
    }
public Messenger(IBinder target) {
        mTarget = IMessenger.Stub.asInterface(target);
    }1
2
3
4
5
6


1
2
3
4
5
6

不管是IMessenger还是Stub.asInterface,这种使用方法都表明它的底层是AIDL,它一次只处理一个请求,不存在线程同步问题。 
实现步骤: 
(1) 服务端进程 
1) 定义一个Service用于客户端的绑定,建立一个Handler,在handleMessage里处理客户端发送过来的消息。

//用ServiceHandler接收并处理来自于客户端的消息
    private class ServiceHandler extends Handler {
        @Override
        public void handleMessage(Message msg) {
            if(msg.what == RECEIVE_MESSAGE_CODE){
                Bundle data = msg.getData();
                if(data != null){
                    String str = data.getString("msg");
                }
                //通过Message的replyTo获取到客户端自身的Messenger,
                //Service可以通过它向客户端发送消息
                clientMessenger = msg.replyTo;
                if(clientMessenger != null){
                    Message msgToClient = Message.obtain();
                    msgToClient.what = SEND_MESSAGE_CODE;
                    //可以通过Bundle发送跨进程的信息
                    Bundle bundle = new Bundle();
                    bundle.putString("msg", "你好,客户端,我是MyService");
                    msgToClient.setData(bundle);
                    try{
                        clientMessenger.send(msgToClient);
                    }catch (RemoteException e){
                        e.printStackTrace();
                        Log.e("DemoLog", "向客户端发送信息失败: " + e.getMessage());
                    }
                }
            }
        }
    }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


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

2) 通过Handler创建一个Messenger对象

//serviceMessenger是Service自身的Messenger,其内部指向了ServiceHandler的实例
    //客户端可以通过IBinder构建Service端的Messenger,从而向Service发送消息,
    //并由ServiceHandler接收并处理来自于客户端的消息
    private Messenger serviceMessenger = new Messenger(new ServiceHandler());1
2
3
4


1
2
3
4

3) 在Service的onBind中通过Messenger.getBinder()返回底层的Binder对象。

@Override
    public IBinder onBind(Intent intent) {
        Log.i("DemoLog", "MyServivce -> onBind");
        //获取Service自身Messenger所对应的IBinder,并将其发送共享给所有客户端
        return serviceMessenger.getBinder();
    }1
2
3
4
5
6


1
2
3
4
5
6

4) 注册服务,让其运行在单独进程中

<service
    android:name=".MyService"
    android:enabled="true"
    android:process=":remote" >
</service>1
2
3
4
5


1
2
3
4
5

(2) 客户端进程 
1) 绑定服务端的Service

 private ServiceConnection conn = new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName name, IBinder binder) {
            //客户端与Service建立连接
            Log.i("DemoLog", "客户端 onServiceConnected");

            //我们可以通过从Service的onBind方法中返回的IBinder初始化一个指向Service端的Messenger
            serviceMessenger = new Messenger(binder);
            isBound = true;

            Message msg = Message.obtain();
            msg.what = SEND_MESSAGE_CODE;

            //此处跨进程Message通信不能将msg.obj设置为non-Parcelable的对象,应该使用Bundle
            //msg.obj = "你好,MyService,我是客户端";
            Bundle data = new Bundle();
            data.putString("msg", "你好,MyService,我是客户端");
            msg.setData(data);

            //需要将Message的replyTo设置为客户端的clientMessenger,
            //以便Service可以通过它向客户端发送消息
            msg.replyTo = clientMessenger;
            try {
                Log.i("DemoLog", "客户端向service发送信息");
                serviceMessenger.send(msg);
            } catch (RemoteException e) {
                e.printStackTrace();
                Log.i("DemoLog", "客户端向service发送消息失败: " + e.getMessage());
            }
        }

        @Override
        public void onServiceDisconnected(ComponentName name) {
            //客户端与Service失去连接
            serviceMessenger = null;
            isBound = false;
            Log.i("DemoLog", "客户端 onServiceDisconnected");
        }
    };

    Intent intent = new Intent();            
    ComponentName componentName = new ComponentName(packageName, serviceNmae);
    intent.setComponent(componentName);
    try{
  

50000+
5万行代码练就真实本领
17年
创办于2008年老牌培训机构
1000+
合作企业
98%
就业率

联系我们

电话咨询

0532-85025005

扫码添加微信