IPC机制总结

本文基于《Android艺术探索》中知识的总结而创


1 简介

1.1 简述

IPC 进程间通讯或跨进程通讯

  • android中IPC方式 Binder(AIDL)、Socket、Bundle、文件共享、Messenger、ContentProvider
  • IPC的基础场景是多进程:一般由两种,应用内多进程模式,应用间数据共享

1.2 进程与线程

  • 线程是cpu调度的最小单位,是一种有限的系统资源
  • 进程指一个执行单元,是一个独立的应用或者程序
  • 一个进程可包含多个线程
  • ANR 主线程无响应,以多线程解决

2 多进程模式

2.1 开启

  • 一般只有一种方法 就是在AndroidMenifest文件中给四大组件指定android:process属性。
    当然也可以通过JNI的natice层去fork。但是很少这么玩
  • 查看当前进程的命令:adb shell ps | grep com.gjg.text
  • 进程名以":"开头的进程属于应用的私有进程,其它应用的组件不可以和它泡在同一个进程中,非":"开头的进程属于全局进程,其它应用可以要通过ShareUID方式(ShareUID相同且签名相同)和它跑在同一个进程中。

2.2 多进程模式运行机制

不同的进程有独立的虚拟机,Applicaition以及内存空间,所以开启了多进程模式的应用应注意一下问题:

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

3 IPC基础载体

3.1 Serializable

  • serialVersionUID:用来辅助序列化和反序列化过程的,原则上序列化后的数据中的serialVersionUID只有和当前类的serialVersionUID相同才能够被正常的反序列化。
  • 系统默认用hash值作为serialVersionUID,当变量数和类型改变时,hash会变化,所以系统默认情况下这种改变发生时反序列化会crash
public class User implements Serializable{
}
//序列化过程
User user = new User();
ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream("cache.txt"));
out.writeObject(user);
out.close();
//反序列化过程
ObjectInputStream in = new ObjectInputStream(new FileInputStream("cache.txt"));
User user = (User)in.readObject();
in.close();

3.2 Parcelable

public class User implements Parcelable{
    private String name;
    private int age;

    public User() {
    }

    public User(String name, int age) {
        this.name = name;
        this.age = age;
    }

    //反序列化
    protected User(Parcel in) {
        name = in.readString();
        age = in.readInt();
    }

    //用于反序列化的创建器
    public static final Creator CREATOR = new Creator() {
        //从序列化的数据中创建原始对象
        @Override
        public User createFromParcel(Parcel in) {
            return new User(in);
        }
        //创建指定长度的原始对象数组
        @Override
        public User[] newArray(int size) {
            return new User[size];
        }
    };

    /**
     * 返回当前对象的内容描述
     * 如果含有文件描述符,返回1(CONTENTS_FILE_DESCRIPTOR),否则返回0
     * 多数都返回0
     * @return
     */
    @Override
    public int describeContents() {
        return 0;
    }

    /**
     * 序列化操作
     * @param dest
     * @param flags
     *  当为1(PARCELABLE_WRITE_RETURN_VALUE) 标示当前对象需要作为返回值返回,不能立即释放资源
     *  几乎所有情况都为0
     */
    @Override
    public void writeToParcel(Parcel dest, int flags) {
        dest.writeString(name);
        dest.writeInt(age);
    }
    /**
     * Parcel
     * 内部包装了可序列化的数据的native操作,例如
     *   @FastNative
     *   private static native void nativeWriteInt(long nativePtr, int val);
     *   @FastNative
     *   private static native void nativeWriteLong(long nativePtr, long val);
     */
}
  • Serializabble 是java中的序列化接口,使用简单但开销大,序列化和反序列化过程需要大量IO操作,适用于序列化到设备或者网络传输中
  • Parcelable 是android 中的序列化方式,使用麻烦但是效率高,主要用于内存序列化上。

3.3 Binder

