欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

Android進(jìn)階手寫IPC通信框架告別繁瑣AIDL

 更新時(shí)間:2023年01月29日 09:04:48   作者:layz4android  
這篇文章主要為大家介紹了Android進(jìn)階手寫IPC通信框架告別繁瑣AIDL實(shí)現(xiàn)詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪

正文

對于進(jìn)程間通信,很多項(xiàng)目中可能根本沒有涉及到多進(jìn)程,很多公司的app可能就一個(gè)主進(jìn)程,但是對于進(jìn)程間通信,我們也是必須要了解的。

如果在Android中想要實(shí)現(xiàn)進(jìn)程間通信,有哪些方式呢?

(1)發(fā)廣播(sendBroadcast):e.g. 兩個(gè)app之間需要通信,那么可以通過發(fā)送廣播的形式進(jìn)行通信,如果只想單點(diǎn)通信,可以指定包名。但是這種方式存在的弊端在于發(fā)送方無法判斷接收方是否接收到了廣播,類似于UDP的通信形式,而且存在丟數(shù)據(jù)的形式;

(2)Socket通信:這種屬于Linux層面的進(jìn)程間通信了,除此之外,還包括管道、信號量等,像傳統(tǒng)的IPC進(jìn)程間通信需要數(shù)據(jù)二次拷貝,這種效率是最低的;

(3)AIDL通信:這種算是Android當(dāng)中主流的進(jìn)程間通信方案,通過Service + Binder的形式進(jìn)行通信,具備實(shí)時(shí)性而且能夠通過回調(diào)得知接收方是否收到數(shù)據(jù),弊端在于需要管理維護(hù)aidl接口,如果不同業(yè)務(wù)方需要使用不同的aidl接口,維護(hù)的成本會越來越高。

那么本篇文章并不是說完全丟棄掉AIDL,它依然不失為一個(gè)很好的進(jìn)程間通信的手段,只是我會封裝一個(gè)適用于任意業(yè)務(wù)場景的IPC進(jìn)程間通訊框架,這個(gè)也是我在自己的項(xiàng)目中使用到的,不需要維護(hù)很多的AIDL接口文件。

有需要源碼的伙伴,可以去我的github首頁獲取 FastIPC源碼地址,分支:feature/v0.0.1-snapshot,有幫助的話麻煩給點(diǎn)個(gè)star??????

1 服務(wù)端 - register

首先這里先說明一下,就是對于傳統(tǒng)的AIDL使用方式,這里就不再過多介紹了,這部分還是比較簡單的,有興趣的伙伴們可以去前面的文章中查看,本文將著重介紹框架層面的邏輯。

那么IPC進(jìn)程間通信,需要兩個(gè)端:客戶端和服務(wù)端。服務(wù)端會提供一個(gè)注冊方法,例如客戶端定義的一些服務(wù),通過向服務(wù)端注冊來做一個(gè)備份,當(dāng)客戶端調(diào)用服務(wù)端某個(gè)方法的時(shí)候來返回值。

object IPC {
    //==========================================
    /**
     * 服務(wù)端暴露的接口,用于注冊服務(wù)使用
     */
    fun register(service: Class<*>) {
        Registry.instance.register(service)
    }
}

其實(shí)在注冊的時(shí)候,我們的目的肯定是能夠方便地拿到某個(gè)服務(wù),并且能夠調(diào)用這個(gè)服務(wù)提供的方法,拿到我想要的值;所以在定義服務(wù)的時(shí)候,需要注意以下兩點(diǎn):

(1)需要定義一個(gè)與當(dāng)前服務(wù)一一對應(yīng)的serviceId,通過serviceId來獲取服務(wù)的實(shí)例;

(2)每個(gè)服務(wù)當(dāng)中定義的方法同樣需要對應(yīng)起來,以便拿到服務(wù)對象之后,通過反射調(diào)用其中的方法。

所以在注冊的時(shí)候,需要從這兩點(diǎn)入手。

1.1 定義服務(wù)唯一標(biāo)識serviceId

@Target(AnnotationTarget.TYPE)
@Retention(AnnotationRetention.RUNTIME)
annotation class ServiceId(
    val name: String
)

一般來說,如果涉及到反射,最常用的就是通過注解給Class做標(biāo)記,因?yàn)橥ㄟ^反射能夠拿到類上標(biāo)記的注解,就能夠拿到對應(yīng)的serviceId。

