關(guān)于Android 進(jìn)程間通信的基本知識(shí)以及Android中進(jìn)程間通信的方式
IPC簡介與基礎(chǔ)概念
基礎(chǔ)
- android 開啟多進(jìn)程的方式
- 指定四大組件的 android:process 屬性
- 通過Jni在native層 fork一個(gè)進(jìn)程
- "anroid:process" 進(jìn)程命名的含義
- ":"開頭表示當(dāng)前進(jìn)程名前面要加上包名
- ":" 表示是當(dāng)前應(yīng)用的私有進(jìn)程爸业,其它應(yīng)用的組件不可以和它跑在同一個(gè)進(jìn)程中
- 沒有 ":" 表示是全局進(jìn)程避乏,其它應(yīng)用的組件在ShareUID并且簽名相同的情況下也可以跑在這個(gè)進(jìn)程
- 多進(jìn)程會(huì)帶來的問題
- 靜態(tài)成員和單例模式失效
- Application 會(huì)創(chuàng)建多次
- SharedPreference 變得不可靠
- 線程同步機(jī)制失效
- Serializable , Parcelable
-
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") } } }
-
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:- Bundle繼承BaseBundle類,實(shí)現(xiàn)了序列化Parcelable接口
- 在putXxx和getXxx的時(shí)候癣诱,第一步都要先調(diào)用unparcel方法计维,主要是對(duì)Parcel mParcelledData進(jìn)行操作,然后再從map中獲取數(shù)據(jù)
- 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:
- 通過讀/寫磁盤中的同一個(gè)文件實(shí)現(xiàn)進(jìn)程間的數(shù)據(jù)交換
- 由于Linux系統(tǒng)并發(fā)讀寫文件可以沒有限制的進(jìn)行吆寨,不適合并發(fā)的情景
- 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ù)類型
- 用于AIDL的Parcelable實(shí)現(xiàn)類必須與對(duì)應(yīng)的AIDL文件在同一個(gè)包下(Parcelable實(shí)現(xiàn)見基礎(chǔ)部分)
- 使用自定義的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:- 用與對(duì)回調(diào)方法的管理博脑,由于是跨進(jìn)程通信,經(jīng)過反序列化后票罐,回調(diào)方法的對(duì)象與序列化前的對(duì)象實(shí)際不是同一個(gè)叉趣,這樣就造成不能夠簡單的注冊(cè)和注銷回調(diào)方法。而使用RemoteCallbackList能解決這個(gè)問題该押。
- 內(nèi)部實(shí)現(xiàn)了線程同步
- 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:- 注冊(cè)的時(shí)候以底層不變的binder對(duì)象作為key保存到ArrayMap中,注銷的時(shí)候同樣用binder作為key從ArrayMap中移除蚕礼。
- 使用的時(shí)候?qū)rrayMap中的數(shù)據(jù)添加到數(shù)組中烟具,然后再從數(shù)組中獲取對(duì)象進(jìn)行回調(diào)的執(zhí)行。
- 內(nèi)部使用synchronized關(guān)鍵字實(shí)現(xiàn)了線程同步
- 在注冊(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 } }
- 繼承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ù)訪問操作 - 在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)限) />
- 使用
// ORDER_URL中包含authorities,根據(jù)這個(gè)ContentProvider唯一標(biāo)識(shí)找到對(duì)應(yīng)的ContentProvider contentResolver.query(OrderProvider.ORDER_URL, null, null, null, null)
-
分析
NOTE(可以根據(jù)以下所列查看源碼):- 獲取contentResolver, ContextImpl#ApplicationContentResolver
- 獲取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); }
- ContentProviderNative#ContentProviderProxy是客戶端通過ApplicationContentResolver獲取的并運(yùn)行在客戶端的
- 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ù)交換 |