(1)Binder 继承在IBinder。是IPC的框进程通讯方式
(2)还可以理解为一种虚拟的物理设备
(3)从android framework层来说Binder是ServiceManager连接各种Manager(ActivityManager WindowManager)和响应ManagerService的桥梁。
(4)从android 应用层来说是客户端和服务端进行通讯的媒介。当bindService时,返回段会返回一个包含服务端业务调用的Binder对象。

AIDL创建及核心源码解析

创建AIDL文件

//1.创建User.java 同上
//2.创建User在AIDL中的声明文件 User.aidl
package gjg.com.fundemo.dbg;
parcelable User;
//3.创建User的管理aidl文件
package gjg.com.fundemo.dbg;
//这里要注意使用全包名,且User有对应的User.aidl文件,否则会提示找不到
//aidl只支持方法生命,不支持静态变量
import gjg.com.fundemo.dbg.User;
interface IUserManager {
    List getUserList();
   /**
    * in 输入型参数
    * out 输出型参数
    * inout 输入输出型参数
    */
    void addUser(in User user);
}
//4.重新编译后系统会生成IUserManager.java

package gjg.com.fundemo.dbg;

public interface IUserManager extends android.os.IInterface {
    /**
     * 集成Binder 实现了IUserManager接口本身
     * 这是个抽象方法,并没有实现IUserManager具体方法,需要在服务端自定义Binder时实现
     * 一般在服务端继承自改抽象类,并在onBind中返回其实现
     */
    public static abstract class Stub extends android.os.Binder implements gjg.com.fundemo.dbg.IUserManager {
        //Binder的唯一标示
        private static final java.lang.String DESCRIPTOR = "gjg.com.fundemo.dbg.IUserManager";

        public Stub() {
            this.attachInterface(this, DESCRIPTOR);
        }

        /**
         * 用于将服务端的Binder对象转换成客户端所需的AIDL接口类型的对象
         */
        public static gjg.com.fundemo.dbg.IUserManager asInterface(android.os.IBinder obj) {
            if ((obj == null)) {
                return null;
            }
            //如果客户端和服务端在同一进程,返回的就是服务端的Stub对象本身Stub.Proxy对象
            android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
            if (((iin != null) && (iin instanceof gjg.com.fundemo.dbg.IUserManager))) {
                return ((gjg.com.fundemo.dbg.IUserManager) iin);
            }
            //如果客户端和服务端不在同一进程,返回的是封装后的
            return new gjg.com.fundemo.dbg.IUserManager.Stub.Proxy(obj);
        }

        /**
         * 用于返回当前Binder对象,在IInterface中定义的
         */
        @Override
        public android.os.IBinder asBinder() {
            return this;
        }

