Android IPC(進(jìn)程間通信)

關(guān)于Android 進(jìn)程間通信的基本知識(shí)以及Android中進(jìn)程間通信的方式

IPC簡介與基礎(chǔ)概念

基礎(chǔ)

  • android 開啟多進(jìn)程的方式
    1. 指定四大組件的 android:process 屬性
    2. 通過Jninative層 fork一個(gè)進(jìn)程
  • "anroid:process" 進(jìn)程命名的含義
    1. ":"開頭表示當(dāng)前進(jìn)程名前面要加上包名
    2. ":" 表示是當(dāng)前應(yīng)用的私有進(jìn)程爸业,其它應(yīng)用的組件不可以和它跑在同一個(gè)進(jìn)程中
    3. 沒有 ":" 表示是全局進(jìn)程避乏,其它應(yīng)用的組件在ShareUID并且簽名相同的情況下也可以跑在這個(gè)進(jìn)程
  • 多進(jìn)程會(huì)帶來的問題
    1. 靜態(tài)成員和單例模式失效
    2. Application 會(huì)創(chuàng)建多次
    3. SharedPreference 變得不可靠
    4. 線程同步機(jī)制失效
  • Serializable , Parcelable
  1. Serializable

    /**
     * 父類沒有實(shí)現(xiàn)Serializable,子類實(shí)現(xiàn)了Serializable祭务,父類的屬性不會(huì)參與序列化
     */
    open class Parent(var parentName: String) {
        constructor() : this("parent")
    }
    
    /**
     * 定義[readObject] 和[writeObject] 可以對(duì)序列化進(jìn)行一些自定義的操作
     */
    data class Child(
        var childName: String = "Bob",
        //有Transient標(biāo)識(shí)的 不參與序列化
        @Transient var childAge: Int = 5,
        var password: String = "password"
    ) : Serializable, Parent() {
        companion object {
            /**
             * 1.在反序列化的時(shí)候,只有這個(gè)值相同的情況下才有可能反序列化成功
             * 2.可以不指定這個(gè)值,系統(tǒng)會(huì)有默認(rèn)值
             * 3.在默認(rèn)值的情況下,如果增加和刪除了成員變量,反序列化不能成功吕粗,默認(rèn)值改變了
             * 4.如果類名發(fā)生改變或成員的類型發(fā)生改變,不管這個(gè)值是相同旭愧,反序列化都不能成功
             * */
            private const val serialVersionUID = 2L
        }
        /**
         * 反序列化操作
         * 默認(rèn)會(huì)調(diào)用 [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")
            }
        }
    
        /**
         * 序列化操作
         * 默認(rèn)會(huì)調(diào)用 [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

    /**
     * 必須與相應(yīng)的AIDL在同個(gè)包下
     */
    data class Info(var data: String,
                    var times: Int) : Parcelable {
        companion object CREATOR : Parcelable.Creator<Info> {
            /**
             * 從序列化的對(duì)象中創(chuàng)建原始對(duì)象
             * */
            override fun createFromParcel(source: Parcel): Info = Info(source)
    
            /**
             * 創(chuàng)建指定長度的原始對(duì)象數(shù)組
             * */
            override fun newArray(size: Int): Array<Info?> = arrayOfNulls(size)
        }
    
        constructor(parcel: Parcel) : this(
                parcel.readString() ?: "",
                parcel.readInt())
    
        /**
         * 將當(dāng)前對(duì)象寫入序列化對(duì)象中
         * @param flags  1表示當(dāng)前對(duì)象需要最為返回值返回颅筋,不能立即釋放,幾乎所有情況都為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(遠(yuǎn)程請(qǐng)求調(diào)用)的時(shí)候输枯,會(huì)掛起當(dāng)前線程直到RPC返回后才會(huì)重新喚醒议泵,所以客戶端啟動(dòng)的RPC是耗時(shí)任務(wù)時(shí)務(wù)必要新開一個(gè)線程
2. 服務(wù)端相關(guān)的方法已經(jīng)運(yùn)行在Binder的線程池中,就算是耗時(shí)任務(wù)也不需要新開一個(gè)線程
3. 同理在服務(wù)端發(fā)起RPC時(shí)也會(huì)掛起服務(wù)端的當(dāng)前線程桃熄,此時(shí)調(diào)用到的客戶端的方法運(yùn)行在客戶端的Binder線程池中先口,涉及到UI操作時(shí)要做切換線程處理(可以使用Handler);如果是耗時(shí)操作,服務(wù)端務(wù)必不要在主線程中發(fā)起此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的唯一標(biāo)識(shí)
            */
           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
            * 將服務(wù)端的Binder對(duì)象轉(zhuǎn)換成客戶端所需的AIDL接口對(duì)象瞳收。
            * 如果是同一進(jìn)程中碉京,此方法返回的是服務(wù)端的Stub對(duì)象本生
            * 如果是不同進(jìn)程,返回的是系統(tǒng)封裝后的Stub.proxy對(duì)象
            */
           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 返回當(dāng)前的 Binder對(duì)象
            */
           @Override
           public android.os.IBinder asBinder() {
               return this;
           }
   
           /**
            * 運(yùn)行在服務(wù)端的Binder線程池中螟深,當(dāng)客戶端發(fā)起跨進(jìn)程請(qǐng)求時(shí)谐宙,遠(yuǎn)程請(qǐng)求通過系統(tǒng)底層封裝后交由此方法處理
            * {@link android.os.Binder}
            * @param code  服務(wù)端通過code可以確定所請(qǐng)求的方法是什么
            * @param data  從data中取出目標(biāo)方法所需要的參數(shù)(如果需要的話),然后執(zhí)行目標(biāo)方法
            * @param reply 在reply中寫入返回值(如果需要的話)
            * @return 返回 false ,客戶端的請(qǐng)求就會(huì)失敗
            * */
           @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;
               }
   
               /**
                * 此方法運(yùn)行在客戶端界弧。首先會(huì)創(chuàng)建兩個(gè)對(duì)象卧惜,輸入型Parcel:"_data",輸出型Parcel:"_reply",如果有返回值
                * 會(huì)創(chuàng)建一個(gè)返回值對(duì)象。其次夹纫,將方法需要的參數(shù)寫入_data(如果有參數(shù)的話);接著調(diào)用transact
                * 發(fā)起RPC(遠(yuǎn)程過程調(diào)用)請(qǐng)求同時(shí)當(dāng)前線程掛起,服務(wù)端的onTransact會(huì)被調(diào)用设凹,直到RPC過程返回后舰讹,當(dāng)前線程
                * 繼續(xù)執(zhí)行,并從_reply中取出結(jié)果,如果有返回值的話闪朱,賦值給返回值對(duì)象月匣。然后_data,_reply調(diào)用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工作機(jī)制

    Binder工作機(jī)制

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

     private val mServiceConn =  object : ServiceConnection {
         override fun onServiceConnected(name: ComponentName?, service: IBinder?) {
             mBinder = IBinderPool.Stub.asInterface(service)
             //設(shè)置死亡監(jiān)聽
             service?.linkToDeath(mDeathRecipient,0)
         }
    
         override fun onServiceDisconnected(name: ComponentName?) { }
     }
     private val mDeathRecipient = object : IBinder.DeathRecipient {
         /**
          * 遠(yuǎn)程服務(wù)端由于某種原因終止奋姿,此時(shí)Binder"死亡",當(dāng)Binder死亡時(shí)锄开,
          * 會(huì)回調(diào)此方法,在此處調(diào)用unlinkToDeath且將Binder置為NULL,
          * 并重新綁定服務(wù)
          * */
         override fun binderDied() {
             mBinder?.asBinder()?.unlinkToDeath(this, 0)
             mBinder = null
             //TODO bindService() 重新綁定遠(yuǎn)程服務(wù)
         }
     }   
    
    

