程序员

Android IPC(进程间通信)

2019-04-10  本文已影响0人  瓶子狗坏人

关于Android 进程间通信的基本知识以及Android中进程间通信的方式

IPC简介与基础概念

基础

  1. Serializable

    /**
     * 父类没有实现Serializable,子类实现了Serializable,父类的属性不会参与序列化
     */
    open class Parent(var parentName: String) {
        constructor() : this("parent")
    }
    
    /**
     * 定义[readObject] 和[writeObject] 可以对序列化进行一些自定义的操作
     */
    data class Child(
        var childName: String = "Bob",
        //有Transient标识的 不参与序列化
        @Transient var childAge: Int = 5,
        var password: String = "password"
    ) : Serializable, Parent() {
        companion object {
            /**
             * 1.在反序列化的时候,只有这个值相同的情况下才有可能反序列化成功
             * 2.可以不指定这个值,系统会有默认值
             * 3.在默认值的情况下,如果增加和删除了成员变量,反序列化不能成功,默认值改变了
             * 4.如果类名发生改变或成员的类型发生改变,不管这个值是相同,反序列化都不能成功
             * */
            private const val serialVersionUID = 2L
        }
        /**
         * 反序列化操作
         * 默认会调用 [java.io.ObjectInputStream.defaultReadObject]
         *
         * */
        private fun readObject(ins: ObjectInputStream) {
            try {
                val fields = ins.readFields()
                password = fields.get("password", "").toString()
            } catch (e: Exception) {
                Log.d("SerialTest", e.message ?: "UnKnowError")
            }
        }
    
        /**
         * 序列化操作
         * 默认会调用 [java.io.ObjectOutputStream.defaultWriteObject]
         *
         * */
        private fun writeObject(out: ObjectOutputStream) {
            try {
                val fields = out.putFields()
                fields.put("password", password + "encryption")
                //  fields.put("childName",childName)
                out.writeFields()
            } catch (e: Exception) {
                Log.d("SerialTest", e.message ?: "UnKnowError")
            }
        }
    }
       
    
  2. Parcelable

    /**
     * 必须与相应的AIDL在同个包下
     */
    data class Info(var data: String,
                    var times: Int) : Parcelable {
        companion object CREATOR : Parcelable.Creator<Info> {
            /**
             * 从序列化的对象中创建原始对象
             * */
            override fun createFromParcel(source: Parcel): Info = Info(source)
    
            /**
             * 创建指定长度的原始对象数组
             * */
            override fun newArray(size: Int): Array<Info?> = arrayOfNulls(size)
        }
    
        constructor(parcel: Parcel) : this(
                parcel.readString() ?: "",
                parcel.readInt())
    
        /**
         * 将当前对象写入序列化对象中
         * @param flags  1表示当前对象需要最为返回值返回,不能立即释放,几乎所有情况都为0
         * [android.os.Parcelable.PARCELABLE_WRITE_RETURN_VALUE]
         * */
        override fun writeToParcel(dest: Parcel?, flags: Int) {
            dest?.apply {
                writeString(data)
                writeInt(times)
            }
        }
    
        /**
         * @retrun 如果有文件描述符返回1, 否则返回0(几乎所有情况都是0)
         * [android.os.Parcelable.CONTENTS_FILE_DESCRIPTOR]
         * */
        override fun describeContents() = 0
    }
    
    

Binder

