Android IPC(进程间通信)

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

IPC简介与基础概念

基础

  • android 开启多进程的方式
    1. 指定四大组件的 android:process 属性
    2. 通过Jninative层 fork一个进程
  • "anroid:process" 进程命名的含义
    1. ":"开头表示当前进程名前面要加上包名
    2. ":" 表示是当前应用的私有进程,其它应用的组件不可以和它跑在同一个进程中
    3. 没有 ":" 表示是全局进程,其它应用的组件在ShareUID并且签名相同的情况下也可以跑在这个进程
  • 多进程会带来的问题
    1. 静态成员和单例模式失效
    2. Application 会创建多次
    3. SharedPreference 变得不可靠
    4. 线程同步机制失效
  • Serializable , Parcelable
  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

  • 通过AIDL了解Binder
   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;
   }
      
  • Binder工作机制

    Binder工作机制

  • Binder死亡代理(linkToDeath(),unLinkToDeath())

     private val mServiceConn =  object : ServiceConnection {
         override fun onServiceConnected(name: ComponentName?, service: IBinder?) {
             mBinder = IBinderPool.Stub.asInterface(service)
             //设置死亡监听
             service?.linkToDeath(mDeathRecipient,0)
         }
    
         override fun onServiceDisconnected(name: ComponentName?) { }
     }
     private val mDeathRecipient = object : IBinder.DeathRecipient {
         /**
          * 远程服务端由于某种原因终止,此时Binder"死亡",当Binder死亡时,
          * 会回调此方法,在此处调用unlinkToDeath且将Binder置为NULL,
          * 并重新绑定服务
          * */
         override fun binderDied() {
             mBinder?.asBinder()?.unlinkToDeath(this, 0)
             mBinder = null
             //TODO bindService() 重新绑定远程服务
         }
     }   
    
    

IPC方式

Bundle

Bundle 结合 Intent一起使用,一般用于四大组件(Activity、Service、Broadcast)的进程间的通信
  • 源码分析
    NOTE:
    1. Bundle继承BaseBundle类,实现了序列化Parcelable接口
    2. 在putXxx和getXxx的时候,第一步都要先调用unparcel方法,主要是对Parcel mParcelledData进行操作,然后再从map中获取数据
    3. Bundle中数据都存储在ArrayMap<String, Object> mMap中,如果mParcelledData不为null的时候, mMap就是null,数据存储为包含Bundle的Parcel。当数据unparcelled,mParcelledData置为null, map中存储数据
     public void putInt(@Nullable String key, int value) {
         unparcel();
         mMap.put(key, value);
     }  
      
     void unparcel() {
         synchronized (this) {
             final Parcel source = mParcelledData;
             // 如果不为null,说明数据都存在一个包含Bundle的Parcel即mParcelledData中,此时map为null
             // 就要根据mParcelledData的情况创建(如果mMap为null的话)或更新ArrayMap,并根据mParcelledData写入内容
             if (source != null) {
                 initializeFromParcelLocked(source, /*recycleParcel=*/ true);
             } else {
                 if (DEBUG) {
                     Log.d(TAG, "unparcel "
                             + Integer.toHexString(System.identityHashCode(this))
                             + ": no parcelled data");
                 }
             }
         }
     }
          
      public int getInt(String key) {
           unparcel();
           return getInt(key, 0);
       }
    
      public int getInt(String key, int defaultValue) {
           unparcel();
           Object o = mMap.get(key);
           if (o == null) {
               return defaultValue;
           }
           try {
               return (Integer) o;
           } catch (ClassCastException e) {
               typeWarning(key, o, "Integer", defaultValue, e);
               return defaultValue;
           }
      }
    

文件共享

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接口

  • 使用
    • 客户端

      // 在ServiceConnection#onServiceConnected里面获取到Binder并创建Messenger
      clientMessengerHandler.service = Messenger(it.queryBinder(IPCService.MESSENGER_BINDER_CODE)) 
      
      class ClientMessengerHandler(private val activity: WeakReference<IPCActivity>) : Handler(Looper.getMainLooper()) {
          var service: Messenger? = null
          override fun handleMessage(msg: Message?) {
              when (msg?.what) {
                  RECEIVE_FROM_SERVICE_CODE_INIT -> {
                      //将客户端的Messenger通过服务端的Messenger传送给服务端
                      service?.send( Message.obtain(null, IPCService.RECEIVE_FROM_CLIENT_CODE_INIT).apply {
                          replyTo = Messenger(clientMessengerHandler)
                           })
                  }
                 ...
              }
              super.handleMessage(msg)
          }
      }
      
    • 服务端

         MESSENGER_BINDER_CODE -> { // Binder 连接池,选用Messenger
      
           // 运行在Binder线程池中
           // 此处必须加上Looper.getMainLooper()
           serviceMessengerHandler = object : Handler(Looper.getMainLooper()) {
                 override fun handleMessage(msg: Message?) {
                     when (msg?.what) {
                         RECEIVE_FROM_CLIENT_CODE_INIT -> { //初次信息传递
      
                             if (client != msg.replyTo) {// 获取客户端返回的Messenger,用于之后两端通信
                                  client = msg.replyTo
                              }
                                
                                 //通知客户端,首次消息接收成功
                             client?.send(Message.obtain(null, IPCActivity.RECEIVE_FROM_SERVICE_CODE_INIT))   
                          }
                         
                             ...
      
                         else -> {
                             super.handleMessage(msg)
                         }
                       }
                  }
             }
              //返回binder给客户端
             Messenger(serviceMessengerHandler).binder
          }
      
  • 源码
    Messenger 中
     public Messenger(Handler target) {
         mTarget = target.getIMessenger();
     }
     public Messenger(IBinder target) {
       mTarget = IMessenger.Stub.asInterface(target);
     }
     public void send(Message message) throws RemoteException {
       mTarget.send(message);
     }
    Handler中
     final IMessenger getIMessenger() {
       synchronized (mQueue) {
           if (mMessenger != null) {
               return mMessenger;
           }
           mMessenger = new MessengerImpl();
           return mMessenger;
       }
     }
     private final class MessengerImpl extends IMessenger.Stub {
       public void send(Message msg) {
           msg.sendingUid = Binder.getCallingUid();
           Handler.this.sendMessage(msg);
       }
     }
    
    IMessenger.aidl中
    package android.os;
    
    import android.os.Message;
    
    /** @hide */
    oneway interface IMessenger {
       void send(in Message msg);
    } 
    
    