        /**
         * 该方法运行在服务端中Binder线程池中
         * 当客户端发起请求时,远程请求会通过系统底层封装后交由此方法来处理
         * 只有跨进程时才会调用此方法
         * @param code
         *  服务端通过code可以确定所请求的目标方法
         * @param data
         *  data中存有目标方法所需的参数
         * @param reply
         *  当目标方法执行完后会将返回值写入reply中
         * @param flags
         * @return 返回false 证明客户端请求失败
         * @throws android.os.RemoteException
         */
        @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_getUserList: {
                    data.enforceInterface(DESCRIPTOR);
                    //调用服务端方法得到返回值
                    java.util.List _result = this.getUserList();
                    reply.writeNoException();
                    //返回值写入reply
                    reply.writeTypedList(_result);
                    return true;
                }
                case TRANSACTION_addUser: {
                    data.enforceInterface(DESCRIPTOR);
                    gjg.com.fundemo.dbg.User _arg0;
                    if ((0 != data.readInt())) {
                        _arg0 = gjg.com.fundemo.dbg.User.CREATOR.createFromParcel(data);
                    } else {
                        _arg0 = null;
                    }
                    //调用服务端方法得到返回值
                    this.addUser(_arg0);
                    reply.writeNoException();
                    return true;
                }
            }
            return super.onTransact(code, data, reply, flags);
        }

        /**
         * 如果是跨进程通讯,该代理会被返回
         */
        private static class Proxy implements gjg.com.fundemo.dbg.IUserManager {
            //这个mRemote就是服务端继承自Stub的Binder对象
            private android.os.IBinder mRemote;

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

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

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

            //在客户端调用,最终会通过transact调服务端方法
            @Override
            public java.util.List getUserList() throws android.os.RemoteException {
                //创建所需的输入型Parcel对象
                android.os.Parcel _data = android.os.Parcel.obtain();
                //创建输出型Parcel对象
                android.os.Parcel _reply = android.os.Parcel.obtain();
                //创建返回值对象
                java.util.List _result;
                try {
                    _data.writeInterfaceToken(DESCRIPTOR);
                    //发起RPC(远程过程调用)请求,同时当前线程挂起
                    //transact被调用,知道RPC过程返回后,当前线程继续执行
                    //从reply中取出RPC过程的返回结果
                    mRemote.transact(Stub.TRANSACTION_getUserList, _data, _reply, 0);
                    _reply.readException();
                    //返回reply中的结果,反序列化,这里可以看出AIDL的对象需要细线parcelable接口
                    _result = _reply.createTypedArrayList(gjg.com.fundemo.dbg.User.CREATOR);
                } finally {
                    _reply.recycle();
                    _data.recycle();
                }
                return _result;
            }

            @Override
            public void addUser(gjg.com.fundemo.dbg.User user) 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 ((user != null)) {
                        _data.writeInt(1);
                        //将参数写入输出型parcel对象中
                        user.writeToParcel(_data, 0);
                    } else {
                        _data.writeInt(0);
                    }
                    mRemote.transact(Stub.TRANSACTION_addUser, _data, _reply, 0);
                    _reply.readException();
                } finally {
                    _reply.recycle();
                    _data.recycle();
                }
            }
        }

        static final int TRANSACTION_getUserList = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
        static final int TRANSACTION_addUser = (android.os.IBinder.FIRST_CALL_TRANSACTION + 1);
    }
    //接口的方法定义
    public java.util.List getUserList() throws android.os.RemoteException;
    //接口的方法定义
    public void addUser(gjg.com.fundemo.dbg.User user) throws android.os.RemoteException;
}
// 5.给Binder设置死亡代理
// 当服务端Binder连接断裂时,客户端可以收到通知,一遍再次发起请求从而恢复连接的解决方案
//示例代码如下
//服务端Service
public class UserRemoteService extends Service {
    @Nullable
    @Override
    public IBinder onBind(Intent intent) {
        return binder;
    }
    //返回个客户端的Binder
    IUserManager.Stub binder = new IUserManager.Stub() {

        @Override
        public List getUserList() throws RemoteException {
            return null;
        }

        @Override
        public void addUser(User user) throws RemoteException {

        }
    };
}
public class UserTestActivity extends AppCompatActivity {
    //客户端对IUserManager
    private IUserManager mUserManager;
    //服务端Binder死亡代理对象
    private DeathRecipient mDeathRecipient = new DeathRecipient();
    private ServiceConnection mSc = new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            //根据服务端Binder创建客户端IUserManager对象
            mUserManager = IUserManager.Stub.asInterface(service);
            try {
                //绑定服务端Binder死亡代理
                service.linkToDeath(mDeathRecipient,0);
            } catch (RemoteException e) {
                e.printStackTrace();
            }
        }

        @Override
        public void onServiceDisconnected(ComponentName name) {

        }
    };
    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        Intent intent = new Intent(this,UserRemoteService.class);
        //绑定服务
        bindService(intent,mSc,0);
        if(mUserManager != null){
            try {
                List users = mUserManager.getUserList();
            } catch (RemoteException e) {
                e.printStackTrace();
            }
        }
    }

    /**
     * Binder死亡代理
     * 一般在bindService中Connection后创建并设置
     * IUserManager mServiceBinder = IUserManager.Stub.asInterface(binder);
     * binder.linkToDeath(this,0);
     */
    public  class DeathRecipient implements IBinder.DeathRecipient {
        //当Binder死亡是会回调此方法
        @Override
        public void binderDied() {
            if(mUserManager == null){
                return;
            }
            //解绑服务端Binder死亡代理对象
            mUserManager.asBinder().unlinkToDeath(this,0);
            mUserManager = null;
        }
    }
}

