餓了麼開源專案Hermes:新穎巧妙易用的Android程式間通訊IPC框架
版權所有。所有權利保留。
歡迎轉載,轉載時請註明出處:
http://blog.csdn.net/xiaofei_it/article/details/51464518
Android程式間通訊IPC是比較高階的話題,很多Android程式設計師碰到IPC就覺得頭疼,尤其是AIDL這類東西。
公司最近在研究DroidPlugin外掛開發,DroidPlugin把每個子app都變成一個程式。這樣的話子app和主app如果需要共享資料,就需要IPC。所以我開發了Hermes框架,讓IPC變得非常簡單優雅。
專案地址:
https://github.com/Xiaofei-it/Hermes
這個框架開發難度很大,涉及到AIDL、binder、反射、註解、程式間垃圾回收、動態代理等很多技術。我以後會對原始碼進行解析。
本來我寫的文件是英文的,後來為了便於讀者查閱,特意翻譯成了中文文件。希望大家持續關注,可以給個star。
中文文件連結:
https://github.com/Xiaofei-it/Hermes/blob/master/README-ZH-CN.md
Hermes是一套新穎巧妙易用的Android程式間通訊IPC框架。這個框架使得你不用瞭解IPC機制就可以進行程式間通訊,像呼叫本地函式一樣呼叫其他程式的函式。
你們知道把英文文件翻譯成中文有多麼蛋疼嗎???還不給我star一下 o(╥﹏╥)o
使得程式間通訊像呼叫本地函式一樣方便簡單。
輕而易舉在本地程式建立其他程式類的物件,輕而易舉在本程式獲取其他程式的單例,輕而易舉在本程式使用其他程式的工具類。
支援程式間函式回撥,呼叫其他程式函式的時候可以傳入回撥函式,讓其他程式回撥本程式的方法。
自帶記憶體優化,並且支援跨程式垃圾回收。
IPC的主要目的是呼叫其他程式的函式,Hermes讓你方便地呼叫其他程式函式,呼叫語句和本地程式函式呼叫一模一樣。
比如,單例模式經常在Android App中使用。假設有一個app有兩個程式,它們共享如下單例:
@ClassId(“Singleton”)
public class Singleton {
private static Singleton sInstance = null;
private volatile String mData;
private Singleton() {
mData = new String();
}
public static synchronized Singleton getInstance() {
if (sInstance == null) {
sInstance = new Singleton();
}
return sInstance;
}
@MethodId(“setData”)
public void setData(String data) {
mData = data;
}
@MethodId(“getData”)
public String getData() {
return mData;
}
}
如果不使用Hermes,單例是無法共享的。
假設單例在程式A中,程式B想訪問這個單例。那麼你寫如下介面:
@ClassId(“Singleton”)
public interface ISingleton {
@MethodId(“setData”)
void setData(String data);
@MethodId(“getData”)
String getData();
}
程式B使用單例的時候,程式碼如下:
//obtain the instance of Singleton
ISingleton singleton = Hermes.getInstance(ISingleton.class);
//Set a data
singleton.setData(“Hello, Hermes!”);
//Get the data
Log.v(TAG, singleton.getData());
是不是很神奇?
只要給Hermes.getInstance()傳入這樣的介面,Hermes.getInstance()便會返回和程式A中例項一模一樣的例項。之後你在程式B中呼叫這個例項的方法時,程式A的同一個例項的方法也被呼叫。
但是,怎麼寫這種介面呢?很簡單。比如,程式A有一個類Foo,你想在程式B中訪問使用這個類。那麼你寫如下介面IFoo,加入同樣的方法,再在類Foo和介面IFoo上加上同樣的@ClassId註解,相同的方法上加上同樣的@MethodId註解。之後你就可以在程式B使用Hermes.getInstance(IFoo.class)獲取程式A的Foo例項。
dependencies {
compile 'xiaofei.library:hermes:0.2'
}
xiaofei.library
hermes
0.2
pom
接下來的部分將告訴你如何在其他程式呼叫主程式的函式。Hermes支援任意程式之間的函式呼叫,想要知道如何呼叫非主程式的函式,請看這裡。
在AndroidManifest.xml中加入如下宣告,你可以加上其他屬性。
經常地,一個app有一個主程式。給這個主程式命名為程式A。
假設有一個程式B,想要呼叫程式A的函式。那麼程式B應該初始化Hermes。
你可以在程式B的Application.OnCreate()或者Activity.OnCreate()中對Hermes初始化。相應的API是Hermes.connect(Context)。
Hermes.connect(getApplicationContext());
你可以呼叫Hermes.isConnected()來檢視通訊的程式是否還活著。
在給其他程式提供函式的程式中,可以使用Hermes.setContext(Context)來設定context。
函式呼叫時,如果引數有Context,這個引數便會被轉換成之前設定的Context。具體見“注意事項”的第8點。
程式A中,被程式B呼叫的類需要事先註冊。有兩種註冊類的API:Hermes.register(Class)和Hermes.register(Object)。Hermes.register(object)等價於Hermes.register(object.getClass())。
但是如果類上面沒有加上註解,那麼註冊就不是必須的,Hermes會通過類名進行反射查詢相應的類。詳見“注意事項”的第3點。
程式B中,建立程式A中的例項有三種方法:Hermes.newInstance()、Hermes.getInstance()和Hermes.getUtilityClass()。
Hermes.newInstance(Class, Object...)
這個函式在程式A中建立指定類的例項,並將引用返回給程式B。函式的第二個引數將傳給指定類的對應的構造器。
@ClassId(“LoadingTask”)
public class LoadingTask {
public LoadingTask(String path, boolean showImmediately) {
//...
}
@MethodId(“start”)
public void start() {
//...
}
}
@ClassId(“LoadingTask”)
public class ILoadingTask {
@MethodId(“start”)
void start();
}
在程式B中,呼叫Hermes.newInstance(ILoadingTask.class, “files/image.png”, true)便得到了LoadingTask的例項。
Hermes.getInstance(Class, Object...)
這個函式在程式A中通過指定類的getInstance方法建立例項,並將引用返回給程式B。第二個引數將傳給對應的getInstance方法。
這個函式特別適合獲取單例,這樣程式A和程式B就使用同一個單例。
@ClassId(“BitmapWrapper”)
public class BitmapWrapper {
@GetInstance
public static BitmapWrapper getInstance(String path) {
//...
}
@GetInstance
public static BitmapWrapper getInstance(int label) {
//...
}
@MethodId(“show”)
public void show() {
//...
}
}
@ClassId(“BitmapWrapper”)
public class IBitmapWrapper {
@MethodId(“show”)
void show();
}
程式B中,呼叫Hermes.getInstance(IBitmapWrapper.class,
“files/image.png”)或Hermes.getInstance(IBitmapWrapper.class,
1001)將得到BitmapWrapper的例項。
Hermes.getUtilityClass(Class)
這個函式獲取程式A的工具類。
這種做法在外掛開發中很有用。外掛開發的時候,通常主app和外掛app存在不同的程式中。為了維護方便,應該使用統一的工具類。這時外掛app可以通過這個方法獲取主app的工具類。
@ClassId(“Maths”)
public class Maths {
@MethodId(“plus”)
public static int plus(int a, int b) {
//...
}
@MethodId(“minus”)
public static int minus(int a, int b) {
//...
}
}
@ClassId(“Maths”)
public class IMaths {
@MethodId(“plus”)
int plus(int a, int b);
@MethodId(“minus”)
int minus(int a, int b);
}
程式B中,使用下面程式碼使用程式A的工具類。
IMaths maths = Hermes.getUtilityClass(IMaths.class);
int sum = maths.plus(3, 5);
int diff = maths.minus(3, 5);
事實上,如果兩個程式屬於兩個不同的app(分別叫App
A和App B),App A想訪問App B的一個類,並且App A的介面和App
B的對應類有相同的包名和類名,那麼就沒有必要在類和介面上加@ClassId註解。但是要注意使用ProGuard後類名和包名仍要保持一致。
如果介面和類裡面對應的方法的名字相同,那麼也沒有必要在方法上加上@MethodId註解,同樣注意ProGuard的使用後介面內的方法名字必須仍然和類內的對應方法名字相同。
如果程式A的一個類上面有一個@ClassId註解,這個類在程式B中對應的介面上有一個相同的@ClassId註解,那麼程式A在程式B訪問這個類之前必須註冊這個類。否則程式B使用Hermes.newInstance()、Hermes.getInstance()或Hermes.getUtilityClass()時,Hermes在程式A中找不到匹配的類。類可以在構造器或者Application.OnCreate()中註冊。
但是,如果類和對應的介面上面沒有@ClassId註解,但有相同的包名和類名,那麼就不需要註冊類。Hermes通過包名和類名匹配類和介面。
對於介面和類裡面的函式,上面的說法仍然適用。
如果你不想讓一個類或者函式被其他程式訪問,可以在上面加上@WithinProcess註解。
使用Hermes跨程式呼叫函式的時候,傳入引數的型別可以是原引數型別的子類,但不可以是匿名類和區域性類。但是回撥函式例外,關於回撥函式詳見“注意事項”的第7點。
public class A {}
public class B extends A {}
程式A中有下面這個類:
@ClassId(“Foo”)
public class Foo {
public static A f(A a) {
}
}
程式B的對應介面如下:
@ClassId(“Foo”)
public interface IFoo {
A f(A a);
}
程式B中可以寫如下程式碼:
IFoo foo = Hermes.getUtilityClass(IFoo.class);
B b = new B();
A a = foo.f(b);
但你不能寫如下程式碼:
A a = foo.f(new A(){});
如果被呼叫的函式的引數型別和返回值型別是int、double等基本型別或者String這樣的Java通用型別,上面的說法可以很好地解決問題。但如果型別是自定義的類,比如“注意事項”的第5點中的例子,並且兩個程式分別屬於兩個不同app,那麼你必須在兩個app中都定義這個類,且必須保證程式碼混淆後,兩個類仍然有相同的包名和類名。不過你可以適用@ClassId和@MethodId註解,這樣包名和類名在混淆後不同也不要緊了。
如果被呼叫的函式有回撥引數,那麼函式定義中這個引數必須是一個介面,不能是抽象類。請特別注意回撥函式執行的執行緒。
如果程式A呼叫程式B的函式,並且傳入一個回撥函式供程式B在程式A進行回撥操作,那麼預設這個回撥函式將執行在程式A的主執行緒(UI執行緒)。如果你不想讓回撥函式執行在主執行緒,那麼在介面宣告的函式的對應的回撥引數之前加上@Background註解。
如果回撥函式有返回值,那麼你應該讓它執行在後臺執行緒。如果執行在主執行緒,那麼返回值始終為null。
預設情況下,Hermes框架持有回撥函式的強引用,這個可能會導致記憶體洩漏。你可以在介面宣告的對應回撥引數前加上@WeakRef註解,這樣Hermes持有的就是回撥函式的弱引用。如果程式的回撥函式被回收了,而對方程式還在呼叫這個函式(對方程式並不會知道回撥函式被回收),這個不會有任何影響,也不會造成崩潰。如果回撥函式有返回值,那麼就返回null。
如果你使用了@Background和@WeakRef註解,你必須在介面中對應的函式引數前進行新增。如果加在其他地方,並不會有任何作用。
@ClassId(“Foo”)
public class Foo {
public static void f(int i, Callback callback) {
}
}
@ClassId(“callback”)
public interface Callback {
void callback();
}
@ClassId(“Foo”)
public interface IFoo {
void f(int i, @WeakRef @Background Callback callback);
}
呼叫函式的時候,任何Context在另一個程式中都會變成對方程式的application context。
資料傳輸是基於Json的。
使用Hermes框架的時候,有任何的錯誤,都會使用android.util.Log.e()打出錯誤日誌。你可以通過日誌定位問題。
相關文章
- 餓了麼開源專案:便捷高效的Android資料持久化儲存框架Android持久化框架
- 程式間通訊 --IPC
- 程式間通訊(IPC) 的限制
- IPC-程式間通訊
- 微服務的程式間通訊(IPC)微服務
- 餓了麼開源專案:Java Comparator生成器Java
- Android IPC程式間通訊之AIDL和Messenger的使用AndroidC程式AIMessenger
- linux程式間通訊(IPC)小結Linux
- 【IPC程式間通訊之二】管道PipeC程式
- ui餓了麼框架UI框架
- 【IPC程式間通訊之一】郵槽MailSlotC程式AI
- Android程式間通訊–訊息機制及IPC機制實現薦Android
- 程式間通訊——XSI IPC之訊息佇列佇列
- android開源專案和框架Android框架
- 程式間的通訊實現(IPC)的11種方法 (轉)
- Android程式間的通訊Android
- android開源專案和框架(轉)Android框架
- Android 程式間通訊Android
- Vue仿餓了麼app專案總結VueAPP
- Vue2.x開發餓了麼專案(header部分)VueHeader
- 開源專案之Android Afinal框架Android框架
- [Android]程式通訊Andromeda框架Android框架
- 【Android】開源專案UniversalImageLoader及開源框架ImageLoaderAndroid框架
- 慕課網 餓了麼 vue2.0 專案Vue
- android-IPC/Binder/D-BUS(Binder/Messager/AIDL)程式間通訊(訊息機制)AndroidAI
- Android 程式之間通訊Android
- 餓了麼手機版-VUE2,專案實戰程式碼Vue
- Android元件化專題 - 路由框架進階模組間的業務通訊Android元件化路由框架
- Android程式間通訊(IPC)機制Binder簡要介紹和學習計劃Android
- 【IPC程式間通訊之四】資料複製訊息WM_COPYDATAC程式
- 【IPC程式間通訊之三】記憶體對映檔案Mapping FileC程式記憶體APP
- IPC(InterProcess Communication):程序間通訊
- Android開啟多程式及程式間通訊的幾種方式Android
- linux環境程式設計(3): 使用POSIX IPC完成程式間通訊Linux程式設計
- Android程式間通訊詳解Android
- 程式間的八種通訊方式----共享記憶體是最快的 IPC 方式記憶體
- Android 開源專案Android
- android開源專案Android