AIDL

  • 支持的数据类型

    类型 说明
    基本数据类型 int, char,boolean,double,float等
    String和CharSequence
    List 只支持ArrayList传输,但是如果List是返回类型,return了一个非ArrayList的数据,Binder会根据List的规范生成一个ArrayList返回
    Map 只支持HashMap
    Parcelable 支持所有实现了Parcelable接口的类,自定义的Parcelable详情(下文)
    AIDL 所用AIDL接口,自定义的AIDL详情(下文)
  • 实现

    • 使用自定义Parcelable作为传递的数据类型

      1. 用于AIDL的Parcelable实现类必须与对应的AIDL文件在同一个包下(Parcelable实现见基础部分)
      2. 使用自定义的Parcelable,要据此创建一个AIDL
         //import 是必须要有的
        Info.aidl
         package com.open.aqrlei.ipc;
         import com.open.aqrlei.ipc.Info;
        
         parcelable Info;
        
        
        IChangeListener.aidl
         package com.open.aqrlei.ipc;
         import com.open.aqrlei.ipc.Info;
        
        
         interface IChangeListener {
             void msgChange(in Info info);
         }
        
    • 使用自定义的AIDL作为传递的数据类型,import必须要有

       package com.open.aqrlei.ipc;
       import com.open.aqrlei.ipc.IChangeListener;
      
       interface IListenerManager {
      
           void registerChangeListener(in IChangeListener listener);
      
           void unregisterChangeListener(in IChangeListener listener);
       }
      
      
    • RemoteCallbackList(使用,源码分析)
      NOTE:

      1. 用与对回调方法的管理,由于是跨进程通信,经过反序列化后,回调方法的对象与序列化前的对象实际不是同一个,这样就造成不能够简单的注册和注销回调方法。而使用RemoteCallbackList能解决这个问题。
      2. 内部实现了线程同步
      3. Binder死亡后,能自动解除Binder所对应的回调
      • 使用
      // 注册和注销
       private val mListenerManager = object : IListenerManager.Stub() {
         override fun registerChangeListener(listener: IChangeListener?) {
             listenerList.register(listener)
             sendMsgChange(Info("receive from service", 0))
         }
      
         override fun unregisterChangeListener(listener: IChangeListener?) {
           listenerList.unregister(listener)
         }
       }
      
       // 回调
       //必须先调用这个方法
       val n = listenerList.beginBroadcast()
       for (i in 0 until n) {
           listenerList.getBroadcastItem(0).msgChange(info)
       }
      
      
      • 源码
        NOTE:
        1. 注册的时候以底层不变的binder对象作为key保存到ArrayMap中,注销的时候同样用binder作为key从ArrayMap中移除。
        2. 使用的时候将ArrayMap中的数据添加到数组中,然后再从数组中获取对象进行回调的执行。
        3. 内部使用synchronized关键字实现了线程同步
        4. 在注册时候使用了linkToDeath() 关联了死亡代理 IBinder.DeathRecipient,在触发死亡代理的时候,移除相关的回调对象。在注销的时候unlinkToDeath()
       注册和注销
        public boolean register(E callback, Object cookie) {
        synchronized (mCallbacks) {//同步代码块
            if (mKilled) {
                return false;
            }
            logExcessiveCallbacks();
            IBinder binder = callback.asBinder();
            try {
                Callback cb = new Callback(callback, cookie);
                // 关联到IBinder.DeathRecipient接口,Binder死亡后,移除此回调
                binder.linkToDeath(cb, 0);
                //序列化和反序列化后,底层binder对象是同一个,以它作为key
                mCallbacks.put(binder, cb);
                return true;
            } catch (RemoteException e) {
                return false;
            }
          }
        }
        public boolean unregister(E callback) {
        synchronized (mCallbacks) {
            Callback cb = mCallbacks.remove(callback.asBinder());
            if (cb != null) {
                cb.mCallback.asBinder().unlinkToDeath(cb, 0);
                return true;
            }
            return false;
         }
        }
      广播
        public int beginBroadcast() {
          synchronized (mCallbacks) {
            if (mBroadcastCount > 0) {
                throw new IllegalStateException(
                        "beginBroadcast() called while already in a broadcast");
            }
            
            final int N = mBroadcastCount = mCallbacks.size();
            if (N <= 0) {
                return 0;
            }
            Object[] active = mActiveBroadcast;
            if (active == null || active.length < N) {
                mActiveBroadcast = active = new Object[N];
            }
            for (int i=0; i<N; i++) {
                //将ArrayMap中的回调添加到数组active(mActiveBroadcast中)
                active[i] = mCallbacks.valueAt(i);
            }
            return N;
          }
        } 
      
        public E getBroadcastItem(int index) {
            //从数据mACtiveBroadcast中获取注册的回调
            return ((Callback)mActiveBroadcast[index]).mCallback;
        }
        
        public void finishBroadcast() {
          synchronized (mCallbacks) {
            if (mBroadcastCount < 0) {
                throw new IllegalStateException(
                        "finishBroadcast() called outside of a broadcast");
            }
      
            Object[] active = mActiveBroadcast;
            if (active != null) {
                final int N = mBroadcastCount;
                for (int i=0; i<N; i++) {
                    //将active(mActiveBroadcast)中的数据置为null
                    active[i] = null;
                }
            }
      
            mBroadcastCount = -1;
          }
        }
      
      
    • 连接权限验证 (表格中的方式和地方无强制关联)

    验证权限的方式 验证权限的地方
    使用自定义Permission Service#onBind方法中,不通过则返回null
    使用包名等(通过Uid获取包名) 实现AIDL的服务端调用的onTransact ,不通过则返回false
     //自定义Permission,位于AndroidManifest.xml
     <permission android:name="com.aqrlei.permission.IPC_AIDL" />
     //示例 
     override fun onBind(intent: Intent?): IBinder? {
         val check = checkCallingOrSelfPermission("com.aqrlei.permission.PROVIDER")
         return if (check == PackageManager.PERMISSION_DENIED) {
             null
         } else {
             mIBinderPool
         }
     }
    
    • 使用示例
      //服务端
       private val mListener = object:IChangeListener.Stub(){
           override fun msgChange(info: Info?) {
                //method code
           }
       }
       override fun onBind(intent: Intent?): IBinder? {
           // 在onBind中返回
           return mListener
       }
      //客户端
        private var mListener:IChangeListener?= null
        //在ServiceConnction#onServiceConnected
        mListener = IChangeListener.Stub.asInterface(service)  
        ...
        //接下来就可以通过mListener通信了
      

