IPC机制(上)

目录

  • Android IPC简介
  • Android中的多进程模式
  • IPC基础概念介绍

Android IPC简介

IPC:跨进程通信,是指两个进程之间进行数据交换的过程。

线程:CPU调度的最小单元,同时线程是一种有限的系统资源。

进程:一般指一个执行单位,在PC或者移动设别上指一个程序或者一个应用。

进程和线程的关系:一个进程可以包含多个线程,因此进程和线程是包含与被包含的关系。

使用多进程的场景:

  • 一个应用因为某些原因自生需要采用多进程模式来实现。
  • 当前应用需要向其他应用获取数据,由于是两个应用,所以必须采用跨进程的方式来获取所需的数据。

Android中的多进程模式

开启多进程模式

在Android中使用多进程只有一种方法,那就是给四大组件指定android:process,除此之外,没有其他办法。

        
            
                

                
            
        

        

        

上面的示例分别为SecondActivity和ThirdActivity指定了process属性,并且他们的属性值不同,这意味着当前应用又增加了两个进程。

虽然这两个新进程的名字相同,但由于命名方式的不同,还是有区别。进程以“:”开头的属于当前应用的私有进程,其他应用的组件不可以和它跑在同一个进程中,而进程名不以“:”开头的进程属于全局进程,其他应用通过ShareUID方式可以和它跑在同一进程中。

Android系统会为每个应用分配一个唯一的UID,具有相同UID的应用才能共享数据。这里要说明的是,两个应用通过ShareUID跑在同一个进程由是有要求的,需要这两个应用有相同的ShareUID并且签名相同才可以。

多进程模式的运行机制

Android为每一个应用都分配了一个独立的虚拟机,或者说为每个进程都分配一个独立的虚拟机,不同的虚拟机在内存上有不同的地址空间,这就导致在不同的虚拟机中访问同一个类的对象会产生多份副本。

在一个进程中修改同一个值只会影响当前进程,对其他进程不会造成任何影响。

一般来说,使用多进程会造成如下几方面的问题:

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

运行在同一个进程中的组件是属于同一个虚拟机和同一个Application的,同理,运行在不同进程中的组件是属于两个不同的虚拟机和Application的。

IPC基础概念介绍

Serializable

Serializable 是 java所提供的一个序列化接口,它是一个空接口,为对象提供标准的序列化和反序列化的操作。使用Serializable来实现序列化相当简单,只需要在类的声明中指定一个类似下面的标识即可自动实现默认的序列化过程。

private static final long serialVersionUID = 8711368828010083044L;

serialversionUID的详细工作机制是这样的,序列化的时候会把当前类的serialversionUID写进序列化的文件中(也可能是其他中介),当反序列化的时候系统会去检测文件中的serialversionUID,看它是否和当前类的serialversionUID一致,如果一致就说明序列化的类的版本和当前类的版本是相同的,这个时候可以成功反序列化;否则就说明当前类和序列化的类相比发生了某些变换,比如成员,类型可能发生了变化,这个时候是无法正常的反序列化的。

以下两点需要特别提一下,首先静态成员变量属于类不属于对象,所以不会参与序列化过程,其次用transient关键字标记的成员变量不参与序列化过程。

Parcelable

Parcelable也是一个接口,只要实现了这个接口,一个类的对象就可以实现序列化并可以通过Intent和Binder传递。

使用Parcelable的序列化过程中需要实现的功能有序列化、反序列化和内容描述。

Parcelable和Serializable的区别:

Serializable是Java中的序列化接口,其使用起来简单但是开销很大,序列化和反序列化过程需要大量I/O操作。对象序列化到存储设备中或者将对象序列化后通过网络传输,推荐用Serializable。

Parcelable是Andrord中的序列化方式,因此更适合用在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的服务。

下面是使用AIDL时,Android studio自动创建的一个实现Binder类,我们通过这个生成Binder来分析Binder的工作原理:

/*
 * This file is auto-generated.  DO NOT MODIFY.
 * Original file: F:\\AndroidStudioProjects\\IPCDemo\\app\\src\\main\\aidl\\com\\tin\\ipcdemo\\IBookManagerInterface.aidl
 */
package com.tin.ipcdemo;

public interface IBookManagerInterface extends android.os.IInterface {
    /**
     * Local-side IPC implementation stub class.
     */
    public static abstract class Stub extends android.os.Binder implements IBookManagerInterface {

        /**
         * Binder的唯一标识,一般用当前Binder的类名表示
         */
        private static final String DESCRIPTOR = "com.tin.ipcdemo.IBookManagerInterface";

        /**
         * 声明了一个内部类Stub,这个Stub就是一个Binder类,当客户端和服务端都位于同一个进程时,方法调用不会走跨进程的transact过程,
         * 而当两者位于不同进程时,方法调用需要走transact过程,这个逻辑由Stub的内部代理类Proxy来完成
         */
        public Stub() {
            this.attachInterface(this, DESCRIPTOR);
        }