4 Android中的IPC方式

4.1 Bundle

Bundle implements Parcelable 所以可以方便的在不同进程进行通讯

4.2 使用文件共享

适合同步要求不高的进程间通讯

4.3 Messenger

是对AIDL的简单封装,以Message作为载体

4.4 AIDL

流程:
-->服务端创建AIDL文件,编译生成接口,服务端实现接口的.Stub返回
-->客户端绑定Service,绑定成功后,将服务端返回的Binder对象转成AIDL文件对应的接口对象
注意:
aidl文件中不是所有类型都支持,支持的类型如下:

基本数据类型
String CharSequence
List-ArrayList,里面的每个元素必须被aidl支持,这里指服务端最终会返回ArrayList类型
Map-HashMap,里面的每个元素必须被aidl支持
Parcelable,所有实现了Parcelable接口的对象
AIDL:所有aidl接库本身也可以在aidl文件中使用

自定义的Parcelable对象,在aidl中使用时,必须新建一个和它同名的AIDL文件,并在其中生命它为parcleable类型。例如:

package com.gjg
parcelable User;

RemoteCallbackList

  • 跨进程传输对象的最想都会经过反序列化,也就是说两端针对相同内容的对象却不是真正的同一个对象,所以就会导致跨进程解注册时会出现问题,而RemoteCallbackList就为解决此问题而生。
  • RemoteCallbackList是专门提供的用于删除款进程listener的接口,它是一个泛型,支持管理任意的aidl接口,其声明如下:
    public class RemoteCallbackList
    其本质就是利用了跨进程对象的相同对象在两端拥有相同的底层Binder对象

安全验证,在androidMenifest中声明,可在服务端onBind或者onTransact中进行验证是否有权限

4.5 ContentProvider

  • 底层也是Binder机制
  • 六个方法 onCreate() getType() CRUD
  • CRUD 在Binder线程池
  • android:authorities 声明 ContentResolver中Uri参数与之配对
  • UriMatcher 实现外界访问的匹配功能

4.6 Socket

通过网络了。不光能跨进程,还能跨设备

5 Binder连接池

  • 背景有很多aidl时不可能对应创建很多Service
  • Binder连接池的作用就是将每个业务模块的Binder请求统一转发到远程的Service中去执行,从而避免重复创建Service的过程
//1. 创建两个简单aidl文件
package gjg.com.fundemo.binderchi;

interface ICompute {
    int add(int a,int b);
}
package gjg.com.fundemo.binderchi;

interface ISecurityCenter {
    String encrypt(String content);
    String decrypt(String password);
}
//2. 创建aidl文件对应的实现
package gjg.com.fundemo.binderchi;

import android.os.RemoteException;

public class SecurityCenerImpl extends ISecurityCenter.Stub {
    @Override
    public String encrypt(String content) throws RemoteException {
        return null;
    }

    @Override
    public String decrypt(String password) throws RemoteException {
        return null;
    }
}

package gjg.com.fundemo.binderchi;

public class ComputeImpl extends ICompute.Stub {
    @Override
    public int add(int a, int b) throws RemoteException {
        return 0;
    }
}

//3. 创建IBinderPool的aidl文件
package gjg.com.fundemo.binderchi;

interface IBinderPool {
    IBinder queryBinder(int binderCode);
}

//4. 创建IBinderPool的实现,作为BinderPool的内部类
public static class BinderPoolImpl extends IBinderPool.Stub{

