Android進(jìn)階手寫(xiě)IPC通信框架告別繁瑣AIDL
正文
對(duì)于進(jìn)程間通信,很多項(xiàng)目中可能根本沒(méi)有涉及到多進(jìn)程,很多公司的app可能就一個(gè)主進(jìn)程,但是對(duì)于進(jìn)程間通信,我們也是必須要了解的。
如果在Android中想要實(shí)現(xiàn)進(jìn)程間通信,有哪些方式呢?
(1)發(fā)廣播(sendBroadcast):e.g. 兩個(gè)app之間需要通信,那么可以通過(guò)發(fā)送廣播的形式進(jìn)行通信,如果只想單點(diǎn)通信,可以指定包名。但是這種方式存在的弊端在于發(fā)送方無(wú)法判斷接收方是否接收到了廣播,類(lèi)似于UDP的通信形式,而且存在丟數(shù)據(jù)的形式;
(2)Socket通信:這種屬于Linux層面的進(jìn)程間通信了,除此之外,還包括管道、信號(hào)量等,像傳統(tǒng)的IPC進(jìn)程間通信需要數(shù)據(jù)二次拷貝,這種效率是最低的;
(3)AIDL通信:這種算是Android當(dāng)中主流的進(jìn)程間通信方案,通過(guò)Service + Binder的形式進(jìn)行通信,具備實(shí)時(shí)性而且能夠通過(guò)回調(diào)得知接收方是否收到數(shù)據(jù),弊端在于需要管理維護(hù)aidl接口,如果不同業(yè)務(wù)方需要使用不同的aidl接口,維護(hù)的成本會(huì)越來(lái)越高。
那么本篇文章并不是說(shuō)完全丟棄掉AIDL,它依然不失為一個(gè)很好的進(jìn)程間通信的手段,只是我會(huì)封裝一個(gè)適用于任意業(yè)務(wù)場(chǎng)景的IPC進(jìn)程間通訊框架,這個(gè)也是我在自己的項(xiàng)目中使用到的,不需要維護(hù)很多的AIDL接口文件。
有需要源碼的伙伴,可以去我的github首頁(yè)獲取 FastIPC源碼地址,分支:feature/v0.0.1-snapshot,有幫助的話麻煩給點(diǎn)個(gè)star??????
1 服務(wù)端 - register
首先這里先說(shuō)明一下,就是對(duì)于傳統(tǒng)的AIDL使用方式,這里就不再過(guò)多介紹了,這部分還是比較簡(jiǎn)單的,有興趣的伙伴們可以去前面的文章中查看,本文將著重介紹框架層面的邏輯。
那么IPC進(jìn)程間通信,需要兩個(gè)端:客戶端和服務(wù)端。服務(wù)端會(huì)提供一個(gè)注冊(cè)方法,例如客戶端定義的一些服務(wù),通過(guò)向服務(wù)端注冊(cè)來(lái)做一個(gè)備份,當(dāng)客戶端調(diào)用服務(wù)端某個(gè)方法的時(shí)候來(lái)返回值。
object IPC {
//==========================================
/**
* 服務(wù)端暴露的接口,用于注冊(cè)服務(wù)使用
*/
fun register(service: Class<*>) {
Registry.instance.register(service)
}
}
其實(shí)在注冊(cè)的時(shí)候,我們的目的肯定是能夠方便地拿到某個(gè)服務(wù),并且能夠調(diào)用這個(gè)服務(wù)提供的方法,拿到我想要的值;所以在定義服務(wù)的時(shí)候,需要注意以下兩點(diǎn):
(1)需要定義一個(gè)與當(dāng)前服務(wù)一一對(duì)應(yīng)的serviceId,通過(guò)serviceId來(lái)獲取服務(wù)的實(shí)例;
(2)每個(gè)服務(wù)當(dāng)中定義的方法同樣需要對(duì)應(yīng)起來(lái),以便拿到服務(wù)對(duì)象之后,通過(guò)反射調(diào)用其中的方法。
所以在注冊(cè)的時(shí)候,需要從這兩點(diǎn)入手。
1.1 定義服務(wù)唯一標(biāo)識(shí)serviceId
@Target(AnnotationTarget.TYPE)
@Retention(AnnotationRetention.RUNTIME)
annotation class ServiceId(
val name: String
)
一般來(lái)說(shuō),如果涉及到反射,最常用的就是通過(guò)注解給Class做標(biāo)記,因?yàn)橥ㄟ^(guò)反射能夠拿到類(lèi)上標(biāo)記的注解,就能夠拿到對(duì)應(yīng)的serviceId。
class Registry {
//=======================================
/**用于存儲(chǔ) serviceId 對(duì)應(yīng)的服務(wù) class對(duì)象*/
private val serviceMaps: ConcurrentHashMap<String, Class<*>> by lazy {
ConcurrentHashMap()
}
/**用于存儲(chǔ) 服務(wù)中全部的方法*/
private val methodsMap: ConcurrentHashMap<Class<*>, ConcurrentHashMap<String, Method>> by lazy {
ConcurrentHashMap()
}
//=======================================
/**
* 服務(wù)端注冊(cè)方法
* @param service 服務(wù)class對(duì)象
*/
fun register(service: Class<*>) {
// 獲取serviceId與服務(wù)一一對(duì)應(yīng)
val serviceIdAnnotation = service.getAnnotation(ServiceId::class.java)
?: throw IllegalArgumentException("只有標(biāo)記@ServiceId的服務(wù)才能夠被注冊(cè)")
//獲取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() }
}
}
通過(guò)上面的register方法,當(dāng)傳入定義的服務(wù)class對(duì)象的時(shí)候,首先獲取到服務(wù)上標(biāo)記的@ServiceId注解,注意這里如果要注冊(cè)必須標(biāo)記,否則直接拋異常;拿到serviceId之后,存入到serviceMaps中。
然后需要獲取服務(wù)中的全部方法,因?yàn)榭紤]到重載方法的存在,所以不能單單以方法名作為key,而是需要把參數(shù)也加上,因此這里做了一個(gè)邏輯就是將方法名與參數(shù)名組合一個(gè)key,存入到方法表中。
這樣注冊(cè)任務(wù)就完成了,其實(shí)還是比較簡(jiǎn)單的,關(guān)鍵在于完成2個(gè)表:服務(wù)表和方法表的初始化以及數(shù)據(jù)存儲(chǔ)功能。
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提供用戶信息查詢(xún)。
@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
}
}
用戶中心可以注冊(cè)這個(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ù)端注冊(cè)就完成了。
2 客戶端與服務(wù)端的通信協(xié)議
對(duì)于客戶端的連接,其實(shí)就是綁定服務(wù),那么這里就會(huì)使用到AIDL通信,但是跟傳統(tǒng)的相比,我們是將AIDL封裝到框架層內(nèi)部,對(duì)于用戶來(lái)說(shuō)是無(wú)感知的。
2.1 創(chuàng)建IPCService
這個(gè)服務(wù)就是用來(lái)完成進(jìn)程間通信的,客戶端需要與這個(gè)服務(wù)建立連接,通過(guò)服務(wù)端分發(fā)消息,或者接收客戶端發(fā)送來(lái)的消息。
abstract class IPCService : Service() {
override fun onBind(intent: Intent?): IBinder? {
return null
}
}
這里我定義了一個(gè)抽象的Service基類(lèi),為啥要這么做,前面我們提到過(guò)是因?yàn)檎麄€(gè)項(xiàng)目中不可能只有一個(gè)服務(wù),因?yàn)闃I(yè)務(wù)眾多,為了保證單一職責(zé),需要?jiǎng)澐植煌念?lèi)型,所以在框架中會(huì)衍生多個(gè)實(shí)現(xiàn)類(lèi),不同業(yè)務(wù)方可以注冊(cè)這些服務(wù),當(dāng)然也可以自定義服務(wù)繼承IPCService。
class IPCService01 : IPCService() {
}
在IPCService的onBind需要返回一個(gè)Binder對(duì)象,因此需要?jiǎng)?chuàng)建aidl文件。
2.2 定義通訊協(xié)議
像我們?cè)谡?qǐng)求接口的時(shí)候,通常也是向服務(wù)端發(fā)起一個(gè)請(qǐng)求(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<Parameters>?
) : Serializable, Parcelable {
//=====================================
/**請(qǐng)求類(lèi)型*/
//獲取實(shí)例的對(duì)象
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<Request> {
override fun createFromParcel(parcel: Parcel): Request {
return Request(parcel)
}
override fun newArray(size: Int): Array<Request?> {
return arrayOfNulls(size)
}
}
}
對(duì)于客戶端來(lái)說(shuō),致力于發(fā)起請(qǐng)求,請(qǐng)求實(shí)體類(lèi)Request參數(shù)介紹如下:
type表示請(qǐng)求的類(lèi)型,包括兩種分別是:執(zhí)行靜態(tài)方法和執(zhí)行普通方法(考慮到反射傳參);
serviceId表示請(qǐng)求的服務(wù)id,要請(qǐng)求哪個(gè)服務(wù),便可以獲取到這個(gè)服務(wù)的實(shí)例對(duì)象,調(diào)用服務(wù)中提供的方法;
methodName表示要請(qǐng)求的方法名,也是在serviceId服務(wù)中定義的方法;
params表示請(qǐng)求的方法參數(shù)集合,我們?cè)诜?wù)端注冊(cè)的時(shí)候,方法名 + 參數(shù)名 作為key,因此需要知道請(qǐng)求的方法參數(shù),以便獲取到Method對(duì)象。
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)
}
}
}
對(duì)于服務(wù)端來(lái)說(shuō),在接收到請(qǐng)求之后,需要針對(duì)具體的請(qǐng)求返回相應(yīng)的結(jié)果,Response實(shí)體類(lèi)參數(shù)介紹:
result表示請(qǐng)求成功或者失??;
value表示服務(wù)端返回的結(jié)果,是一個(gè)json字符串。
因此定義aidl接口文件如下,輸入一個(gè)請(qǐng)求之后,返回一個(gè)服務(wù)端的響應(yīng)。
interface IIPCServiceInterface {
Response send(in Request request);
}
這樣IPCService就可以將aidl生成的Stub類(lèi)作為Binder對(duì)象返回。
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ā)起請(qǐng)求,想要執(zhí)行某個(gè)方法的時(shí)候,首先服務(wù)端會(huì)先向Registery中查詢(xún)注冊(cè)的服務(wù),從而找到這個(gè)要執(zhí)行的方法,這個(gè)流程是在內(nèi)部完成。
override fun send(request: Request?): Response? {
//獲取服務(wù)對(duì)象id
val serviceId = request?.serviceId
val methodName = request?.methodName
val params = request?.params
// 反序列化拿到具體的參數(shù)類(lèi)型
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)
}
//存儲(chǔ)實(shí)例對(duì)象
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ā)起請(qǐng)求時(shí),會(huì)將請(qǐng)求的參數(shù)封裝到Request中,在服務(wù)端接收到請(qǐng)求后,就會(huì)解析這些參數(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í)體類(lèi),如下所示:
UserManager().setUserInfo(User("ming",25))
那么在調(diào)用這個(gè)方法的時(shí)候,首先會(huì)把這個(gè)實(shí)體類(lèi)轉(zhuǎn)成一個(gè)JSON字符串,例如:
{
"name":"ming",
"age":25
}
為啥要”多此一舉“呢?其實(shí)這種處理方式是最快速直接的,轉(zhuǎn)成json字符串之后,能夠最大限度地降低數(shù)據(jù)傳輸?shù)拇笮?,等到服?wù)端處理這個(gè)方法的時(shí)候,再把Request中的params反json轉(zhuǎn)成User對(duì)象即可。
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()
}
那么在查找注冊(cè)方法的時(shí)候就簡(jiǎn)單多了,直接抽絲剝繭一層一層取到最終的Method。在拿到Method之后,這里是有2種處理方式,一種是通過(guò)靜態(tài)單例的形式拿到實(shí)例對(duì)象,并保存在服務(wù)端;另一種就是執(zhí)行普通方法,因?yàn)樵诜瓷涞臅r(shí)候需要拿到類(lèi)的實(shí)例對(duì)象才能調(diào)用,所以才在GET_INSTANCE的時(shí)候存一遍。
3 客戶端 - connect
在第二節(jié)中,我們已經(jīng)完成了通訊協(xié)議的建設(shè),最終一步就是客戶端通過(guò)綁定服務(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)
}
}
對(duì)于綁定服務(wù)這塊,相信伙伴們也很熟悉了,這個(gè)需要說(shuō)一點(diǎn)的就是,在Android 5.0以后,啟動(dòng)服務(wù)不能只依賴(lài)action啟動(dòng),還需要指定應(yīng)用包名,否則就會(huì)報(bào)錯(cuò)。
在服務(wù)連接成功之后,即回調(diào)onServiceConnected方法的時(shí)候,需要拿到服務(wù)端的一個(gè)代理對(duì)象,即IIPCServiceInterface的實(shí)例對(duì)象,然后存儲(chǔ)在binders集合中,key為綁定的服務(wù)類(lèi)class對(duì)象,value就是對(duì)應(yīng)的服務(wù)端的代理對(duì)象。
fun send(
type: Int,
service: Class<out IPCService>,
serviceId: String,
methodName: String,
params: Array<Parameters>
): Response? {
//創(chuàng)建請(qǐng)求
val request = Request(type, serviceId, methodName, params)
//發(fā)起請(qǐng)求
return try {
binders[service]?.send(request)
} catch (e: Exception) {
null
}
}
當(dāng)拿到服務(wù)端的代理對(duì)象之后,就可以在客戶端調(diào)用send方法向服務(wù)端發(fā)送消息。
class Channel {
//====================================
/**每個(gè)服務(wù)對(duì)應(yīng)的Binder對(duì)象*/
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)建請(qǐng)求
val request = Request(type, serviceId, methodName, params)
//發(fā)起請(qǐng)求
return try {
binders[service]?.send(request)
} catch (e: Exception) {
null
}
}
companion object {
private val instance by lazy {
Channel()
}
/**
* 獲取單例對(duì)象
*/
fun getDefault(): Channel {
return instance
}
}
}
3.2 動(dòng)態(tài)代理獲取接口實(shí)例
回到1.2小節(jié)中,我們定義了一個(gè)IUserManager接口,通過(guò)前面我們定義的通信協(xié)議,只要我們獲取了IUserManager的實(shí)例對(duì)象,那么就能夠調(diào)用其中的任意普通方法,所以在客戶端需要設(shè)置一個(gè)獲取接口實(shí)例對(duì)象的方法。
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) {
//請(qǐng)求成功,返回接口實(shí)例對(duì)象
return Proxy.newProxyInstance(
classType.classLoader,
arrayOf(classType),
IPCInvocationHandler()
) as T
}
return null
}
當(dāng)我們通過(guò)客戶端發(fā)送一個(gè)獲取單例的請(qǐng)求后,如果成功了,那么就直接返回這個(gè)接口的單例對(duì)象,這里直接使用動(dòng)態(tài)代理的方式返回一個(gè)接口實(shí)例對(duì)象,那么后續(xù)執(zhí)行這個(gè)接口的方法時(shí),會(huì)直接走到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ā)送方法請(qǐng)求
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對(duì)象,那么就直接獲取到了結(jié)果。
3.3 框架使用
服務(wù)端:
UserManager2.getDefault().setUserInfo(User("ming", 25))
IPC.register(UserManager2::class.java)
同時(shí)在服務(wù)端需要注冊(cè)一個(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ù)端注冊(cè)的UserManager2是同一個(gè)ServiceId,而且接口、javabean需要存放在與服務(wù)端一樣的文件夾下。
val userManager = IPC.getInstanceWithName(
IPCService01::class.java,
IUserManager::class.java,
"getDefault",
null
)
val info = userManager?.getUserInfo()
通過(guò)動(dòng)態(tài)代理拿到接口的實(shí)例對(duì)象,只要調(diào)用接口中的方法,就會(huì)進(jìn)入到InvocationHandler中的invoke方法,在這個(gè)方法中,通過(guò)查找服務(wù)端注冊(cè)的方法名從而找到對(duì)應(yīng)的Method,通過(guò)反射調(diào)用拿到UserManager中的方法返回值。
這樣其實(shí)就通過(guò)5-6行代碼,就完成了進(jìn)程間通信,是不是比我們?cè)谑褂肁IDL的時(shí)候要方便地許多。
4 總結(jié)
如果我們面對(duì)下面這個(gè)類(lèi),如果這個(gè)類(lèi)是個(gè)私有類(lèi),外部沒(méi)法調(diào)用,想通過(guò)反射的方式調(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è)框架的原理就是上面這幾行代碼所能夠完成的事;通過(guò)服務(wù)端注冊(cè)的形式,將UserManager2中所有的方法Method收集起來(lái);當(dāng)另一個(gè)進(jìn)程,也就是客戶端想要調(diào)用其中某個(gè)方法的時(shí)候,通過(guò)方法名來(lái)獲取到對(duì)應(yīng)的Method,調(diào)用這個(gè)方法得到最終的返回值。
以上就是Android進(jìn)階手寫(xiě)IPC通信框架告別繁瑣AIDL的詳細(xì)內(nèi)容,更多關(guān)于Android IPC通信框架的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
Android進(jìn)階手寫(xiě)IPC通信框架告別繁瑣AIDL
這篇文章主要為大家介紹了Android進(jìn)階手寫(xiě)IPC通信框架告別繁瑣AIDL實(shí)現(xiàn)詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-01-01
android中實(shí)現(xiàn)背景圖片顏色漸變方法
這篇文章主要介紹了android中實(shí)現(xiàn)背景圖片顏色漸變方法,本文直接使用配置文件實(shí)現(xiàn)了這個(gè)效果,需要的朋友可以參考下2015-05-05
Android自定義View實(shí)現(xiàn)心形圖案
這篇文章主要為大家詳細(xì)介紹了Android自定義View實(shí)現(xiàn)心形圖案,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2021-09-09
kotlin實(shí)現(xiàn)強(qiáng)制下線功能
這篇文章主要為大家詳細(xì)介紹了kotlin實(shí)現(xiàn)強(qiáng)制下線功能,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2018-06-06
Android中Retrofit+OkHttp進(jìn)行HTTP網(wǎng)絡(luò)編程的使用指南
Retrofit和OkHttp都是Square在GitHub上開(kāi)源的第三方HTTP支持包,兩個(gè)包可以搭配使用,本文即是來(lái)講解Android中Retrofit+OkHttp進(jìn)行HTTP網(wǎng)絡(luò)編程的使用指南:2016-07-07
Android實(shí)現(xiàn)簡(jiǎn)單斷點(diǎn)續(xù)傳和下載到本地功能
這篇文章主要為大家詳細(xì)介紹了Android實(shí)現(xiàn)簡(jiǎn)單斷點(diǎn)續(xù)傳和下載到本地功能,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2017-11-11
Android studio git創(chuàng)建與刪除標(biāo)簽(Tag)的教程詳解
這篇文章主要介紹了Android studio git創(chuàng)建與刪除標(biāo)簽(Tag)的教程詳解,本文通過(guò)圖文并茂的形式給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2020-12-12