NOTE:
1. 客户端在RPC(远程请求调用)的时候,会挂起当前线程直到RPC返回后才会重新唤醒,所以客户端启动的RPC是耗时任务时务必要新开一个线程
2. 服务端相关的方法已经运行在Binder的线程池中,就算是耗时任务也不需要新开一个线程
3. 同理在服务端发起RPC时也会挂起服务端的当前线程,此时调用到的客户端的方法运行在客户端的Binder线程池中,涉及到UI操作时要做切换线程处理(可以使用Handler);如果是耗时操作,服务端务必不要在主线程中发起此RPC

   package com.open.aqrlei.ipc.aidl;
   
   public interface IChangeListener extends android.os.IInterface {
       /**
        * Local-side IPC implementation stub class.
        */
       public static abstract class Stub extends android.os.Binder implements com.open.aqrlei.ipc.aidl.IChangeListener {
           /**
            * Binder的唯一标识
            */
           private static final java.lang.String DESCRIPTOR = "com.open.aqrlei.ipc.aidl.IChangeListener";
   
           /**
            * Construct the stub at attach it to the interface.
            */
           public Stub() {
               this.attachInterface(this, DESCRIPTOR);
           }
   
           /**
            * @return
            * 将服务端的Binder对象转换成客户端所需的AIDL接口对象。
            * 如果是同一进程中,此方法返回的是服务端的Stub对象本生
            * 如果是不同进程,返回的是系统封装后的Stub.proxy对象
            */
           public static com.open.aqrlei.ipc.aidl.IChangeListener asInterface(android.os.IBinder obj) {
               if ((obj == null)) {
                   return null;
               }
               android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
               if (((iin != null) && (iin instanceof com.open.aqrlei.ipc.aidl.IChangeListener))) {
                   return ((com.open.aqrlei.ipc.aidl.IChangeListener) iin);
               }
               return new com.open.aqrlei.ipc.aidl.IChangeListener.Stub.Proxy(obj);
           }
   
           /**
            * @return 返回当前的 Binder对象
            */
           @Override
           public android.os.IBinder asBinder() {
               return this;
           }
   
           /**
            * 运行在服务端的Binder线程池中,当客户端发起跨进程请求时,远程请求通过系统底层封装后交由此方法处理
            * {@link android.os.Binder}
            * @param code  服务端通过code可以确定所请求的方法是什么
            * @param data  从data中取出目标方法所需要的参数(如果需要的话),然后执行目标方法
            * @param reply 在reply中写入返回值(如果需要的话)
            * @return 返回 false ,客户端的请求就会失败
            * */
           @Override
           public boolean onTransact(int code, android.os.Parcel data, android.os.Parcel reply, int flags) throws android.os.RemoteException {
               java.lang.String descriptor = DESCRIPTOR;
               switch (code) {
                   case INTERFACE_TRANSACTION: {
                       reply.writeString(descriptor);
                       return true;
                   }
                   case TRANSACTION_msgChange: {
                       data.enforceInterface(descriptor);
                       com.open.aqrlei.ipc.Info _arg0;
                       if ((0 != data.readInt())) {
                           _arg0 = com.open.aqrlei.ipc.Info.CREATOR.createFromParcel(data);
                       } else {
                           _arg0 = null;
                       }
                       this.msgChange(_arg0);
                       reply.writeNoException();
                       return true;
                   }
                   default: {
                       return super.onTransact(code, data, reply, flags);
                   }
               }
           }
   
           private static class Proxy implements com.open.aqrlei.ipc.aidl.IChangeListener {
               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;
               }
   
               /**
                * 此方法运行在客户端。首先会创建两个对象,输入型Parcel:"_data",输出型Parcel:"_reply",如果有返回值
                * 会创建一个返回值对象。其次,将方法需要的参数写入_data(如果有参数的话);接着调用transact
                * 发起RPC(远程过程调用)请求同时当前线程挂起,服务端的onTransact会被调用,直到RPC过程返回后,当前线程
                * 继续执行,并从_reply中取出结果,如果有返回值的话,赋值给返回值对象。然后_data,_reply调用recycle
                * 最后如果有返回值的话,返回返回值
                *
                */
               @Override
               public void msgChange(com.open.aqrlei.ipc.Info info) throws android.os.RemoteException {
                   //输入型Parcel
                   android.os.Parcel _data = android.os.Parcel.obtain();
                   //输出型Parcel
                   android.os.Parcel _reply = android.os.Parcel.obtain();
                   try {
                       _data.writeInterfaceToken(DESCRIPTOR);
                       if ((info != null)) {
                           _data.writeInt(1);
                           info.writeToParcel(_data, 0);
                       } else {
                           _data.writeInt(0);
                       }
                       mRemote.transact(Stub.TRANSACTION_msgChange, _data, _reply, 0);
                       _reply.readException();
                   } finally {
                       _reply.recycle();
                       _data.recycle();
                   }
               }
           }
   
           static final int TRANSACTION_msgChange = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
       }
   
       /**
        * Demonstrates some basic types that you can use as parameters
        * and return values in AIDL.
        */
       public void msgChange(com.open.aqrlei.ipc.Info info) throws android.os.RemoteException;
   }
      

IPC方式

Bundle

Bundle 结合 Intent一起使用,一般用于四大组件(Activity、Service、Broadcast)的进程间的通信

文件共享