IPC方式

Bundle

Bundle 結(jié)合 Intent一起使用称诗,一般用于四大組件(Activity萍悴、Service、Broadcast)的進(jìn)程間的通信
  • 源碼分析
    NOTE:
    1. Bundle繼承BaseBundle類,實(shí)現(xiàn)了序列化Parcelable接口
    2. 在putXxx和getXxx的時(shí)候癣诱,第一步都要先調(diào)用unparcel方法计维,主要是對(duì)Parcel mParcelledData進(jìn)行操作,然后再從map中獲取數(shù)據(jù)
    3. Bundle中數(shù)據(jù)都存儲(chǔ)在ArrayMap<String, Object> mMap中撕予,如果mParcelledData不為null的時(shí)候鲫惶, mMap就是null,數(shù)據(jù)存儲(chǔ)為包含Bundle的Parcel。當(dāng)數(shù)據(jù)unparcelled,mParcelledData置為null, map中存儲(chǔ)數(shù)據(jù)
     public void putInt(@Nullable String key, int value) {
         unparcel();
         mMap.put(key, value);
     }  
      
     void unparcel() {
         synchronized (this) {
             final Parcel source = mParcelledData;
             // 如果不為null实抡,說明數(shù)據(jù)都存在一個(gè)包含Bundle的Parcel即mParcelledData中欠母,此時(shí)map為null
             // 就要根據(jù)mParcelledData的情況創(chuàng)建(如果mMap為null的話)或更新ArrayMap,并根據(jù)mParcelledData寫入內(nèi)容
             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. 通過讀/寫磁盤中的同一個(gè)文件實(shí)現(xiàn)進(jìn)程間的數(shù)據(jù)交換
  2. 由于Linux系統(tǒng)并發(fā)讀寫文件可以沒有限制的進(jìn)行吆寨,不適合并發(fā)的情景
  3. SharedPreference雖然數(shù)據(jù)也是存在磁盤中的文件里赏淌,但是系統(tǒng)利用Map<String, Object> mMap在內(nèi)存中進(jìn)行了緩存,這一點(diǎn)會(huì)導(dǎo)致問題
 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 寫入內(nèi)容的文件
    * @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 要寫入的可序列化對(duì)象
    * */
   fun writeObject(file: File?, user: User): Boolean {
       return try {
           ObjectOutputStream(FileOutputStream(file)).use {
               it.writeObject(user)
               it.flush()
               true
           }
  
       } catch (e: Exception) {
           false
       }
   }
  
   /**
    * 讀取寫入文件的序列化對(duì)象
    * */
   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的,底層實(shí)現(xiàn)也是AIDL
2. Messenger同樣實(shí)現(xiàn)了Parcelable接口

  • 使用
    • 客戶端

      // 在ServiceConnection#onServiceConnected里面獲取到Binder并創(chuàng)建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通過服務(wù)端的Messenger傳送給服務(wù)端
                      service?.send( Message.obtain(null, IPCService.RECEIVE_FROM_CLIENT_CODE_INIT).apply {
                          replyTo = Messenger(clientMessengerHandler)
                           })
                  }
                 ...
              }
              super.handleMessage(msg)
          }
      }
      
    • 服務(wù)端

         MESSENGER_BINDER_CODE -> { // Binder 連接池鸟废,選用Messenger
      
           // 運(yùn)行在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

  • 支持的數(shù)據(jù)類型

    類型 說明
    基本數(shù)據(jù)類型 int, char,boolean,double,float等
    String和CharSequence
    List 只支持ArrayList傳輸盒延,但是如果List是返回類型缩擂,return了一個(gè)非ArrayList的數(shù)據(jù),Binder會(huì)根據(jù)List的規(guī)范生成一個(gè)ArrayList返回
    Map 只支持HashMap
    Parcelable 支持所有實(shí)現(xiàn)了Parcelable接口的類添寺,自定義的Parcelable詳情(下文)
    AIDL 所用AIDL接口胯盯,自定義的AIDL詳情(下文)
  • 實(shí)現(xiàn)

    • 使用自定義Parcelable作為傳遞的數(shù)據(jù)類型

      1. 用于AIDL的Parcelable實(shí)現(xiàn)類必須與對(duì)應(yīng)的AIDL文件在同一個(gè)包下(Parcelable實(shí)現(xiàn)見基礎(chǔ)部分)
      2. 使用自定義的Parcelable,要據(jù)此創(chuàng)建一個(gè)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作為傳遞的數(shù)據(jù)類型,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. 用與對(duì)回調(diào)方法的管理博脑,由于是跨進(jìn)程通信,經(jīng)過反序列化后票罐,回調(diào)方法的對(duì)象與序列化前的對(duì)象實(shí)際不是同一個(gè)叉趣,這樣就造成不能夠簡單的注冊(cè)和注銷回調(diào)方法。而使用RemoteCallbackList能解決這個(gè)問題该押。
      2. 內(nèi)部實(shí)現(xiàn)了線程同步
      3. Binder死亡后疗杉,能自動(dòng)解除Binder所對(duì)應(yīng)的回調(diào)
      • 使用
      // 注冊(cè)和注銷
       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)
         }
       }
      
       // 回調(diào)
       //必須先調(diào)用這個(gè)方法
       val n = listenerList.beginBroadcast()
       for (i in 0 until n) {
           listenerList.getBroadcastItem(0).msgChange(info)
       }
      
      
      • 源碼
        NOTE:
        1. 注冊(cè)的時(shí)候以底層不變的binder對(duì)象作為key保存到ArrayMap中,注銷的時(shí)候同樣用binder作為key從ArrayMap中移除蚕礼。
        2. 使用的時(shí)候?qū)rrayMap中的數(shù)據(jù)添加到數(shù)組中烟具,然后再從數(shù)組中獲取對(duì)象進(jìn)行回調(diào)的執(zhí)行。
        3. 內(nèi)部使用synchronized關(guān)鍵字實(shí)現(xiàn)了線程同步
        4. 在注冊(cè)時(shí)候使用了linkToDeath() 關(guān)聯(lián)了死亡代理 IBinder.DeathRecipient奠蹬,在觸發(fā)死亡代理的時(shí)候朝聋,移除相關(guān)的回調(diào)對(duì)象。在注銷的時(shí)候unlinkToDeath()
       注冊(cè)和注銷
        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);
                // 關(guān)聯(lián)到IBinder.DeathRecipient接口囤躁,Binder死亡后,移除此回調(diào)
                binder.linkToDeath(cb, 0);
                //序列化和反序列化后冀痕,底層binder對(duì)象是同一個(gè)荔睹,以它作為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中的回調(diào)添加到數(shù)組active(mActiveBroadcast中)
                active[i] = mCallbacks.valueAt(i);
            }
            return N;
          }
        } 
      
        public E getBroadcastItem(int index) {
            //從數(shù)據(jù)mACtiveBroadcast中獲取注冊(cè)的回調(diào)
            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)中的數(shù)據(jù)置為null
                    active[i] = null;
                }
            }
      
            mBroadcastCount = -1;
          }
        }
      
      
    • 連接權(quán)限驗(yàn)證 (表格中的方式和地方無強(qiáng)制關(guān)聯(lián))

    驗(yàn)證權(quán)限的方式 驗(yàn)證權(quán)限的地方
    使用自定義Permission Service#onBind方法中,不通過則返回null
    使用包名等(通過Uid獲取包名) 實(shí)現(xiàn)AIDL的服務(wù)端調(diào)用的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
         }
     }
    
    • 使用示例
      //服務(wù)端
       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實(shí)現(xiàn)的