class Registry {
    //=======================================
    /**用于存儲 serviceId 對應(yīng)的服務(wù) class對象*/
    private val serviceMaps: ConcurrentHashMap<String, Class<*>> by lazy {
        ConcurrentHashMap()
    }
    /**用于存儲 服務(wù)中全部的方法*/
    private val methodsMap: ConcurrentHashMap<Class<*>, ConcurrentHashMap<String, Method>> by lazy {
        ConcurrentHashMap()
    }
    //=======================================
    /**
     * 服務(wù)端注冊方法
     * @param service 服務(wù)class對象
     */
    fun register(service: Class<*>) {
        // 獲取serviceId與服務(wù)一一對應(yīng)
        val serviceIdAnnotation = service.getAnnotation(ServiceId::class.java)
            ?: throw IllegalArgumentException("只有標(biāo)記@ServiceId的服務(wù)才能夠被注冊")
        //獲取serviceId
        val name = serviceIdAnnotation.name
        serviceMaps[name] = service
        //temp array
        val methods: ConcurrentHashMap<String, Method> = ConcurrentHashMap()
        // 獲取服務(wù)當(dāng)中的全部方法
        for (method in service.declaredMethods) {
            //這里需要注意,因?yàn)榉椒ㄖ写嬖谥剌d方法,所以不能把方法名當(dāng)做key,需要加上參數(shù)
            val buffer = StringBuffer()
            buffer.append(method.name).append("(")
            val params = method.parameterTypes
            if (params.size > 0) {
                buffer.append(params[0].name)
            }
            for (index in 1 until params.size) {
                buffer.append(",").append(params[index].name)
            }
            buffer.append(")")
            //保存
            methods[buffer.toString()] = method
        }
        //存入方法表
        methodsMap[service] = methods
    }
    companion object {
        val instance by lazy { Registry() }
    }
}

通過上面的register方法,當(dāng)傳入定義的服務(wù)class對象的時(shí)候,首先獲取到服務(wù)上標(biāo)記的@ServiceId注解,注意這里如果要注冊必須標(biāo)記,否則直接拋異常;拿到serviceId之后,存入到serviceMaps中。

然后需要獲取服務(wù)中的全部方法,因?yàn)榭紤]到重載方法的存在,所以不能單單以方法名作為key,而是需要把參數(shù)也加上,因此這里做了一個(gè)邏輯就是將方法名與參數(shù)名組合一個(gè)key,存入到方法表中。

這樣注冊任務(wù)就完成了,其實(shí)還是比較簡單的,關(guān)鍵在于完成2個(gè)表:服務(wù)表和方法表的初始化以及數(shù)據(jù)存儲功能

1.2 使用方式

@ServiceId("UserManagerService")
interface IUserManager {
    fun getUserInfo(): User?
    fun setUserInfo(user: User)
    fun getUserId(): Int
    fun setUserId(id: Int)
}

假設(shè)項(xiàng)目中有一個(gè)用戶信息管理的服務(wù),這個(gè)服務(wù)用于給所有的App提供用戶信息查詢。

@ServiceId("UserManagerService")
class UserManager : IUserManager {
    private var user: User? = null
    private var userId: Int = 0
    override fun getUserInfo(): User? {
        return user
    }
    override fun setUserInfo(user: User) {
        this.user = user
    }
    override fun getUserId(): Int {
        return userId
    }
    override fun setUserId(id: Int) {
        this.userId = id
    }
}

用戶中心可以注冊這個(gè)服務(wù),并且調(diào)用setUserInfo方法保存用戶信息,那么其他App(客戶端)連接這個(gè)服務(wù)之后,就可以調(diào)用getUserInfo這個(gè)方法,獲取用戶信息,從而完成進(jìn)程間通信。

