Android中的IPC机制

作者: 斜杠时光 | 来源:发表于2017-01-15 02:00 被阅读173次

    IPC简介

    IPC是Inter-Process Communication的缩写,含义就是进程间通信或者跨进程通信,是指两个进程之间进行数据交换的过程。那么什么是进程,什么是线程,进程和线程是两个截然不同的概念。在操作系统中,线程是CPU调度的最小单元,同时线程是一种有限的系统资源。而进程指的一个执行单元,在PC和移动设备上指的是一个程序或者一个应用。一个进程可以包含多个线程,因此进程和线程是包含被包含的关系,最简单情况下,一个进程可以只有一个线程,即主线程,在Android里面也叫UI线程,在UI线程里才能操作界面元素。

    那么在Android中,有特色的进程间通信方式就是Binder了,通过Binder可以轻松实现进程间通信。除了Binder,Android还支持Socket,通过Socket也可以实现任意两个终端之间的通信,当然一个设备上的两个进程之间通过Socket通信自然也是可以的。

    说到IPC的使用场景就必须提到多进程,只有面对多进程这种场景下,才需要考虑进程间通信。所有运行在不同进程中的四大组件,只要它们之间需要通过内存来共享数据,都会共享失败,这也是多进程所带来的主要影响。正常情况下,四大组件中间不可能不通过一些中间层来共享数据,那么通过简单地指定进程名来开启多进程都会无法正确运行。一般来说,使用多进程会造成如下几方面的问题:

    • 静态成员和单例模式完全失效
    • 线程同步机制完全失效
    • SharedPreferences的可靠性下降
    • Application会多次创建

    为了解决这个问题,系统提供了很多跨进程通信方法,虽然说不能直接地共享内存,但是通过跨进程通信我们还是可以实现数据交互。实现跨进程通信的方式有很多,比如通过Intent来传递数据,共享文件SharedPreference,基于Binder的Messenger和AIDL以及Socket等。

    IPC基础概念介绍

    Serializable接口
    Serializable是Java提供的一个序列化接口,它是一个空接口,为对象标准的序列化和反序列化操作。使用Serializable来实现序列化相当简单,一句话即可。

    public class User implements Serializable {
      private static final long seriaVersionUID = 519067123721295773L
    }
    

    Parcelable接口

    Parcel内部包装了可序列化的数据,可以在Binder中自由传输,在序列化过程中需要实现的功能有序列化、反序列化和内容描述序列化功能有writeToParcel方法来完成,最终是通过Parcel中的一系列write方法来完成的。用法如下:

    public class MyParcelable implements Parcelable {
        // You can include parcel data types
        private int mData;
        private String mName;
        
        // We can also include child Parcelable objects. Assume MySubParcel is such a Parcelable:
        private MySubParcelable mInfo;
    
        // This is where you write the values you want to save to the `Parcel`.  
        // The `Parcel` class has methods defined to help you save all of your values.  
        // Note that there are only methods defined for simple values, lists, and other Parcelable objects.  
        // You may need to make several classes Parcelable to send the data you want.
        @Override
        public void writeToParcel(Parcel out, int flags) {
            out.writeInt(mData);
            out.writeString(mName);
            out.writeParcelable(mInfo, flags);
        }
    
        // Using the `in` variable, we can retrieve the values that 
        // we originally wrote into the `Parcel`.  This constructor is usually 
        // private so that only the `CREATOR` field can access.
        private MyParcelable(Parcel in) {
            mData = in.readInt();
            mName = in.readString();
            mInfo = in.readParcelable(MySubParcelable.class.getClassLoader());
        }
    
        public MyParcelable() {
            // Normal actions performed by class, since this is still a normal object!
        }
    
        // In the vast majority of cases you can simply return 0 for this.  
        // There are cases where you need to use the constant `CONTENTS_FILE_DESCRIPTOR`
        // But this is out of scope of this tutorial
        @Override
        public int describeContents() {
            return 0;
        }
    
        // After implementing the `Parcelable` interface, we need to create the 
        // `Parcelable.Creator<MyParcelable> CREATOR` constant for our class; 
        // Notice how it has our class specified as its type.  
        public static final Parcelable.Creator<MyParcelable> CREATOR
                = new Parcelable.Creator<MyParcelable>() {
    
            // This simply calls our new constructor (typically private) and 
            // passes along the unmarshalled `Parcel`, and then returns the new object!
            @Override
            public MyParcelable createFromParcel(Parcel in) {
                return new MyParcelable(in);
            }
    
            // We just need to copy this and change the type to match our class.
            @Override
            public MyParcelable[] newArray(int size) {
                return new MyParcelable[size];
            }
        };
    }
    

    Serializable和Parcelable区别
    Serializable是Java中的序列化接口,其使用起来简单但是开销很大,在序列化和反序列化过程中需要大量的I/O操作。而Parcelable是Android中的序列化方式,因此更适合用在Android平台上,它的缺点就是使用起来稍微麻烦点,但是它的效率很高。

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

    Binder工作机制

    Android中的IPC方式

    使用Bundler
    我们知道,四大组件中三大组件(activity、service、receiver)都是支持在Intent中传递Bundle数据的,由于Bundle实现了Parcelable接口,所以它可以方便地在不同的进程间传输。

    使用文件共享
    共享文件也是一种不错的进程间通信方式,两个进程间通过读/写同一个文件来交换数据,比如A进程把数据写入文件,B进程通过读取这个文件来获取数据。

    使用Messenger
    Messenger可以翻译为信使,顾名思义,通过它可以在不同进程中传递Message对象,在Message中放入我们需要传递的数据,就可以轻松地实现数据的进程间传递。Messenger是一种轻量级的IPC方案,它的底层实现是AIDL,实现Messenger有以下两个步骤,分为服务端进程和客户端进程。

    Messenger工作原理

    使用AIDL
    远程服务跨进程通信的一种方式。

    使用ContentProvider
    ContentProvider是Android中提供的专门用于不同应用间进行数据共享的方式,它的底层实现同样也是Binder。

    使用Socket
    Socket也称为“套接字”,是网络通信中的概念,它分为流式套接字和用户数据套接字两种,分别应于网络的传输控制层中的TCP和UDP协议。

    选用合适的IPC方式

    不同IPC方式比较

    相关文章

      网友评论

      • Knytt:请问文中的照片是哪本书?
        Knytt:@斜杠Allen 谢谢!
        斜杠时光:《Android开发艺术探索》

      本文标题:Android中的IPC机制

      本文链接:https://www.haomeiwen.com/subject/jwjybttx.html