2. 客戶端的是ContentProviderNative#ContentProviderProxy,服務(wù)端的是ContentProvider#Transport

  • 自定義ContentProvider (Sqlite為例)

      class OrderProvider : ContentProvider() {
      companion object {
          //必須和AndroidManifest.xml中配置的一樣应媚,這是ContentProvider的唯一標(biāo)識(shí)
          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,實(shí)現(xiàn)方法
    方法 說明
    onCreate 可以作數(shù)據(jù)庫的初始化操作
    insert 插入數(shù)據(jù),返回Uri對(duì)象
    update 更新數(shù)據(jù)猜极,返回影響的行數(shù)
    delete 刪除數(shù)據(jù)中姜,返回影響的行數(shù)
    query 返回Cursor對(duì)象
    getType 返回MIME type,單條數(shù)據(jù)以vnd.android.cursor.item 開頭 ,多條數(shù)據(jù)以 vnd.android.cursor.dir/ 開頭.; 或者是null
    call 實(shí)現(xiàn)自定義的數(shù)據(jù)訪問操作
    1. 在AndroidManifest.xml中配置
       <provider
          android:name=".contentprovider.OrderProvider" //類名
          android:authorities="aqrlei.OrderProvider" //ContentProvider 的唯一標(biāo)識(shí)
          android:process=":remote" // 指定進(jìn)程名
          android:permission="com.aqrlei.permission.PROVIDER" // 自定義使用權(quán)限(讀寫權(quán)限,也可以分別指定讀寫權(quán)限)
           />
      
      
    2. 使用
        // ORDER_URL中包含authorities,根據(jù)這個(gè)ContentProvider唯一標(biāo)識(shí)找到對(duì)應(yīng)的ContentProvider
        contentResolver.query(OrderProvider.ORDER_URL, null, null, null, null)
      
  • 分析
    NOTE(可以根據(jù)以下所列查看源碼):

    1. 獲取contentResolver, ContextImpl#ApplicationContentResolver
    2. 獲取ContentProvider的順序?yàn)?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獲取的并運(yùn)行在客戶端的
    2. ContentProvider#Transport是運(yùn)行在服務(wù)端的

Socket

NOTE: 使用Socket需要使用網(wǎng)絡(luò)相關(guān)權(quán)限

  <uses-permission android:name="android.permission.INTERNET" />
  <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
  • 服務(wù)端
       private inner class TcpServer : Runnable {
         private var client: Socket? = null
         override fun run() {
             val serverSocket: ServerSocket
             try {
                 //監(jiān)聽 本地端口
                 serverSocket = ServerSocket(9999)
               } catch (e: IOException) {
                 e.printStackTrace()
                 return
               }
             while (!serviceDestroyed) {//服務(wù)未停止
                 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")
                 //讀取客戶端發(fā)來的信息
                 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
                   // 用于向服務(wù)端寫入數(shù)據(jù)
                   mPrintWriter = PrintWriter(BufferedWriter(OutputStreamWriter(socket.getOutputStream())), true)
                   //通過Handler通知Socket連接成功
                   clientMessengerHandler.sendEmptyMessage(LOCAL_SOCKET_CONNECTED)
               } catch (e: IOException) {
                   SystemClock.sleep(1000)
               }
           }
           try {
               //獲取服務(wù)端發(fā)送的數(shù)據(jù)
               BufferedReader(InputStreamReader(socket.getInputStream())).use {
                   while (!this.isFinishing) {
                       var msg = it.readLine()
                       if (!msg.isNullOrEmpty()) {
                           msg = "$msg-$time"
                           //通過Handler將數(shù)據(jù)發(fā)送到UI線程處理
                           clientMessengerHandler.obtainMessage(LOCAL_SOCKET_SEND_MESSAGE, msg).sendToTarget()
                       }
                   }
               }
           } catch (e: IOException) {
               e.printStackTrace()
           }
       }
        //向服務(wù)端發(fā)送消息
       mPrintWriter?.println("Hello Socket:$time")
    
       // onDestroy的時(shí)候處理socket相關(guān)
       override fun onDestroy() {
         if (mClientSocket != null) {
             try {
                 mPrintWriter?.close()
                 mClientSocket?.shutdownInput()
                 mClientSocket?.close()
             } catch (e: IOException) {
                 e.printStackTrace()
             }
         }
         super.onDestroy()
     }
    

Binder連接池

NOTE:AIDL的實(shí)現(xiàn)方式跟伏,主要原理是:
1. 客戶端發(fā)送對(duì)應(yīng)的Code;
2. 服務(wù)端根據(jù)Code返回對(duì)應(yīng)的IBinder;
3. 客戶端再根據(jù)IBinder將之轉(zhuǎn)換成對(duì)應(yīng)的AIDL接口.

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

   interface IBinderPool {
        IBinder queryBinder(int binderCode);
   }

   //#服務(wù)端
   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方式適用場(chǎng)景(源于Android開發(fā)藝術(shù)探索)

方式 優(yōu)點(diǎn) 缺點(diǎn) 適用場(chǎng)景
Bundle 簡單易用 只能傳輸Bundle支持的數(shù)據(jù) 四大組件間的進(jìn)程間通信
文件共享 簡單易用 不適合高并發(fā)丢胚,無法做到進(jìn)程間實(shí)時(shí)通信 無并發(fā),交換簡單的數(shù)據(jù)受扳,實(shí)時(shí)性不高
Messenger 功能一般携龟,一對(duì)多串行通信,實(shí)時(shí)通信 不能很好的處理高并發(fā), 不支持RPC勘高,數(shù)據(jù)通過Message傳輸,支持的類型有局限性 低并發(fā)的一對(duì)多通信峡蟋,無RPC請(qǐng)求(或無需返回值的RPC)
AIDL 功能強(qiáng)大,一對(duì)多并發(fā)华望,實(shí)時(shí)通信 使用稍微復(fù)雜蕊蝗,需要處理好線程同步 一對(duì)多通信且有RPC需求
ContentProvider 數(shù)據(jù)訪問方面功能強(qiáng)大,一對(duì)多并發(fā)數(shù)據(jù)共享赖舟,Call方法擴(kuò)展其它操作 主要提供數(shù)據(jù)源的CRUD 一對(duì)多進(jìn)程間數(shù)據(jù)共享
Socket 功能強(qiáng)大蓬戚,通過網(wǎng)絡(luò)傳輸字節(jié)流,一對(duì)多并發(fā)實(shí)時(shí)通信 實(shí)現(xiàn)稍微繁瑣宾抓,不支持RPC 網(wǎng)絡(luò)數(shù)據(jù)交換

資源

Android開發(fā)藝術(shù)探索 - 任玉剛

Android-Art-Res-Chapter_2

IPCSample

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末子漩,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子石洗,更是在濱河造成了極大的恐慌幢泼,老刑警劉巖,帶你破解...
    沈念sama閱讀 218,755評(píng)論 6 507
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件讲衫,死亡現(xiàn)場(chǎng)離奇詭異缕棵,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)焦人,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,305評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來重父,“玉大人花椭,你說我怎么就攤上這事》课纾” “怎么了矿辽?”我有些...
    開封第一講書人閱讀 165,138評(píng)論 0 355
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經(jīng)常有香客問我双戳,道長敞掘,這世上最難降的妖魔是什么声离? 我笑而不...
    開封第一講書人閱讀 58,791評(píng)論 1 295
  • 正文 為了忘掉前任,我火速辦了婚禮批狐,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘前塔。我一直安慰自己嚣艇,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,794評(píng)論 6 392
  • 文/花漫 我一把揭開白布华弓。 她就那樣靜靜地躺著食零,像睡著了一般。 火紅的嫁衣襯著肌膚如雪寂屏。 梳的紋絲不亂的頭發(fā)上贰谣,一...
    開封第一講書人閱讀 51,631評(píng)論 1 305
  • 那天,我揣著相機(jī)與錄音迁霎,去河邊找鬼吱抚。 笑死,一個(gè)胖子當(dāng)著我的面吹牛欧引,可吹牛的內(nèi)容都是我干的频伤。 我是一名探鬼主播,決...
    沈念sama閱讀 40,362評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼芝此,長吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼憋肖!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起婚苹,我...
    開封第一講書人閱讀 39,264評(píng)論 0 276
  • 序言:老撾萬榮一對(duì)情侶失蹤岸更,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后膊升,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體怎炊,經(jīng)...
    沈念sama閱讀 45,724評(píng)論 1 315
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,900評(píng)論 3 336
  • 正文 我和宋清朗相戀三年廓译,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了评肆。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 40,040評(píng)論 1 350
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡非区,死狀恐怖瓜挽,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情征绸,我是刑警寧澤久橙,帶...
    沈念sama閱讀 35,742評(píng)論 5 346
  • 正文 年R本政府宣布俄占,位于F島的核電站,受9級(jí)特大地震影響淆衷,放射性物質(zhì)發(fā)生泄漏缸榄。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,364評(píng)論 3 330
  • 文/蒙蒙 一祝拯、第九天 我趴在偏房一處隱蔽的房頂上張望甚带。 院中可真熱鬧,春花似錦鹿驼、人聲如沸欲低。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,944評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽砾莱。三九已至,卻和暖如春凄鼻,著一層夾襖步出監(jiān)牢的瞬間腊瑟,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,060評(píng)論 1 270
  • 我被黑心中介騙來泰國打工块蚌, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留闰非,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 48,247評(píng)論 3 371
  • 正文 我出身青樓峭范,卻偏偏與公主長得像财松,于是被迫代替她去往敵國和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子纱控,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,979評(píng)論 2 355

推薦閱讀更多精彩內(nèi)容