        @Override
        public IBinder queryBinder(int binderCode) throws RemoteException {
            IBinder binder = null;
            switch (binderCode){
                case BINDER_SECURITY_CENTER:
                    binder = new SecurityCenerImpl();
                    break;
                case BINDER_COMPUTE:
                    binder = new ComputeImpl();
                    break;
            }
            return binder;
        }
    }
//5. 创建Binder连接池的Service
public class BinderPoolService extends Service {
    private BinderPool.BinderPoolImpl mBinderPoolImpl = new BinderPool.BinderPoolImpl();
    @Nullable
    @Override
    public IBinder onBind(Intent intent) {
        //返回的是服务端的BinderPoolImpl
        return mBinderPoolImpl;
    }
}
// 6. 创建BinderPool
public class BinderPool {
    public static final int BINDER_SECURITY_CENTER = 1;
    public static final int BINDER_COMPUTE = 2;

    private Context mContext;
    private IBinderPool mBinderPool;
    private static volatile BinderPool sInstance;
    private CountDownLatch mCountDownLatch;//用于保证线程同步
    private BinderPool(Context context){
        this.mContext = context;
        connectBinderPoolService();
    }

    public static BinderPool getsInstance(Context context){
        if(sInstance == null){
            synchronized (BinderPool.class){
                sInstance = new BinderPool(context);
            }
        }
        return sInstance;
    }
    public IBinder queryBinder(int binderCode){
        IBinder iBinder = null;
        if(mBinderPool != null){
            try {
                mBinderPool.queryBinder(binderCode);
            } catch (RemoteException e) {
                e.printStackTrace();
            }
        }
        return iBinder;
    }
    private void connectBinderPoolService() {
        mCountDownLatch = new CountDownLatch(1);
        Intent service = new Intent(mContext,BinderPoolService.class);
        mContext.bindService(service,mBinderPoolConnection,Context.BIND_AUTO_CREATE);
        try {
            mCountDownLatch.await();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
    //连接
    private ServiceConnection mBinderPoolConnection = new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            mBinderPool  = IBinderPool.Stub.asInterface(service);
            try {
                mBinderPool.asBinder().linkToDeath(mBinderPoolDeathRecipient,0);
            } catch (RemoteException e) {
                e.printStackTrace();
            }
            mCountDownLatch.countDown();
        }

        @Override
        public void onServiceDisconnected(ComponentName name) {

        }
    };
    /**
     * Binder死亡监控
     */
    private IBinder.DeathRecipient mBinderPoolDeathRecipient = new IBinder.DeathRecipient() {
        @Override
        public void binderDied() {
            mBinderPool.asBinder().unlinkToDeath(mBinderPoolDeathRecipient,0);
            mBinderPool = null;
            connectBinderPoolService();
        }
    };
    public static class BinderPoolImpl extends IBinderPool.Stub{

        @Override
        public IBinder queryBinder(int binderCode) throws RemoteException {
            IBinder binder = null;
            switch (binderCode){
                case BINDER_SECURITY_CENTER:
                    binder = new SecurityCenerImpl();
                    break;
                case BINDER_COMPUTE:
                    binder = new ComputeImpl();
                    break;
            }
            return binder;
        }
    }
}
// 7. 使用
public class BinderPoolTestActivity extends AppCompatActivity {
    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        doWork();
    }

    private void doWork() {
        BinderPool binderPool = BinderPool.getsInstance(this.getApplicationContext());
        IBinder securityCenterBinder = binderPool.queryBinder(BinderPool.BINDER_SECURITY_CENTER);
        ISecurityCenter securityCenter = SecurityCenerImpl.asInterface(securityCenterBinder);
        try {
            securityCenter.encrypt("");
        } catch (RemoteException e) {
            e.printStackTrace();
        }
        IBinder computeBinder = binderPool.queryBinder(BinderPool.BINDER_COMPUTE);
        ICompute compute = ComputeImpl.asInterface(computeBinder);
        try {
            compute.add(1,2);
        } catch (RemoteException e) {
            e.printStackTrace();
        }
    }
}

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