ContentProvider

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

  • 自定义ContentProvider (Sqlite为例)

      class OrderProvider : ContentProvider() {
      companion object {
          //必须和AndroidManifest.xml中配置的一样,这是ContentProvider的唯一标识
          private const val AUTHORITY = "aqrlei.OrderProvider"
    
          val ORDER_URL = Uri.parse("content://$AUTHORITY/order")
          private const val ORDER_CODE = 0X01
          private val mMatcher = UriMatcher(UriMatcher.NO_MATCH).apply {
              addURI(AUTHORITY, "order", ORDER_CODE)
          }
      }
    
      private lateinit var dataBaseOperator: DatabaseOperator
    
      override fun insert(uri: Uri, values: ContentValues?): Uri? {
          dataBaseOperator.insert(false)
          return uri
      }
    
      override fun query(uri: Uri, projection: Array<String>?, selection: String?, selectionArgs: Array<String>?, sortOrder: String?): Cursor? {
          return dataBaseOperator.query()
      }
    
      override fun onCreate(): Boolean {
          DatabaseOperator.init(context!!)
          dataBaseOperator = DatabaseOperator.getInstance()
          return true
      }
    
      override fun update(uri: Uri, values: ContentValues?, selection: String?, selectionArgs: Array<String>?): Int {
          return dataBaseOperator.update(false)
      }
    
      override fun delete(uri: Uri, selection: String?, selectionArgs: Array<String>?): Int {
          return dataBaseOperator.delete(false)
      }
    
      override fun getType(uri: Uri): String? {
          return null
      }
    
    }
    
    
    1. 继承ContentProvider,实现方法
    方法 说明
    onCreate 可以作数据库的初始化操作
    insert 插入数据,返回Uri对象
    update 更新数据,返回影响的行数
    delete 删除数据,返回影响的行数
    query 返回Cursor对象
    getType 返回MIME type,单条数据以vnd.android.cursor.item 开头 ,多条数据以 vnd.android.cursor.dir/ 开头.; 或者是null
    call 实现自定义的数据访问操作
    1. 在AndroidManifest.xml中配置
       <provider
          android:name=".contentprovider.OrderProvider" //类名
          android:authorities="aqrlei.OrderProvider" //ContentProvider 的唯一标识
          android:process=":remote" // 指定进程名
          android:permission="com.aqrlei.permission.PROVIDER" // 自定义使用权限(读写权限,也可以分别指定读写权限)
           />
      
      
    2. 使用
        // ORDER_URL中包含authorities,根据这个ContentProvider唯一标识找到对应的ContentProvider
        contentResolver.query(OrderProvider.ORDER_URL, null, null, null, null)
      
  • 分析
    NOTE(可以根据以下所列查看源码):

    1. 获取contentResolver, ContextImpl#ApplicationContentResolver
    2. 获取ContentProvider的顺序为 ApplicationContentResolver#acquireProvider -> ActivityThread#acquireProvider -> ContentProviderHolder#provider
    // ContentProviderHolder
       private ContentProviderHolder(Parcel source) {
         info = ProviderInfo.CREATOR.createFromParcel(source);
         provider = ContentProviderNative.asInterface(
                 source.readStrongBinder());
         connection = source.readStrongBinder();
         noReleaseNeeded = source.readInt() != 0;
      }  
    
    //ContentProviderNative
      static public IContentProvider asInterface(IBinder obj){
         if (obj == null) {
             return null;
         }
         IContentProvider in =
             (IContentProvider)obj.queryLocalInterface(descriptor);
         if (in != null) {
             return in;
         }
    
         return new ContentProviderProxy(obj);
     }
    
    
    1. ContentProviderNative#ContentProviderProxy是客户端通过ApplicationContentResolver获取的并运行在客户端的
    2. ContentProvider#Transport是运行在服务端的