        /**
         * 用于将服务端的Binder对象转换成客户端所需的AIDL接口类型的对象,这种转换过程是区分进程的,
         * 如果客户端和服务端位于同一进程,那么此方法返回的就是服务端的Stub对象本身,否则返回的是系统封装后的Stub.proxy
         */
        public static IBookManagerInterface asInterface(android.os.IBinder obj) {
            if ((obj == null)) {
                return null;
            }
            android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
            if (((iin != null) && (iin instanceof IBookManagerInterface))) {
                return ((IBookManagerInterface) iin);
            }
            return new Proxy(obj);
        }

        /**
         * 此方法用于返回当前Binder对象
         */
        @Override
        public android.os.IBinder asBinder() {
            return this;
        }

        /**
         * @param code  服务端通过code可以确定客户端所请求的目标方法是什么
         * @param data  接着从data中取出目标方法所需要的参数(如果目标方法中有参数的话)
         * @param reply 然后执行目标方法,当目标方法执行完毕后,就向reply中写入返回值(如果目标方法有返回值的话)
         */
        @Override
        public boolean onTransact(int code, android.os.Parcel data, android.os.Parcel reply, int flags) throws android.os.RemoteException {
            switch (code) {
                case INTERFACE_TRANSACTION: {
                    reply.writeString(DESCRIPTOR);
                    return true;
                }
                case TRANSACTION_getBookList: {
                    data.enforceInterface(DESCRIPTOR);
                    java.util.List _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);
        }

        /**
         * Stub的内部代理类
         */
        private static class Proxy implements IBookManagerInterface {
            private android.os.IBinder mRemote;

            Proxy(android.os.IBinder remote) {
                mRemote = remote;
            }

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

            public String getInterfaceDescriptor() {
                return DESCRIPTOR;
            }

            /**
             * 这个方法运行在客户端,当客户端远程调用此方法时,它的内部实现是这样的:
             * 首先创建该方法所需要的输入型Parcel对象_data、输出型Parcel对象_reply和返回值对象List然后把该方法的参数信息写入_data中(如果有参数的话):
             * 接着调用transact方法来发起RPC(远程过程调用)请求,同时当前线程挂起;
             * 然后服务端的onTransact方法会被调用。
             * 直到RPC过程返回后,当前线程继续执行,并从_reply中取出RPC过程的返回结果;
             * 最后返回_reply中的数据。
             */
            @Override
            public java.util.List getBookList() throws android.os.RemoteException {
                android.os.Parcel _data = android.os.Parcel.obtain();
                android.os.Parcel _reply = android.os.Parcel.obtain();
                java.util.List _result;
                try {
                    _data.writeInterfaceToken(DESCRIPTOR);
                    mRemote.transact(Stub.TRANSACTION_getBookList, _data, _reply, 0);
                    _reply.readException();
                    _result = _reply.createTypedArrayList(Book.CREATOR);
                } finally {
                    _reply.recycle();
                    _data.recycle();
                }
                return _result;
            }

            /**
             * 这个方法路行在客户端,它的执行过程和getBookList是一样的,addBook没有返回值,所以它不需要从.reply中取出返回值。
             */
            @Override
            public void addBook(Book book) 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 ((book != null)) {
                        _data.writeInt(1);
                        book.writeToParcel(_data, 0);
                    } else {
                        _data.writeInt(0);
                    }
                    mRemote.transact(Stub.TRANSACTION_addBook, _data, _reply, 0);
                    _reply.readException();
                } finally {
                    _reply.recycle();
                    _data.recycle();
                }
            }
        }

        /**
         * 声明两个整型的id分别用于标识这两个方法,这两个id用于标识在transact过程中客户端所请求的到底是哪个方法
         */
        static final int TRANSACTION_getBookList = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
        static final int TRANSACTION_addBook = (android.os.IBinder.FIRST_CALL_TRANSACTION + 1);
    }

    /**
     * 这两个方法就是我们在AIDL文件中声明的方法
     */
    public java.util.List getBookList() throws android.os.RemoteException;

    public void addBook(Book book) throws android.os.RemoteException;
}

通过上面分析,读者应该已经理解到Binder工作机制,但是有两点还是需要额外说明一下,首先,当客户端发起远程请求时,由于当前线程会被挂起直至服务器进程返回数据,所以如果一个远程方法是很耗时的,那么不能再UI线程中发起此远程请求,其次,由于服务器的Binder方法运行在Binder的线程池中,所以Binder方法不管是否耗时都应该给出一个Binder的工作机制图:

Binder的工作机制

参考资料:

《Android开发艺术探索》

Android开发艺术探索笔记

你可能感兴趣的:(IPC机制(上))