項目地址:github.com/wutongke/Mo…javascript
寫這篇文章主要是有兩個緣由:java
咱們先來看一下架構圖,圖片來源於blog.spinytech.com/2016/12/28/…android
從A、B到N的多個Module都引用了Common庫,同時Main Module還引用了A、B、N這幾個Module,通過這樣的處理以後,全部的Module之間的相互調用就都消失了,耦合性下降,全部的通訊統一都交給Router來處理分發,而註冊工做則交由Main Module去進行初始化。這個架構思想其實和Binder的思想很相似,採用C/S模式,模塊之間隔離,數據經過共享區域進行傳遞。模塊與模塊之間只暴露對外開放的Action,因此也具有面向接口編程思想。git
架構圖中每一個紅色的Action都是能夠提供的服務,而Provider是一個服務的集合,每一個Module中能夠有一個或者多個Provider,當程序開始執行時,module會把Provider註冊到Router Module。Action服務請求流程以下:github
- 任意代碼建立一個RouterRequest,包含Provider和Action信息,向Router進行請求。
- Router接到請求,經過RouterRequest的Provider信息,在內部的HashMap中查找對應的Provider。
- Provider接到請求,在內部的HashMap中查找到對應的Action信息。
- Action調用invoke方法。
- 返回invoke方法生成的ActionResult。
- 將Result封裝成RouterResponse,返回給調用者。
仍是先來看下架構圖:編程
Router是JVM級別的單例模式,並不支持跨進程訪問。也就是說,你的後臺進程的全部Provider、Action,是註冊給後臺Router的。當你在前臺進程調用的時候,根本調用不到其餘進程的Action。json
解決方案是單獨提取一個Wide Router模塊,全部的Local Route與Wide Router經過進程間通訊的方式創建鏈接,Action請求若是在Local Router中找不到時,則經過WideRouter與其它進程創建鏈接,WideRouter充當局域網之間的路由。一次跨進程的Action請求以下圖所示:架構
以上的內容主要來自於Android架構思考(模塊化、多進程) ,目前也已經有了相應的demo,你們能夠去嘗試一下,體驗多進程App的樂趣。app
關於ModularizationArchitecture的使用,有相應的文檔:ModularizationArchitecture 使用教程。我的在使用後感受又一些不太方便的地方,主要有三點:dom
目前發起請求時傳遞的數據是RouterRequest
,接收的數據是RouterReponse
,兩種類型的數據中包含的只有字符串數據,在實際的進程間通訊時傳遞的數據也是字符串(代碼中轉爲了json數據)。這種方式不能傳遞自定義的數據,在數據使用時也要手動解析字符串,比較繁瑣。
其實Android爲進程間通訊提供了Parcelable,能夠經過這種方式很是方便地傳遞數據。
demo中採用新建線程的方式請求異步數據:
final RouterResponse response = LocalRouter.getInstance(MaApplication.getMaApplication())
.route(MainActivity.this, RouterRequest.obtain(MainActivity.this)
.domain("com.spinytech.maindemo:music")
.provider("music")
.action("play"));
response.isAsync();
new Thread(new Runnable() {
@Override
public void run() {
try {
final String temp = response.getData();
final long time = System.currentTimeMillis() - startTime;
handler.post(new Runnable() {
@Override
public void run() {
try {
Toast.makeText(MainActivity.this, "async:" + response.isAsync() + " cost:" + time + " response:" + response.get(), Toast.LENGTH_SHORT).show();
} catch (Exception e) {
e.printStackTrace();
}
}
});
} catch (Exception e) {
e.printStackTrace();
}
}
}).start();複製代碼
代碼看起來不夠優雅,採用Rxjava 的方式會使得線程切換更加優雅,Router模塊可使用Rxjava的方式返回結果。
代碼中Provider、Action須要手動註冊,若是增長一個Action,須要有多個地方進行變更,這裏能夠採用apt的方式來自動進行註冊。
針對以上的三個問題,對代碼進行來部分修改,以下:
請求數據RouterResquest
和返回數據MaActionResult
分別實現了Parcelable接口,而且分別提供了兩個能夠自定義的成員變量requestObject
和result
,用戶在創建請求數據RouterResquest
和返回數據MaActionResult
能夠把自定義的數據傳遞進去,須要注意的是傳遞的自定義類型也要實現Parcelable接口。
//自定義數據
public class Song implements Parcelable {
public String name;
public Song(String name) {
this.name = name;
}
protected Song(Parcel in) {
name = in.readString();
}
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeString(name);
}
@Override
public int describeContents() {
return 0;
}
public static final Creator<Song> CREATOR = new Creator<Song>() {
@Override
public Song createFromParcel(Parcel in) {
return new Song(in);
}
@Override
public Song[] newArray(int size) {
return new Song[size];
}
};
}複製代碼
//RouterResquest中設置了自定義類型Song
RouterRequestUtil.obtain(MainActivity.this)
.domain("com.spinytech.maindemo:music")
.provider("music")
.action("play")
.reqeustObject(new Song("see you"))
)
//MaActionResult中設置自定義類型Song
MaActionResult result = new MaActionResult.Builder()
.code(MaActionResult.CODE_SUCCESS)
.msg("play success")
.result(new Song("lili"))
.build();複製代碼
// 註解Provider,程序運行後將自動註冊MusicProvider到com.spinytech.maindemo:music到Router
@Provider(processName = "com.spinytech.maindemo:music")
public class MusicProvider extends MaProvider{
@Override
protected String getName() {
return "music";
}
}
// 註解Action,程序運行後將自動註冊PlayAction到Provider
@Action(processName = "com.spinytech.maindemo:music", providerName = "music")
public class PlayAction implements MaAction<Song>複製代碼
引入Rxjava以後,修改LocalRoute的route方法,使之返回Observable<MaActionResult>
,在調用時能夠很是方便地使用Rxjava切換線程:
LocalRouter.getInstance(MaApplication.getMaApplication())
.rxRoute(MainActivity.this, RouterRequestUtil.obtain(MainActivity.this)
.domain("com.spinytech.maindemo:pic")
.provider("pic")
.action("pic")
.data("is_big", "0"))
.observeOn(AndroidSchedulers.mainThread())
.subscribeOn(Schedulers.from(ThreadPool.getThreadPoolSingleton()))
.subscribe(new Consumer<MaActionResult>() {
@Override
public void accept(MaActionResult maActionResult) throws Exception {
Toast.makeText(MainActivity.this, maActionResult.getMsg(), Toast.LENGTH_SHORT).show();
}
}, new Consumer<Throwable>() {
@Override
public void accept(Throwable throwable) throws Exception {
Toast.makeText(MainActivity.this, "error", Toast.LENGTH_SHORT).show();
}
});複製代碼
6.1.1 在project的build.gradle中dependencies塊中支持apt:
classpath 'com.neenbedankt.gradle.plugins:android-apt:1.8'複製代碼
6.1.2 全部Module中配置apt插件:
apply plugin: 'com.neenbedankt.android-apt'複製代碼
dependencies塊中設置:
apt 'com.github.wutongke.modularization:compile:1.1.1'
compile 'com.github.wutongke.modularization:macore:1.1.1'複製代碼
6.2.1 實際Application
咱們知道一個app中只有一個Application,因此在主Module中定義Application,而後在其它模塊中根據須要實現邏輯Application便可,而後啓動時註冊邏輯Application,便可管理其生命週期:
public class MyApplication extends MaApplication {
//多進程中註冊各個進程的Router,能夠參考第3小節的原理圖
@Override
public void initializeAllProcessRouter() {
WideRouter.registerLocalRouter("com.spinytech.maindemo",MainRouterConnectService.class);
WideRouter.registerLocalRouter("com.spinytech.maindemo:music",MusicRouterConnectService.class);
WideRouter.registerLocalRouter("com.spinytech.maindemo:pic",PicRouterConnectService.class);
}
//註冊各個模塊中的邏輯Application,每一個模塊中能夠註冊多個邏輯
//Applicatoin,設置優先級,能夠調整模塊中多個邏輯Application的
//調用順序
@Override
protected void initializeLogic() {
registerApplicationLogic("com.spinytech.maindemo",999, MainApplicationLogic.class);
registerApplicationLogic("com.spinytech.maindemo",998, WebApplicationLogic.class);
registerApplicationLogic("com.spinytech.maindemo:music",999, MusicApplicationLogic.class);
registerApplicationLogic("com.spinytech.maindemo:pic",999, PicApplicationLogic.class);
}
//設置是否支持多進程
@Override
public boolean needMultipleProcess() {
return true;
}
}複製代碼
固然這個自定義的Application須要註冊到manifest文件中。
使用多進程提供服務的模塊須要繼承LocalRouterConnectService
,而且在manifest中註冊服務:
public class MusicRouterConnectService extends LocalRouterConnectService {
@Override
public boolean onUnbind(Intent intent) {
Log.e("MRCS","onUnbind");
return super.onUnbind(intent);
}
@Override
public void onDestroy() {
super.onDestroy();
Log.e("MRCS","onDestroy");
}
}複製代碼
<service android:name=".MusicRouterConnectService"
android:process=":music"/>複製代碼
6.2.2 邏輯Application
邏輯Application經過繼承BaseApplicationLogic,實現相應的方法便可被回調。
public class BaseApplicationLogic {
protected MaApplication mApplication;
public BaseApplicationLogic() {
}
public void setApplication(@NonNull MaApplication application) {
mApplication = application;
}
public void onCreate() {
}
public void onTerminate() {
}
public void onLowMemory() {
}
public void onTrimMemory(int level) {
}
public void onConfigurationChanged(Configuration newConfig) {
}
}
//邏輯Application只須要繼承BaseApplicationLogic,註冊後
//生命週期會被回調
public class MainApplicationLogic extends BaseApplicationLogic {
@Override
public void onCreate() {
super.onCreate();
}
}複製代碼
定義Provider
@Provider(processName = "com.spinytech.maindemo:music")
public class MusicProvider extends MaProvider{
@Override
protected String getName() {
return "music";
}
}複製代碼
定義Action
@Action(processName = "com.spinytech.maindemo:music", providerName = "music")
public class PlayAction implements MaAction<Song> {
@Override
public boolean isAsync(Context context, RouterRequest<Song> requestData) {
return false;
}
@Override
public MaActionResult invoke(final Context context, final RouterRequest<Song> requestData) {
Intent intent = new Intent(context, MusicService.class);
intent.putExtra("command", "play");
context.startService(intent);
MaActionResult result = new MaActionResult.Builder()
.code(MaActionResult.CODE_SUCCESS)
.msg("play success")
.result(new Song("lili"))
.build();
Handler handler = new Handler(context.getMainLooper());
handler.post(new Runnable() {
@Override
public void run() {
if (requestData != null && requestData.getRequestObject() != null) {
Toast.makeText(context, "歌曲名字:" + requestData.getRequestObject().name + "(並不知道)", Toast.LENGTH_SHORT).show();
}
}
});
Logger.d("com.spinytech", requestData.getRequestObject().name);
return result;
}
@Override
public String getName() {
return "play";
}
}複製代碼
能夠看到定義Provider和Action時分別使用了@Provider
和@Action
註解,這樣能夠在程序編譯時完成自動的註冊,不須要手動註冊到Router了。
其中 @Provider
須要設置進程名字,@Action
須要設置進程名字和註冊到的Provider名字:
@Provider(processName = "com.spinytech.maindemo:music")
@Action(processName = "com.spinytech.maindemo:music", providerName = "music")複製代碼
6.4.1 創建Action調用
首先需求創建一個請求RouterRequest
,說明要請求的內容:
RouterRequestUtil.obtain(MainActivity.this)
.domain("com.spinytech.maindemo:music")
.provider("music")
.action("play")
.reqeustObject(new Song("see you"))複製代碼
能夠經過RouterRequestUtil的obtain方法快速創建請求,上例中請求的Action位於"com.spinytech.maindemo:music"進程,Provider是"music",Action是"play",而且傳遞了相應的參數new Song("see you")。
而後使用Rxjava的方式請求Action:
LocalRouter.getInstance(MaApplication.getMaApplication())
.rxRoute(MainActivity.this, RouterRequestUtil.obtain(MainActivity.this)
.domain("com.spinytech.maindemo:music")
.provider("music")
.action("play")
.reqeustObject(new Song("see you"))
)
.subscribeOn(Schedulers.from(ThreadPool.getThreadPoolSingleton()))
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Consumer<MaActionResult>() {
@Override
public void accept(MaActionResult maActionResult) throws Exception {
Toast.makeText(MainActivity.this, maActionResult.getMsg(), Toast.LENGTH_SHORT).show();
}
}, new Consumer<Throwable>() {
@Override
public void accept(Throwable throwable) throws Exception {
Toast.makeText(MainActivity.this, "error", Toast.LENGTH_SHORT).show();
}
});複製代碼
6.4.2 處理請求
在music模塊中處理剛剛發出的請求,6.3中定義的Provider和Action其實就是處理6.4.1中的請求的,而且返回了MaActionResult
:
MaActionResult result = new MaActionResult.Builder()
.code(MaActionResult.CODE_SUCCESS)
.msg("play success")
.result(new Song("lili"))
.build();複製代碼
6小節介紹了ModularizationArchitecture的基本使用,ModularizationArchitecture提供了完整的demo,你們能夠clone代碼參考,有任何問題能夠在該項目下提issue,歡迎交流。
注意:在demo的gradle.properties中能夠配置Local屬性,從而根據須要設置使用本地的library,仍是遠端的library,更改Local後注意sync。
歡迎關注公衆號wutongke,天天推送移動開發前沿技術文章:
推薦閱讀: