当前位置:首页 > 未命名 > 正文内容

Android中的IPC机制

u3blog8年前 (2016-04-20)未命名232

什么是IPC

IPC是跨进程通信的简称,全称太长了,不想写了。由于Android中分为了UI线程和其他线程,而且对UI的操作只能在UI线程,为了防止卡顿,我们需要把一些操作放到其他线程处理。那么处理完毕之后如何通知UI线程做出处理呢?这就要靠IPC机制了。 当然,IPC不止在Android中有运用,很多地方也有使用。

Android中的多进程模式

开启多进程模式很简单,只需要在menifest文件中添加android:process属性即可,具体情况网上有很多例子,不再赘述。 但是开启了多进程之后,很多单进程时的问题就浮现了出来,比如,数据共享的时候同步会出问题,作者对这些情况做了总结,大致有如下几种 1.静态成员和单例模式失效 2.线程同步机制失效 3.SharedPreferences的可靠性下降 4.Application会多次创建

Android中的序列化

在进入IPC的总结之前,先来总结一个必不可少的东西,序列化,我们熟悉的是继承Serializable接口,来实现对象的可序列化过程,这种方式很简单,基本不用做多余操作,最多设置一下ID以反序列化之后的比较之用。 Android也提供了一套序列化机制——Parcelable接口,要实现这个接口就需要复杂一些的方法,需要实现几个必备的方法,也很简单,不详细说明。

两种序列化机制的区别

那么,这两种机制有何不同呢?首先,Serializable接口的序列化方式是java提供的,开销比较大,但是胜在简单。 而Parcelable的序列化方式开销比较小,但是使用起来稍显复杂,在应用内部的情况下,应该优先选择使用。 如果是在网络传输的过程中,建议使用Serializable方式,首先是简单,其次是不会因为Android系统的版本而造成差异。

Binder概括

首先,Binder是Android中的一个类,实现了IBinder接口,是链接各种manager和managerservice的桥梁,是客户端与服务端通信的桥梁。 那么,Binder到底是如何运作的呢?我们来看一个书上的例子,一个系统生成的AIDL文件的结构
public class BookManagerImpl extends Binder implements IBookManager {

    /** Construct the stub at attach it to the interface. */
    public BookManagerImpl() {
        this.attachInterface(this, DESCRIPTOR);
    }

    /**
     * Cast an IBinder object into an IBookManager interface, generating a proxy
     * if needed.
     */
    public static IBookManager asInterface(IBinder obj) {
        if ((obj == null)) {
            return null;
        }
        android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
        if (((iin != null) && (iin instanceof IBookManager))) {
            return ((IBookManager) iin);
        }
        return new BookManagerImpl.Proxy(obj);
    }

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

    @Override
    public boolean onTransact(int code, Parcel data, Parcel reply, int flags)
            throws RemoteException {
        switch (code) {
        case INTERFACE_TRANSACTION: {
            reply.writeString(DESCRIPTOR);
            return true;
        }
        case TRANSACTION_getBookList: {
            data.enforceInterface(DESCRIPTOR);
            List<Book> result = this.getBookList();
            reply.writeNoException();
            reply.writeTypedList(result);
            return true;
        }
        case TRANSACTION_addBook: {
            data.enforceInterface(DESCRIPTOR);
            Book arg0;
            if ((0 != data.readInt())) {
                arg0 = Book.CREATOR.createFromParcel(data);
            } else {
                arg0 = null;
            }
            this.addBook(arg0);
            reply.writeNoException();
            return true;
        }
        }
        return super.onTransact(code, data, reply, flags);
    }

    @Override
    public List<Book> getBookList() throws RemoteException {
        // TODO 待实现
        return null;
    }

    @Override
    public void addBook(Book book) throws RemoteException {
        // TODO 待实现
    }

    private static class Proxy implements IBookManager {
        private IBinder mRemote;

        Proxy(IBinder remote) {
            mRemote = remote;
        }

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

        public java.lang.String getInterfaceDescriptor() {
            return DESCRIPTOR;
        }

        @Override
        public List<Book> getBookList() throws RemoteException {
            Parcel data = Parcel.obtain();
            Parcel reply = Parcel.obtain();
            List<Book> result;
            try {
                data.writeInterfaceToken(DESCRIPTOR);
                mRemote.transact(TRANSACTION_getBookList, data, reply, 0);
                reply.readException();
                result = reply.createTypedArrayList(Book.CREATOR);
            } finally {
                reply.recycle();
                data.recycle();
            }
            return result;
        }

        @Override
        public void addBook(Book book) throws RemoteException {
            Parcel data = Parcel.obtain();
            Parcel reply = Parcel.obtain();
            try {
                data.writeInterfaceToken(DESCRIPTOR);
                if ((book != null)) {
                    data.writeInt(1);
                    book.writeToParcel(data, 0);
                } else {
                    data.writeInt(0);
                }
                mRemote.transact(TRANSACTION_addBook, data, reply, 0);
                reply.readException();
            } finally {
                reply.recycle();
                data.recycle();
            }
        }
    }

}
这个例子是一个手动实现的Binder,整个文件看起来非常的长而无须,我们不妨跟随作者的脚步一点点来分析它 第一部分,这是一个叫BookManagerImpl的类,继承了Binder并实现了IBookManager接口 第二部分,其内部有一个内部类,private static class Proxy implements IBookManager同样也实现了IBookManager接口