2023-01-23 22:15:54.729 13361-13361/com.lay.learn.asm E/TAG: entrySet key class com.lay.learn.asm.binder.UserManager
2023-01-23 22:15:54.729 13361-13361/com.lay.learn.asm E/TAG: mapValue key setUserInfo(com.lay.learn.asm.binder.User)
2023-01-23 22:15:54.729 13361-13361/com.lay.learn.asm E/TAG: mapValue value public void com.lay.learn.asm.binder.UserManager.setUserInfo(com.lay.learn.asm.binder.User)
2023-01-23 22:15:54.729 13361-13361/com.lay.learn.asm E/TAG: mapValue key getUserInfo()
2023-01-23 22:15:54.729 13361-13361/com.lay.learn.asm E/TAG: mapValue value public com.lay.learn.asm.binder.User com.lay.learn.asm.binder.UserManager.getUserInfo()
2023-01-23 22:15:54.729 13361-13361/com.lay.learn.asm E/TAG: mapValue key getUserId()
2023-01-23 22:15:54.729 13361-13361/com.lay.learn.asm E/TAG: mapValue value public int com.lay.learn.asm.binder.UserManager.getUserId()
2023-01-23 22:15:54.729 13361-13361/com.lay.learn.asm E/TAG: mapValue key setUserId(int)
2023-01-23 22:15:54.729 13361-13361/com.lay.learn.asm E/TAG: mapValue value public void com.lay.learn.asm.binder.UserManager.setUserId(int)

我們看調(diào)用register方法之后,每個(gè)方法的key值都是跟參數(shù)綁定在一起,這樣服務(wù)端注冊就完成了。

2 客戶端與服務(wù)端的通信協(xié)議

對于客戶端的連接,其實(shí)就是綁定服務(wù),那么這里就會使用到AIDL通信,但是跟傳統(tǒng)的相比,我們是將AIDL封裝到框架層內(nèi)部,對于用戶來說是無感知的。

2.1 創(chuàng)建IPCService

這個(gè)服務(wù)就是用來完成進(jìn)程間通信的,客戶端需要與這個(gè)服務(wù)建立連接,通過服務(wù)端分發(fā)消息,或者接收客戶端發(fā)送來的消息。

abstract class IPCService : Service() {
    override fun onBind(intent: Intent?): IBinder? {
        return null
    }
}

這里我定義了一個(gè)抽象的Service基類,為啥要這么做,前面我們提到過是因?yàn)檎麄€(gè)項(xiàng)目中不可能只有一個(gè)服務(wù),因?yàn)闃I(yè)務(wù)眾多,為了保證單一職責(zé),需要?jiǎng)澐植煌念愋?,所以在框架中會衍生多個(gè)實(shí)現(xiàn)類,不同業(yè)務(wù)方可以注冊這些服務(wù),當(dāng)然也可以自定義服務(wù)繼承IPCService。

class IPCService01 : IPCService() {
}

在IPCService的onBind需要返回一個(gè)Binder對象,因此需要?jiǎng)?chuàng)建aidl文件。

2.2 定義通訊協(xié)議

像我們在請求接口的時(shí)候,通常也是向服務(wù)端發(fā)起一個(gè)請求(Request),然后得到服務(wù)端的一個(gè)響應(yīng)(Response),因此在IPC通信的的時(shí)候,也可以根據(jù)這種方式建立通信協(xié)議。

data class Request(
    val type: Int,
    val serviceId: String?,
    val methodName: String?,
    val params: Array&lt;Parameters&gt;?
) : Serializable, Parcelable {
    //=====================================
    /**請求類型*/
    //獲取實(shí)例的對象
    val GET_INSTANCE = "getInstance"
    //執(zhí)行方法
    val INVOKE_METHOD = "invokeMethod"
    //=======================================
    constructor(parcel: Parcel) : this(
        parcel.readInt(),
        parcel.readString(),
        parcel.readString(),
        parcel.createTypedArray(Parameters.CREATOR)
    )
    override fun writeToParcel(parcel: Parcel, flags: Int) {
        parcel.writeInt(type)
        parcel.writeString(serviceId)
        parcel.writeString(methodName)
    }
    override fun describeContents(): Int {
        return 0
    }
    override fun equals(other: Any?): Boolean {
        if (this === other) return true
        if (javaClass != other?.javaClass) return false
        other as Request
        if (type != other.type) return false
        if (serviceId != other.serviceId) return false
        if (methodName != other.methodName) return false
        if (params != null) {
            if (other.params == null) return false
            if (!params.contentEquals(other.params)) return false
        } else if (other.params != null) return false
        return true
    }
    override fun hashCode(): Int {
        var result = type
        result = 31 * result + (serviceId?.hashCode() ?: 0)
        result = 31 * result + (methodName?.hashCode() ?: 0)
        result = 31 * result + (params?.contentHashCode() ?: 0)
        return result
    }
    companion object CREATOR : Parcelable.Creator&lt;Request&gt; {
        override fun createFromParcel(parcel: Parcel): Request {
            return Request(parcel)
        }
        override fun newArray(size: Int): Array&lt;Request?&gt; {
            return arrayOfNulls(size)
        }
    }
}

對于客戶端來說,致力于發(fā)起請求,請求實(shí)體類Request參數(shù)介紹如下:

type表示請求的類型,包括兩種分別是:執(zhí)行靜態(tài)方法和執(zhí)行普通方法(考慮到反射傳參);

serviceId表示請求的服務(wù)id,要請求哪個(gè)服務(wù),便可以獲取到這個(gè)服務(wù)的實(shí)例對象,調(diào)用服務(wù)中提供的方法;

methodName表示要請求的方法名,也是在serviceId服務(wù)中定義的方法;

params表示請求的方法參數(shù)集合,我們在服務(wù)端注冊的時(shí)候,方法名 + 參數(shù)名 作為key,因此需要知道請求的方法參數(shù),以便獲取到Method對象。

data class Response(
    val value:String?,
    val result:Boolean
):Parcelable {
    @SuppressLint("NewApi")
    constructor(parcel: Parcel) : this(
        parcel.readString(),
        parcel.readBoolean()
    )
    override fun writeToParcel(parcel: Parcel, flags: Int) {
        parcel.writeString(value)
        parcel.writeByte(if (result) 1 else 0)
    }
    override fun describeContents(): Int {
        return 0
    }
    companion object CREATOR : Parcelable.Creator<Response> {
        override fun createFromParcel(parcel: Parcel): Response {
            return Response(parcel)
        }
        override fun newArray(size: Int): Array<Response?> {
            return arrayOfNulls(size)
        }
    }
}

對于服務(wù)端來說,在接收到請求之后,需要針對具體的請求返回相應(yīng)的結(jié)果,Response實(shí)體類參數(shù)介紹:

result表示請求成功或者失??;

value表示服務(wù)端返回的結(jié)果,是一個(gè)json字符串。

因此定義aidl接口文件如下,輸入一個(gè)請求之后,返回一個(gè)服務(wù)端的響應(yīng)。

interface IIPCServiceInterface {
    Response send(in Request request);
}

這樣IPCService就可以將aidl生成的Stub類作為Binder對象返回。

abstract class IPCService : Service() {
    override fun onBind(intent: Intent?): IBinder? {
        return BINDERS
    }
    companion object BINDERS : IIPCServiceInterface.Stub() {
        override fun send(request: Request?): Response? {
            when(request?.type){
                REQUEST.GET_INSTANCE.ordinal->{
                }
                REQUEST.INVOKE_METHOD.ordinal->{
                }
            }
            return null
        }
    }
}

2.3 內(nèi)部通訊協(xié)議完善

當(dāng)客戶端發(fā)起請求,想要執(zhí)行某個(gè)方法的時(shí)候,首先服務(wù)端會先向Registery中查詢注冊的服務(wù),從而找到這個(gè)要執(zhí)行的方法,這個(gè)流程是在內(nèi)部完成。

override fun send(request: Request?): Response? {
    //獲取服務(wù)對象id
    val serviceId = request?.serviceId
    val methodName = request?.methodName
    val params = request?.params
    // 反序列化拿到具體的參數(shù)類型
    val neededParams = parseParameters(params)
    val method = Registry.instance.findMethod(serviceId, methodName, neededParams)
    Log.e("TAG", "method $method")
    Log.e("TAG", "neededParams $neededParams")
    when (request?.type) {
        REQUEST_TYPE.GET_INSTANCE.ordinal -> {
            //==========執(zhí)行靜態(tài)方法
            try {
                var instance: Any? = null
                instance = if (neededParams == null || neededParams.isEmpty()) {
                    method?.invoke(null)
                } else {
                    method?.invoke(null, neededParams)
                }
                if (instance == null) {
                    return Response("instance == null", -101)
                }
                //存儲實(shí)例對象
                Registry.instance.setServiceInstance(serviceId ?: "", instance)
                return Response(null, 200)
            } catch (e: Exception) {
                return Response("${e.message}", -102)
            }
        }
        REQUEST_TYPE.INVOKE_METHOD.ordinal -> {
            //==============執(zhí)行普通方法
            val instance = Registry.instance.getServiceInstance(serviceId)
            if (instance == null) {
                return Response("instance == null ", -103)
            }
            //方法執(zhí)行返回的結(jié)果
            return try {
                val result = if (neededParams == null || neededParams.isEmpty()) {
                    method?.invoke(instance)
                } else {
                    method?.invoke(instance, neededParams)
                }
                Response(gson.toJson(result), 200)
            } catch (e: Exception) {
                Response("${e.message}", -104)
            }
        }
    }
    return null
}

當(dāng)客戶端發(fā)起請求時(shí),會將請求的參數(shù)封裝到Request中,在服務(wù)端接收到請求后,就會解析這些參數(shù),變成Method執(zhí)行時(shí)需要傳入的參數(shù)。

private fun parseParameters(params: Array<Parameters>?): Array<Any?>? {
    if (params == null || params.isEmpty()) {
        return null
    }
    val objects = arrayOfNulls<Any>(params.size)
    params.forEachIndexed { index, parameters ->
        objects[index] =
            gson.fromJson(parameters.value, Class.forName(parameters.className))
    }
    return objects
}

例如用戶中心調(diào)用setUserInfo方法時(shí),需要傳入一個(gè)User實(shí)體類,如下所示:

UserManager().setUserInfo(User("ming",25))

那么在調(diào)用這個(gè)方法的時(shí)候,首先會把這個(gè)實(shí)體類轉(zhuǎn)成一個(gè)JSON字符串,例如:

{
    "name":"ming",
    "age":25
}

為啥要”多此一舉“呢?其實(shí)這種處理方式是最快速直接的,轉(zhuǎn)成json字符串之后,能夠最大限度地降低數(shù)據(jù)傳輸?shù)拇笮?,等到服?wù)端處理這個(gè)方法的時(shí)候,再把Request中的params反json轉(zhuǎn)成User對象即可。

fun findMethod(serviceId: String?, methodName: String?, neededParams: Array<Any?>?): Method? {
    //獲取服務(wù)
    val serviceClazz = serviceMaps[serviceId] ?: return null
    //獲取方法集合
    val methods = methodsMap[serviceClazz] ?: return null
    return methods[rebuildParamsFunc(methodName, neededParams)]
}
private fun rebuildParamsFunc(methodName: String?, params: Array<Any?>?): String {
    val stringBuffer = StringBuffer()
    stringBuffer.append(methodName).append("(")
    if (params == null || params.isEmpty()) {
        stringBuffer.append(")")
        return stringBuffer.toString()
    }
    stringBuffer.append(params[0]?.javaClass?.name)
    for (index in 1 until params.size) {
        stringBuffer.append(",").append(params[index]?.javaClass?.name)
    }
    stringBuffer.append(")")
    return stringBuffer.toString()
}

那么在查找注冊方法的時(shí)候就簡單多了,直接抽絲剝繭一層一層取到最終的Method。在拿到Method之后,這里是有2種處理方式,一種是通過靜態(tài)單例的形式拿到實(shí)例對象,并保存在服務(wù)端;另一種就是執(zhí)行普通方法,因?yàn)樵诜瓷涞臅r(shí)候需要拿到類的實(shí)例對象才能調(diào)用,所以才在GET_INSTANCE的時(shí)候存一遍。

3 客戶端 - connect

在第二節(jié)中,我們已經(jīng)完成了通訊協(xié)議的建設(shè),最終一步就是客戶端通過綁定服務(wù),向服務(wù)端發(fā)起通信了。

3.1 bindService

/**
 * 綁定服務(wù)
 *
 */
fun connect(
    context: Context,
    pkgName: String,
    action: String = "",
    service: Class<out IPCService>
) {
    val intent = Intent()
    if (pkgName.isEmpty()) {
        //同app內(nèi)的不同進(jìn)程
        intent.setClass(context, service)
    } else {
        //不同APP之間進(jìn)行通信
        intent.setPackage(pkgName)
        intent.setAction(action)
    }
    //綁定服務(wù)
    context.bindService(intent, IpcServiceConnection(service), Context.BIND_AUTO_CREATE)
}
inner class IpcServiceConnection(val simpleService: Class<out IPCService>) : ServiceConnection {
    override fun onServiceConnected(name: ComponentName?, service: IBinder?) {
        val mService = IIPCServiceInterface.Stub.asInterface(service) as IIPCServiceInterface
        binders[simpleService] = mService
    }
    override fun onServiceDisconnected(name: ComponentName?) {
        //斷連之后,直接移除即可
        binders.remove(simpleService)
    }
}

對于綁定服務(wù)這塊,相信伙伴們也很熟悉了,這個(gè)需要說一點(diǎn)的就是,在Android 5.0以后,啟動服務(wù)不能只依賴action啟動,還需要指定應(yīng)用包名,否則就會報(bào)錯(cuò)。

在服務(wù)連接成功之后,即回調(diào)onServiceConnected方法的時(shí)候,需要拿到服務(wù)端的一個(gè)代理對象,即IIPCServiceInterface的實(shí)例對象,然后存儲在binders集合中,key為綁定的服務(wù)類class對象,value就是對應(yīng)的服務(wù)端的代理對象。

fun send(
    type: Int,
    service: Class<out IPCService>,
    serviceId: String,
    methodName: String,
    params: Array<Parameters>
): Response? {
    //創(chuàng)建請求
    val request = Request(type, serviceId, methodName, params)
    //發(fā)起請求
    return try {
        binders[service]?.send(request)
    } catch (e: Exception) {
        null
    }
}

當(dāng)拿到服務(wù)端的代理對象之后,就可以在客戶端調(diào)用send方法向服務(wù)端發(fā)送消息。

class Channel {
    //====================================
    /**每個(gè)服務(wù)對應(yīng)的Binder對象*/
    private val binders: ConcurrentHashMap<Class<out IPCService>, IIPCServiceInterface> by lazy {
        ConcurrentHashMap()
    }
    //====================================
    /**
     * 綁定服務(wù)
     *
     */
    fun connect(
        context: Context,
        pkgName: String,
        action: String = "",
        service: Class<out IPCService>
    ) {
        val intent = Intent()
        if (pkgName.isEmpty()) {
            intent.setClass(context, service)
        } else {
            intent.setPackage(pkgName)
            intent.setAction(action)
            intent.setClass(context, service)
        }
        //綁定服務(wù)
        context.bindService(intent, IpcServiceConnection(service), Context.BIND_AUTO_CREATE)
    }
    inner class IpcServiceConnection(val simpleService: Class<out IPCService>) : ServiceConnection {
        override fun onServiceConnected(name: ComponentName?, service: IBinder?) {
            val mService = IIPCServiceInterface.Stub.asInterface(service) as IIPCServiceInterface
            binders[simpleService] = mService
        }
        override fun onServiceDisconnected(name: ComponentName?) {
            //斷連之后,直接移除即可
            binders.remove(simpleService)
        }
    }
    fun send(
        type: Int,
        service: Class<out IPCService>,
        serviceId: String,
        methodName: String,
        params: Array<Parameters>
    ): Response? {
        //創(chuàng)建請求
        val request = Request(type, serviceId, methodName, params)
        //發(fā)起請求
        return try {
            binders[service]?.send(request)
        } catch (e: Exception) {
            null
        }
    }
    companion object {
        private val instance by lazy {
            Channel()
        }
        /**
         * 獲取單例對象
         */
        fun getDefault(): Channel {
            return instance
        }
    }
}

3.2 動態(tài)代理獲取接口實(shí)例

回到1.2小節(jié)中,我們定義了一個(gè)IUserManager接口,通過前面我們定義的通信協(xié)議,只要我們獲取了IUserManager的實(shí)例對象,那么就能夠調(diào)用其中的任意普通方法,所以在客戶端需要設(shè)置一個(gè)獲取接口實(shí)例對象的方法。

fun <T> getInstanceWithName(
    service: Class<out IPCService>,
    classType: Class<T>,
    clazz: Class<*>,
    methodName: String,
    params: Array<Parameters>
): T? {
    //獲取serviceId
    val serviceId = clazz.getAnnotation(ServiceId::class.java)
    val response = Channel.getDefault()
        .send(REQUEST.GET_INSTANCE.ordinal, service, serviceId.name, methodName, params)
    Log.e("TAG", "response $response")
    if (response != null && response.result) {
        //請求成功,返回接口實(shí)例對象
        return Proxy.newProxyInstance(
            classType.classLoader,
            arrayOf(classType),
            IPCInvocationHandler()
        ) as T
    }
    return null
}

當(dāng)我們通過客戶端發(fā)送一個(gè)獲取單例的請求后,如果成功了,那么就直接返回這個(gè)接口的單例對象,這里直接使用動態(tài)代理的方式返回一個(gè)接口實(shí)例對象,那么后續(xù)執(zhí)行這個(gè)接口的方法時(shí),會直接走到IPCInvocationHandler的invoke方法中。