NOTE:

  1. 通过读/写磁盘中的同一个文件实现进程间的数据交换
  2. 由于Linux系统并发读写文件可以没有限制的进行,不适合并发的情景
  3. SharedPreference虽然数据也是存在磁盘中的文件里,但是系统利用Map<String, Object> mMap在内存中进行了缓存,这一点会导致问题
 object FileStreamUtil {
  
   fun getObjectFile(context: Context) = getCacheFile(context, "object_test")
   fun getCacheFile(context: Context, uniqueName: String = "ipc_test.text"): File? {
       val cachePath =
               if (Environment.MEDIA_MOUNTED == Environment.getExternalStorageState() || !Environment.isExternalStorageRemovable())
                   context.externalCacheDir?.path
               else
                   context.cacheDir.path
  
       return File("$cachePath${File.separator}$uniqueName")
   }
  
   /**
    * @param file 写入内容的文件
    * @param content 要写入的字符串
    * */
   fun writeChar(file: File, content: String = "Hello IPC File"): Boolean {
       return try {
           BufferedWriter(FileWriter(file, true)).use { bWriter ->
               bWriter.write((content))
               bWriter.flush()
               true
           }
       } catch (e: Exception) {
           Log.d("IOTest", e.message ?: "UnKnowError")
           false
       }
   }
  
   /**
    * @param user 要写入的可序列化对象
    * */
   fun writeObject(file: File?, user: User): Boolean {
       return try {
           ObjectOutputStream(FileOutputStream(file)).use {
               it.writeObject(user)
               it.flush()
               true
           }
  
       } catch (e: Exception) {
           false
       }
   }
  
   /**
    * 读取写入文件的序列化对象
    * */
   fun readObject(file: File?, action: (User?) -> Unit) {
       if (file == null) {
           return
       }
       try {
           ObjectInputStream(FileInputStream(file)).use {
               val user = it.readObject() as? User
               action(user)
           }
       } catch (e: Exception) {
           action(null)
       }
   }
  
   /**
    * 读取写入文件的字符
    * */
   fun readChar(file: File, action: (String) -> Unit) {
       try {
           BufferedReader(FileReader(file)).use { bReader ->
               val str = StringBuffer()
               var buffer = -1
               while (buffer.let {
                           buffer = bReader.read()
                           buffer
                       } != -1) {
                   str.append(buffer.toChar().toString())
               }
               action(str.toString())
           }
       } catch (e: Exception) {
           action(e.message ?: "UnKnowError")
       }
   }
 }

Messenger

NOTE:
1. Messenger是基于Binder的,底层实现也是AIDL
2. Messenger同样实现了Parcelable接口

AIDL

ContentProvider

NOTE:
1. ContentProvider是基于Binder实现的
2. 客户端的是ContentProviderNative#ContentProviderProxy,服务端的是ContentProvider#Transport

Socket

NOTE: 使用Socket需要使用网络相关权限

  <uses-permission android:name="android.permission.INTERNET" />
  <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />

Binder连接池

NOTE:AIDL的实现方式,主要原理是:
1. 客户端发送对应的Code;
2. 服务端根据Code返回对应的IBinder;
3. 客户端再根据IBinder将之转换成对应的AIDL接口.

   IBinderPool.aidl
   package com.open.aqrlei.ipc;

   interface IBinderPool {
        IBinder queryBinder(int binderCode);
   }

   //#服务端
   private val mIBinderPool = object : IBinderPool.Stub() {
       @Throws(RemoteException::class)
       override fun queryBinder(binderCode: Int): IBinder {
           return when (binderCode) {
              // method code
           }
       }
   }
   // #客户端-ServiceConnection#onServiceConnected
    mBinder = IBinderPool.Stub.asInterface(service)
    
    // 获取到Messenger的IBinder
    messenger = Messenger(mBinder.queryBinder(USE_MESSENGER_CODE))


IPC方式适用场景(源于Android开发艺术探索)

方式 优点 缺点 适用场景
Bundle 简单易用 只能传输Bundle支持的数据 四大组件间的进程间通信
文件共享 简单易用 不适合高并发,无法做到进程间实时通信 无并发,交换简单的数据,实时性不高
Messenger 功能一般,一对多串行通信,实时通信 不能很好的处理高并发, 不支持RPC,数据通过Message传输,支持的类型有局限性 低并发的一对多通信,无RPC请求(或无需返回值的RPC)
AIDL 功能强大,一对多并发,实时通信 使用稍微复杂,需要处理好线程同步 一对多通信且有RPC需求
ContentProvider 数据访问方面功能强大,一对多并发数据共享,Call方法扩展其它操作 主要提供数据源的CRUD 一对多进程间数据共享
Socket 功能强大,通过网络传输字节流,一对多并发实时通信 实现稍微繁琐,不支持RPC 网络数据交换

资源

Android开发艺术探索 - 任玉刚

Android-Art-Res-Chapter_2

IPCSample

上一篇 下一篇

猜你喜欢

热点阅读