Socket

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

  <uses-permission android:name="android.permission.INTERNET" />
  <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
  • 服务端
       private inner class TcpServer : Runnable {
         private var client: Socket? = null
         override fun run() {
             val serverSocket: ServerSocket
             try {
                 //监听 本地端口
                 serverSocket = ServerSocket(9999)
               } catch (e: IOException) {
                 e.printStackTrace()
                 return
               }
             while (!serviceDestroyed) {//服务未停止
                 try {
                     Log.d("Socket", "TcpServer Run")
                     client = serverSocket.accept()
                     thread {
                         try {
                             //客户端连接上了
                             responseClient(client!!)
                         } catch (e: IOException) {
                             e.printStackTrace()
                             Log.d("Socket", "TcpServer Thread Run onError ${e.message}")
                         }
                     }
                 } catch (e: IOException) {
                     Log.d("Socket", "TcpServer Run OnError ${e.message}")
                     e.printStackTrace()
                 }
             }
             client?.close()
             Log.d("Socket", "TcpServer Client close $serviceDestroyed")
         }
    
         @Throws(IOException::class)
         private fun responseClient(client: Socket) {
             val inReader: BufferedReader? = BufferedReader(InputStreamReader(client.getInputStream()))
             val outWriter: PrintWriter? = PrintWriter(BufferedWriter(OutputStreamWriter(client.getOutputStream())), true)
             Log.d("Socket", "TcpServer response $serviceDestroyed")
             while (!serviceDestroyed) {
                 Log.d("Socket", "TcpServer response before $serviceDestroyed")
                 //读取客户端发来的信息
                 val str = inReader?.readLine()
                 if (str != null) {
                     Log.d("Socket", "TcpServer response $str")
                     val time = SimpleDateFormat("hh:mm:ss.SSS", Locale.ROOT).format(System.currentTimeMillis())
                     //给客户端写信息
                     outWriter?.println("通过Socket回传:$str-$time")
                     sendMsgChange(Info("AIDL回传:$str-$time", -1))
                     /*outWriter?.close()
                     inReader?.close()*/
                 }
             }
             Log.d("Socket", "TcpServer response after $serviceDestroyed")
         }
      }
    
    
  • 客户端
       private var mClientSocket: Socket? = null
       private var mPrintWriter: PrintWriter? = null
       private fun connectTcpServer() {
           var socket: Socket? = null
           while (socket == null) {  // 连接失败的话,每隔1秒重连一次
               try {
                   //连接到本地端口
                   socket = Socket("localhost", 9999)
                   mClientSocket = socket
                   // 用于向服务端写入数据
                   mPrintWriter = PrintWriter(BufferedWriter(OutputStreamWriter(socket.getOutputStream())), true)
                   //通过Handler通知Socket连接成功
                   clientMessengerHandler.sendEmptyMessage(LOCAL_SOCKET_CONNECTED)
               } catch (e: IOException) {
                   SystemClock.sleep(1000)
               }
           }
           try {
               //获取服务端发送的数据
               BufferedReader(InputStreamReader(socket.getInputStream())).use {
                   while (!this.isFinishing) {
                       var msg = it.readLine()
                       if (!msg.isNullOrEmpty()) {
                           msg = "$msg-$time"
                           //通过Handler将数据发送到UI线程处理
                           clientMessengerHandler.obtainMessage(LOCAL_SOCKET_SEND_MESSAGE, msg).sendToTarget()
                       }
                   }
               }
           } catch (e: IOException) {
               e.printStackTrace()
           }
       }
        //向服务端发送消息
       mPrintWriter?.println("Hello Socket:$time")
    
       // onDestroy的时候处理socket相关
       override fun onDestroy() {
         if (mClientSocket != null) {
             try {
                 mPrintWriter?.close()
                 mClientSocket?.shutdownInput()
                 mClientSocket?.close()
             } catch (e: IOException) {
                 e.printStackTrace()
             }
         }
         super.onDestroy()
     }
    

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

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 194,491评论 5 459
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 81,856评论 2 371
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 141,745评论 0 319
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,196评论 1 263
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 61,073评论 4 355
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,112评论 1 272
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,531评论 3 381
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,215评论 0 253
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,485评论 1 290
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,578评论 2 309
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,356评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,215评论 3 312
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,583评论 3 299
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,898评论 0 17
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,174评论 1 250
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,497评论 2 341
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,697评论 2 335

推荐阅读更多精彩内容