BookManagerImpl的各个方法

1.public static IBookManager asInterface(IBinder obj) 用于转化服务端的Binder对象为客户端的AIDL接口,区分进程,如果是跨进程,返回的是proxy对象,否则的话返回stud对象本身 2.public IBinder asBinder() 返回当前的Binder对象 3.public boolean onTransact(int code, Parcel data, Parcel reply, int flags) 运行在Binder线程池中,根据code和参数来执行不同的方法,参数从data中获取,然后把结果写入reply中,如果返回false,客户端的请求会失败。 4.public List<Book> getBookList() 与 public void addBook(Book book) throws RemoteException 实现了IBookManager接口中的方法

内部类Proxy的各个方法

大致与BookManagerImpl相同,不过内部的getBook和addBook是通过RPC来实现的,这些方法都运行在客户端,当进行RPC时会被挂起,所以不能在主线程操作,当RPC结束时,会读取结果并写入。 到了这里,Binder的工作流程就大概介绍完了,是不是看起来很复杂,仔细分析起来却很简单呢,有了Binder,我们就可以继续分析接下来的形形色色的IPC方式了。

Android中的几种IPC机制概览

首先说一下Android中的几种常见的IPC机制,Binder是android自己独特的一套机制,在系统层级经常被用到,很多IPC方式都会用到它,上面已经做过了介绍。 android中IPC的方式大概有以下几种 1.使用Bundle 最常见的IPC方式,在多个场合都被使用 2.文件共享 通过对一个文件的读写来进行通信的方式 3.使用Messager 相信大家都很熟悉,messager和handler的组合使用来进行IPC 4.使用AIDL android为我们提供的一种Binder的简化使用方式,实现容易 5.ContentProvider 是Android中专门用于对外部提供数据访问的方式,也是唯一方式,例如读取联系人 6.使用Socket 把跨进程通信当做网络通信来处理,也可以实现IPC的效果 由于1,2,6的方式很简单也很常见,这里就不详细描述了,下面来重点总结一下3,4,5的Android中独特的IPC方式

Messager方式的IPC

顾名思义,使用Message对象来进行IPC,其底层是一个AIDL,这是对其的封装,每次只会处理一条Messager消息,所以不存在并发的问题

服务端

创建一个Service,同时创建一个Handler并通过它创建一个Messager对象,并在onbind将其的binder返回即可

客户端

使用bindService绑定服务,并在ServiceConnection对象的哦你ServiceConnected方法中把返回的IBinder对象转化为Messager对象,在建立Message对象,加入数据,通过Messager对象的send方法进行发送 如果要服务端接收到消息后有回复的话,通过msg的replyTo来获取一个Messager对象,然后和客户端发送的过程是一样的 而客户端,需要一个接受消息的Messager和Handler,还需要在发消息给服务端的时候把服务端接收回复的Messager通过Message的replyTo发回去,这样服务端才能拿到它,进行收到消息之后的回复。

AIDL方式的IPC

服务端

首先定义AIDL文件,其实就是一些特殊格式的接口,不过参数有所限制,需要是可序列化对象或者几个限定的类型之一 其次,实现远程服务端的Service,主要是实现系统生成的XXXManager.stub()内部类中的AIDL接口中对应的方法

其他杂项

RemoteCallbackList类,可以实现跨进程删除添加listener,对于做观察者模式开发有帮助,不过使用方式比较特殊,在使用前必须beginBroadcast使用后需要finishBroadcast。 耗时方法不要在UI线程调用 还需要注意断开连接之后的重连 以及访问的权限认证,可以在onbind方法或者ontransact方法中进行。

客户端

使用bindService绑定服务,并设定绑定成功后的ServicConnection类,将返回的Service转换为AIDL类型,然后就可以调用其中的方法了·

ContentProvider方式的IPC

该方式底层依然是使用的Binder,不过经过系统的封装,更加简单易用

创建FfR

使用非常简单,新建一个类,继承ContentProvider类并实现其必须实现的几个方法即可,具体的内部实现可以自己定义 在menifest文件中申明为provider,并添加android:authorities属性,android:permission属性即可

使用

通过getContentResolver.methodname(params)来调用即可,当然,如果需要对应权限,要申请,methodname对应于几个已经实现的方法,参数表中比较重要的一个是uri,对应android:authorities的值

不同IPC方式的比较

Bundle 简单易用,但是只能传输支持的数据类型,可以用于四大组件之间传输数据 文件共享 简单易用,但是不适合高并发,适用无并发,实时性不高的场景 AIDL 功能强大,支持一对多并发,有些复杂,需要处理好线程同步,用于一对多有远程过程调用的情况 Messager 功能一般,但是不能处理高并发,不支持RPC,只能传输Bundle支持的类型,用于底并发情况,无RPC情况 ContentProvider 数据源访问方面强大,支持一对多并发,可以实现自定义操作,主要提供CRUD操作,用于一对多的进程间数据共享 Socket 功能强大,支持一对多实时通信,不支持直接RPC,细节繁琐,用于网络数据交换场景

扫描二维码推送至手机访问。

版权声明:本文由u3blog发布,如需转载请注明出处。

本文链接:https://u3blog.xyz/?id=336

分享给朋友:

发表评论

访客

◎欢迎参与讨论,请在这里发表您的看法和观点。