class IPCInvocationHandler(
    val service: Class<out IPCService>,
    val serviceId: String?
) : InvocationHandler {
    private val gson = Gson()
    override fun invoke(proxy: Any?, method: Method?, args: Array<out Any>?): Any? {
        //執(zhí)行客戶端發(fā)送方法請求
        val response = Channel.getDefault()
            .send(
                REQUEST.INVOKE_METHOD.ordinal,
                service,
                serviceId,
                method?.name ?: "",
                args
            )
        //拿到服務(wù)端返回的結(jié)果
        if (response != null && response.result) {
            //反序列化得到結(jié)果
            return gson.fromJson(response.value, method?.returnType)
        }
        return null
    }
}

因?yàn)榉?wù)端在拿到Method的返回結(jié)果時(shí),將javabean轉(zhuǎn)換為了json字符串,因此在IPCInvocationHandler中,當(dāng)調(diào)用接口中方法獲取結(jié)果之后,用Gson將json轉(zhuǎn)換為javabean對象,那么就直接獲取到了結(jié)果。

3.3 框架使用

服務(wù)端:

UserManager2.getDefault().setUserInfo(User("ming", 25))
IPC.register(UserManager2::class.java)

同時(shí)在服務(wù)端需要注冊一個(gè)IPCService的實(shí)例,這里用的是IPCService01

<service
    android:name=".UserService"
    android:enabled="true"
    android:exported="true" />
<service
    android:name="com.lay.ipc.service.IPCService01"
    android:enabled="true"
    android:exported="true">
    <intent-filter>
        <action android:name="android.intent.action.GET_USER_INFO" />
    </intent-filter>
</service>

客戶端:

調(diào)用connect方法,需要綁定服務(wù)端的服務(wù),傳入包名和action

IPC.connect(
    this,
    "com.lay.learn.asm",
    "android.intent.action.GET_USER_INFO",
    IPCService01::class.java
)

首先獲取IUserManager的實(shí)例,注意這里要和服務(wù)端注冊的UserManager2是同一個(gè)ServiceId,而且接口、javabean需要存放在與服務(wù)端一樣的文件夾下。

val userManager = IPC.getInstanceWithName(
    IPCService01::class.java,
    IUserManager::class.java,
    "getDefault",
    null
)
val info = userManager?.getUserInfo()

通過動態(tài)代理拿到接口的實(shí)例對象,只要調(diào)用接口中的方法,就會進(jìn)入到InvocationHandler中的invoke方法,在這個(gè)方法中,通過查找服務(wù)端注冊的方法名從而找到對應(yīng)的Method,通過反射調(diào)用拿到UserManager中的方法返回值。

這樣其實(shí)就通過5-6行代碼,就完成了進(jìn)程間通信,是不是比我們在使用AIDL的時(shí)候要方便地許多。

4 總結(jié)

如果我們面對下面這個(gè)類,如果這個(gè)類是個(gè)私有類,外部沒法調(diào)用,想通過反射的方式調(diào)用其中某個(gè)方法。

@ServiceId(name = "UserManagerService")
public class UserManager2 implements IUserManager {
    private static UserManager2 userManager2 = new UserManager2();
    public static UserManager2 getDefault() {
        return userManager2;
    }
    private User user;
    @Nullable
    @Override
    public User getUserInfo() {
        return user;
    }
    @Override
    public void setUserInfo(@NonNull User user) {
        this.user = user;
    }
    @Override
    public int getUserId() {
        return 0;
    }
    @Override
    public void setUserId(int id) {
    }
}

那么我們可以這樣做:

val method = UserManager2::class.java.getDeclaredMethod("getUserInfo")
method.isAccessible = true
method.invoke(this,params)

其實(shí)這個(gè)框架的原理就是上面這幾行代碼所能夠完成的事;通過服務(wù)端注冊的形式,將UserManager2中所有的方法Method收集起來;當(dāng)另一個(gè)進(jìn)程,也就是客戶端想要調(diào)用其中某個(gè)方法的時(shí)候,通過方法名來獲取到對應(yīng)的Method,調(diào)用這個(gè)方法得到最終的返回值。

以上就是Android進(jìn)階手寫IPC通信框架告別繁瑣AIDL的詳細(xì)內(nèi)容,更多關(guān)于Android IPC通信框架的資料請關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

最新評論