XIPC
一個(gè)Android通用的IPC(進(jìn)程通信)框架。該項(xiàng)目主要是模仿餓了么開源項(xiàng)目Hermes的設(shè)計(jì)進(jìn)行的自我理解改寫叫胖。
演示(請(qǐng)star支持)
apk下載
特征
支持自定義服務(wù)接口實(shí)現(xiàn)進(jìn)程通信,無需定義AIDL接口她奥,所有IPC通信就像調(diào)用本地函數(shù)一樣簡(jiǎn)單瓮增。
支持自定義接口服務(wù)(服務(wù)發(fā)現(xiàn))、獲取單例和獲取工具類方法哩俭。
支持進(jìn)程通信的接口回調(diào)绷跑。
支持接口回調(diào)的線程控制。
擁有垃圾回收機(jī)制凡资,防止接口回調(diào)內(nèi)存泄漏砸捏。
支持跨進(jìn)程和跨應(yīng)用通信。
實(shí)現(xiàn)原理
該框架主要使用以下技術(shù)實(shí)現(xiàn):
注解反射
動(dòng)態(tài)代理
AIDL
服務(wù)綁定
進(jìn)程間垃圾回收
詳細(xì)實(shí)現(xiàn)原理請(qǐng)點(diǎn)擊查看
如何使用
1.先在項(xiàng)目根目錄的 build.gradle 的 repositories 添加:
allprojects {
repositories {
...
maven { url "https://jitpack.io" }
}
}
2.然后在dependencies添加:
dependencies {
...
implementation 'com.github.xuexiangjys:XIPC:1.0.1'
}
3.最后在Application中注冊(cè)接口服務(wù):
XIPC.init(this);
XIPC.debug(BuildConfig.DEBUG);
//本地只需要注冊(cè)實(shí)現(xiàn)隙赁,無需注冊(cè)接口
XIPC.register(UserManager.class);
XIPC.register(LoadingTask.class);
XIPC.register(FileUtils.class);
XIPC.register(LoadingCallback.class);
XIPC.register(ComputeService.class);
//遠(yuǎn)程注冊(cè)接口
//注冊(cè)包名下的所有定義的服務(wù)接口
XIPC.register("com.xuexiang.remotedemo.service");
如何實(shí)現(xiàn)跨應(yīng)用通信
1.接口定義和實(shí)現(xiàn)
(1)首先我們需要定義一套統(tǒng)一的交互接口垦藏。使用@ClassName
和@MethodName
進(jìn)行修飾。
@ClassName("ComputeService")
public interface IComputeService {
/**
* 計(jì)算
* @param value1 值1
* @param symbol 算數(shù)符號(hào)
* @param value2 值2
* @return
*/
@MethodName("calculate")
float calculate(float value1, String symbol, float value2);
}
(2)根據(jù)定義的接口伞访,進(jìn)行具體實(shí)現(xiàn)掂骏。使用@ClassName
和@MethodName
進(jìn)行修飾。這里需要注意注解中的內(nèi)容要和之前定義的接口一一對(duì)應(yīng)厚掷。
@ClassName("ComputeService")
public class ComputeService implements IComputeService {
@Override
@MethodName("calculate")
public float calculate(float value1, String symbol, float value2) {
float result;
switch(symbol) {
case "+":
result = value1 + value2;
break;
case "-":
result = value1 - value2;
break;
case "*":
result = value1 * value2;
break;
case "/":
result = value1 / value2;
break;
default:
result = value1 + value2;
break;
}
return result;
}
}
2.注冊(cè)
(1)注冊(cè)接口和實(shí)現(xiàn)類芭挽。對(duì)于調(diào)用App而言,只需要注冊(cè)接口即可蝗肪;對(duì)于被調(diào)用App而言,只需要注冊(cè)實(shí)現(xiàn)類和回調(diào)接口即可蠕趁。統(tǒng)一在Application的onCreate中進(jìn)行注冊(cè)薛闪。
//被調(diào)用App,無需注冊(cè)接口
XIPC.register(UserManager.class);
XIPC.register(LoadingTask.class);
XIPC.register(FileUtils.class);
XIPC.register(LoadingCallback.class);
XIPC.register(ComputeService.class);
//調(diào)用App俺陋,只需要注冊(cè)接口和回調(diào)函數(shù)
XIPC.register("com.xuexiang.remotedemo.service");//該方法注冊(cè)包名下的所有定義的服務(wù)接口
(2)被調(diào)用App需在manifest
中注冊(cè)IPC通信服務(wù)豁延。可以使用默認(rèn)的IPCService0
服務(wù)腊状,也可以繼承IPCService
進(jìn)行自定義通信服務(wù)诱咏。
<service
android:name="com.xuexiang.xipc.core.channel.IPCService$IPCService0"
android:process=":remote"
android:exported="true" />
3.服務(wù)綁定
(1)在調(diào)用前,請(qǐng)先進(jìn)行綁定缴挖,綁定IPC通信服務(wù)袋狞。
XIPC.connectApp(getContext(), "com.xuexiang.xipcdemo"); //這里設(shè)置的是被調(diào)用App的包名
(2)當(dāng)然你也可以設(shè)置綁定的監(jiān)聽回調(diào),以判斷服務(wù)綁定是否成功。
XIPC.setIPCListener(new IPCListener() {
@Override
public void onIPCConnected(Class<? extends IPCService> service) {
ToastUtils.toast("IPC服務(wù)已綁定苟鸯!");
}
});
4.獲取實(shí)例訪問
XIPC提供三種訪問的方式:
getService: 新建獲取一般定義的服務(wù)接口同蜻。
getInstance: 獲取單例。
getUtilityClass: 獲取工具類早处。
IComputeService computeService = XIPC.getService(IComputeService.class);
ToastUtils.toast("3*4=" + computeService.calculate(3 , "*", 4));
5.服務(wù)解綁
當(dāng)不再需要服務(wù)訪問時(shí)湾蔓,我們需要及時(shí)地進(jìn)行服務(wù)解綁,回收資源砌梆。
XIPC.disconnect(getContext());
注意事項(xiàng)
在接口注冊(cè)方面
如果兩個(gè)進(jìn)程屬于兩個(gè)不同的app(分別叫App A和App B)默责。App A想訪問App B的一個(gè)類,并且App A的接口和App B的對(duì)應(yīng)實(shí)現(xiàn)類有
相同的包名和類名
咸包,那么就沒有必要在類和接口上加@ClassName
注解桃序。但是要注意使用ProGuard后類名和包名仍要保持一致。如果接口和類里面對(duì)應(yīng)的方法有相同的名字诉儒,那么也沒有必要在方法上加上
@MethodName
注解葡缰,同樣注意ProGuard的使用后接口內(nèi)的方法名字必須仍然和類內(nèi)的對(duì)應(yīng)方法名字相同。如果接口和實(shí)現(xiàn)類中有任意一個(gè)使用了
@ClassName
和@MethodName
修飾忱反,那么另一個(gè)也一定要使用相同的@ClassName
和@MethodName
修飾泛释,否則將報(bào)錯(cuò)。假設(shè)進(jìn)程B需要訪問進(jìn)程A, 如果進(jìn)程A使用了
@ClassName
注解標(biāo)識(shí)的類温算,那么進(jìn)程B也要對(duì)其對(duì)應(yīng)的接口上加上相同的@ClassName注解怜校,并且進(jìn)程A在進(jìn)程B訪問該接口之前,必須要注冊(cè)注竿。 否則進(jìn)程B使用XIPC.getService()
茄茁、XIPC.getInstance()
或XIPC.getUtilityClass()
訪問進(jìn)程A時(shí),XIPC在進(jìn)程A中找不到匹配的類巩割。所有注冊(cè)的接口類不可以是匿名類和局部類裙顽。
總之為了防止出現(xiàn)各種各樣不匹配或者找不到的問題,最好還是使用@ClassName
和@MethodName
注解宣谈,進(jìn)行一一對(duì)應(yīng)修飾并在Application的onCreate中進(jìn)行注冊(cè)愈犹。
在接口定義方面
如果你不想讓一個(gè)類或者函數(shù)被其他進(jìn)程訪問,可以在上面加上
@WithinProcess
注解闻丑。使用XIPC跨進(jìn)程調(diào)用函數(shù)的時(shí)候漩怎,傳入?yún)?shù)的類型可以是原參數(shù)類型的子類,千萬注意不可以是匿名類和局部類嗦嗡,但是回調(diào)函數(shù)例外勋锤。
在接口的參數(shù)方面,如果被調(diào)用的接口函數(shù)的參數(shù)類型和返回值類型是int侥祭、double等基本類型或者String叁执、Object這樣的Java通用類型無需多余操作茄厘。但是千萬注意,這里目前不支持參數(shù)的類型是數(shù)組徒恋。如果需要用到數(shù)組作為參數(shù)蚕断,可以使用自定義對(duì)象去包一下數(shù)組,再進(jìn)行使用入挣。
對(duì)于接口參數(shù)類型是自定義的類亿乳,并且兩個(gè)進(jìn)程分別屬于兩個(gè)不同app,那么你必須在兩個(gè)app中都定義這個(gè)類径筏,且必須保證代碼混淆后葛假,兩個(gè)類仍然有相同的包名和類名。不過你可以適用
@ClassName
和@MethodName
注解滋恬,這樣包名和類名在混淆后不同也不要緊了聊训。如果被調(diào)用的函數(shù)有回調(diào)參數(shù),那么函數(shù)定義中這個(gè)參數(shù)必須是一個(gè)接口恢氯,不能是抽象類带斑。
在接口回調(diào)方面
需要特別注意回調(diào)函數(shù)運(yùn)行的線程。如果進(jìn)程A調(diào)用進(jìn)程B的函數(shù)勋拟,并且傳入一個(gè)回調(diào)函數(shù)供進(jìn)程B在進(jìn)程A進(jìn)行回調(diào)操作勋磕,那么默認(rèn)這個(gè)回調(diào)函數(shù)將運(yùn)行在進(jìn)程A的主線程(UI線程)。如果你不想讓回調(diào)函數(shù)運(yùn)行在主線程敢靡,那么在接口聲明的函數(shù)的對(duì)應(yīng)的回調(diào)參數(shù)之前加上
@Background
注解挂滓。如果回調(diào)函數(shù)有返回值的話,請(qǐng)使用
@Background
注解讓它運(yùn)行在后臺(tái)線程啸胧。如果運(yùn)行在主線程赶站,那么返回值始終為null。在回調(diào)函數(shù)的引用方面纺念,框架持有回調(diào)函數(shù)的強(qiáng)引用贝椿,這個(gè)可能會(huì)導(dǎo)致內(nèi)存泄漏。為了解決該問題陷谱,你可以在接口聲明的對(duì)應(yīng)回調(diào)參數(shù)前加上@WeakRef注解团秽,這樣XIPC持有的就是回調(diào)函數(shù)的弱引用。如果進(jìn)程的回調(diào)函數(shù)被回收了叭首,而對(duì)方進(jìn)程還在調(diào)用這個(gè)函數(shù)(對(duì)方進(jìn)程并不會(huì)知道回調(diào)函數(shù)被回收),這個(gè)不會(huì)有任何影響踪栋,也不會(huì)造成崩潰焙格。如果回調(diào)函數(shù)有返回值,那么就返回null夷都。
@Background
和@WeakRef
注解眷唉,必須在接口中對(duì)應(yīng)的函數(shù)參數(shù)前進(jìn)行添加。如果加在其他地方,將不會(huì)有任何作用冬阳。
其他方面
調(diào)用函數(shù)的時(shí)候蛤虐,任何Context在另一個(gè)進(jìn)程中都會(huì)變成對(duì)方進(jìn)程的application context。
接口參數(shù)的數(shù)據(jù)傳遞默認(rèn)是基于Json的肝陪。
在使用過程中驳庭,出現(xiàn)任何錯(cuò)誤,都會(huì)有相關(guān)日志記錄氯窍,你只需要執(zhí)行
XIPC.debug
打開調(diào)試即可看見日志饲常。
混淆配置
# xipc
-keep @com.xuexiang.xipc.annotation.* class * {*;}
-keep class * {
@com.xuexiang.xipc.annotation.* <fields>;
}
-keepclassmembers class * {
@com.xuexiang.xipc.annotation.* <methods>;
}