轉(zhuǎn)載自:https://mp.weixin.qq.com/s/67YL9daArYy3dYS_aWyO1w
提升服務(wù)提供方的進程優(yōu)先級
bindService()實質(zhì)上是做了以下事情:
- 獲取服務(wù)提供方的binder
-
client端通過bind操作娇豫,讓Service所在進程的優(yōu)先級提高
bindService過程圖.png
現(xiàn)在我們來思考一個問題:雖然bind操作對用戶不可見,但是怎么知道bind哪個Service呢?
在編譯時,會為每個進程都插樁一個StubService, 并且在StubServiceMatcher這個類中诵姜,插入進程名與StubService的對應(yīng)關(guān)系(編譯時通過javassist插入代碼)盟萨,這樣根據(jù)進程名就可以獲取對應(yīng)的StubService.而IDispatcher的getRemoteService()方法中獲取的BinderBean就包含有進程名信息尿背。
生命周期管理
借鑒Glide的方式逛万,即利用Fragment/Activity的FragmentManager創(chuàng)建一個監(jiān)聽用的Fragment, 這樣當Fragment/Activity回調(diào)onDestroy()時,這個監(jiān)聽用的Fragment也會收到回調(diào)戚长,在這個回調(diào)中進行unbind操作即可盗冷。
回調(diào)監(jiān)聽原理圖.png
當時其實有考慮過是否借助Google推出的Arch componentss來處理生命周期問題,但是考慮到還有的團隊沒有接入這一套同廉,加上arch components的方案其實也變過多次仪糖,所以就暫時采用了這種方案,后面會視情況決定是否借助arch components的方案來進行生命周期管理 迫肖。
IPCCallback
對于耗時操作锅劝,我們直接在client端的work線程調(diào)用是否可以?雖然可以蟆湖,但是server端可能仍然需要把耗時操作放在自己的work線程中執(zhí)行故爵,執(zhí)行完畢之后再回調(diào)結(jié)果,所以這種情況下client端的work線程就有點多余帐姻。所以為了使用方便稠集,就需要一個IPCCallback, 在server端處理耗時操作之后再回調(diào)。
對于需要回調(diào)的AIDL接口饥瓷,其定義如下:
interface IBuyApple {
int buyAppleInShop(int userId);
void buyAppleOnNet(int userId,IPCCallback callback);
}
}
而client端的調(diào)用如下:
IBinder buyAppleBinder = Andromeda.getRemoteService(IBuyApple.class);
if (null == buyAppleBinder) {
return;
}
IBuyApple buyApple = IBuyApple.Stub.asInterface(buyAppleBinder);
if (null != buyApple) {
try {
buyApple.buyAppleOnNet(10, new IPCCallback.Stub() {
@Override
public void onSuccess(Bundle result) throws RemoteException {
...
}
@Override
public void onFail(String reason) throws RemoteException {
...
}
});
} catch (RemoteException ex) {
ex.printStackTrace();
}
}
}
但是考慮到回調(diào)是在Binder線程中剥纷,而絕大部分情況下調(diào)用者希望回調(diào)在主線程,所以lib封裝了一個BaseCallback給接入方使用呢铆,如下:
IBinder buyAppleBinder = Andromeda.getRemoteService(IBuyApple.class);
if (null == buyAppleBinder) {
return;
}
IBuyApple buyApple = IBuyApple.Stub.asInterface(buyAppleBinder);
if (null != buyApple) {
try {
buyApple.buyAppleOnNet(10, new BaseCallback() {
@Override
public void onSucceed(Bundle result) {
...
}
@Override
public void onFailed(String reason) {
...
}
});
} catch (RemoteException ex) {
ex.printStackTrace();
}
}
}
事件總線
Andromeda中Event的定義如下:
public class Event implements Parcelable {
private String name;
private Bundle data;
...
}
即 事件=名稱+數(shù)據(jù)晦鞋,通信時將需要傳遞的數(shù)據(jù)存放在Bundle中。
其中名稱要求在整個項目中唯一棺克,否則可能出錯悠垛。 由于要跨進程傳輸,所以所有數(shù)據(jù)只能放在Bundle中進行包裝娜谊。
事件訂閱
事件訂閱很簡單确买,首先需要有一個實現(xiàn)了EventListener接口的對象。 然后就可以訂閱自己感興趣的事件了纱皆,如下:
Andromeda.subscribe(EventConstants.APPLE_EVENT,MainActivity.this);
其中MainActivity實現(xiàn)了EventListener接口湾趾,此處表示訂閱了名稱為EventConstnts.APPLE_EVENT的事件。
事件發(fā)布
Bundle bundle = new Bundle();
bundle.putString("Result", "gave u five apples!");
Andromeda.publish(new Event(EventConstants.APPLE_EVENT, bundle));
InterStellar
InterStellar支持IPC修飾符in, out, inout和oneway派草,借助InterStellar, 可以像定義本地接口一樣定義遠程接口搀缠,如下:
public interface IAppleService {
int getApple(int money);
float getAppleCalories(int appleNum);
String getAppleDetails(int appleNum, String manifacture, String tailerName, String userName, int userId);
@oneway
void oneWayTest(Apple apple);
String outTest1(@out Apple apple);
String outTest2(@out int[] appleNum);
String outTest3(@out int[] array1, @out String[] array2);
String outTest4(@out Apple[] apples);
String inoutTest1(@inout Apple apple);
String inoutTest2(@inout Apple[] apples);
}
而接口的實現(xiàn)也跟本地服務(wù)的實現(xiàn)完全一樣,如下:
public class AppleService implements IAppleService {
@Override
public int getApple(int money) {
return money / 2;
}
@Override
public float getAppleCalories(int appleNum) {
return appleNum * 5;
}
@Override
public String getAppleDetails(int appleNum, String manifacture, String tailerName, String userName, int userId) {
manifacture = "IKEA";
tailerName = "muji";
userId = 1024;
if ("Tom".equals(userName)) {
return manifacture + "-->" + tailerName;
} else {
return tailerName + "-->" + manifacture;
}
}
@Override
public synchronized void oneWayTest(Apple apple) {
if(apple==null){
Logger.d("Man can not eat null apple!");
}else{
Logger.d("Start to eat big apple that weighs "+apple.getWeight());
try{
wait(3000);
//Thread.sleep(3000);
}catch(InterruptedException ex){
ex.printStackTrace();
}
Logger.d("End of eating apple!");
}
}
@Override
public String outTest1(Apple apple) {
if (apple == null) {
apple = new Apple(3.2f, "Shanghai");
}
apple.setWeight(apple.getWeight() * 2);
apple.setFrom("Beijing");
return "Have a nice day!";
}
@Override
public String outTest2(int[] appleNum) {
if (null == appleNum) {
return "";
}
for (int i = 0; i < appleNum.length; ++i) {
appleNum[i] = i + 1;
}
return "Have a nice day 02!";
}
@Override
public String outTest3(int[] array1, String[] array2) {
for (int i = 0; i < array1.length; ++i) {
array1[i] = i + 2;
}
for (int i = 0; i < array2.length; ++i) {
array2[i] = "Hello world" + (i + 1);
}
return "outTest3";
}
@Override
public String outTest4(Apple[] apples) {
for (int i = 0; i < apples.length; ++i) {
apples[i] = new Apple(i + 2f, "Shanghai");
}
return "outTest4";
}
@Override
public String inoutTest1(Apple apple) {
Logger.d("AppleService-->inoutTest1,apple:" + apple.toString());
apple.setWeight(3.14159f);
apple.setFrom("Germany");
return "inoutTest1";
}
@Override
public String inoutTest2(Apple[] apples) {
Logger.d("AppleService-->inoutTest2,apples[0]:" + apples[0].toString());
for (int i = 0; i < apples.length; ++i) {
apples[i].setWeight(i * 1.5f);
apples[i].setFrom("Germany" + i);
}
return "inoutTest2";
}}
}
可見整個過程完全不涉及到AIDL.
那它是如何實現(xiàn)的呢近迁?
本質(zhì)上AIDL編譯之后生成的Proxy其實是提供了接口的靜態(tài)代理艺普,那么我們其實可以改成動態(tài)代理來實現(xiàn),將服務(wù)方法名和參數(shù)傳遞到服務(wù)提供方,然后調(diào)用相應(yīng)的方法歧譬,最后將結(jié)果回傳即可岸浑。
總結(jié)
Andromeda的意義在于同時融合了本地通信和遠程通信,只有做到這樣瑰步,我覺得才算完整地解決了組件通信的問題助琐。其實跨進程通信都是在binder的基礎(chǔ)上進行封裝,Andromeda的創(chuàng)新之處在于將binder與Service進行剝離面氓,從而使服務(wù)的使用更加靈活。
最后附上Andromeda的開源地址 https://github.com/iqiyi/Andromeda
謝謝~