應(yīng)用模板代碼地址:https://github.com/thfhongfeng/AndroidAppTemplate
項(xiàng)目架構(gòu)和路由介紹
Android項(xiàng)目架構(gòu)主要目的就是實(shí)現(xiàn)業(yè)務(wù)的模塊化,使之成為可插拔可配置的組件:
業(yè)務(wù)模塊化:這點(diǎn)Gradle的模塊化編譯實(shí)際上已經(jīng)實(shí)現(xiàn)憔鬼。
模塊間的通信:模塊化后寇蚊,模塊間的通信成為重點(diǎn)。這個(gè)有很多現(xiàn)成的庫幫我們實(shí)現(xiàn)了,比如Arouter。
Arouter的路由功能的實(shí)現(xiàn)主要分三個(gè)階段:
編譯階段:Arouter在編譯階段通過注解處理器將標(biāo)記的通信類收集起來生成處理這些信息的類文件(類似ARouter$$Group$$XXX類文件),并放在指定的包中(com.alibaba.android.arouter.routes )。
初始化階段:Arouter初始化時(shí)通過掃描指定的包(com.alibaba.android.arouter.routes )握牧,將里面的類進(jìn)行初始化容诬。然后調(diào)用指定的方法(loadInto)將通信類放到幾個(gè)靜態(tài)的Map中。
使用階段:在使用過程中通過標(biāo)記在Map中找到對(duì)應(yīng)的通信類了沿腰,然后就是實(shí)例化這些通信類览徒,根據(jù)通信類的類別做出相應(yīng)的處理和返回相應(yīng)的結(jié)果了。
而筆者應(yīng)用模板的架構(gòu)正是基于Arouter颂龙。
Arouter的路由功能雖然很強(qiáng)大习蓬,但仍然對(duì)業(yè)務(wù)代碼有入侵。為了盡量減少對(duì)業(yè)務(wù)代碼的入侵措嵌,應(yīng)用模板搭架了一個(gè)路由模塊router躲叼。router通信模塊是一個(gè)通信規(guī)范化的模塊,Arouter只是通信的一種實(shí)現(xiàn)方式企巢。
router模塊的目錄結(jié)構(gòu):
應(yīng)用模板模塊間通信的主要分兩塊(新增模塊時(shí)模塊間通信的搭建):
router模塊的command集:新增一個(gè)模塊時(shí)枫慷,如果此模塊需要給其它模塊提供模塊間服務(wù),就需要在router模塊中添加該模塊提供給其他模塊使用的服務(wù)命令集RouterXxxCommand。
業(yè)務(wù)模塊的提供的remote通信服務(wù):新增一個(gè)模塊時(shí)或听,如果此模塊需要給其它模塊提供模塊間服務(wù)探孝,就需要在本模塊中添加提供具體服務(wù)的類XxxRemoteService和服務(wù)協(xié)議類XxxArouterService。XxxRouterClient為本模塊調(diào)用其它模塊的統(tǒng)一服務(wù)出口類誉裆。
以上兩塊都是通過注解的方式來實(shí)現(xiàn)的顿颅,下面進(jìn)行解析說明。
本應(yīng)用架構(gòu)模塊間通信的原理
從使用出發(fā)一步一步解析
上面已經(jīng)展示了Login模塊的模塊間通信設(shè)施的搭架足丢。那么我們來看看Welcome模塊是如何使用Login模塊提供的服務(wù)的粱腻。
Welcome模塊在合適的位置會(huì)通過調(diào)用WelcomeRouterClient里的autoLogin嘗試自動(dòng)登錄。
public class WelcomeRouterClient {
public static void callCommand(Context context, String bundleKey,
String command, Bundle args, IRouterCallback callback) {
RouterManager.getInstance(bundleKey).callUiCommand(context,
command, args, callback);
}
public static void autoLogin(Context context, Bundle args, IRouterCallback callback) {
RouterManager.getInstance(ConfigKey.BUNDLE_LOGIN_KEY).callOpCommand(context,
RouterLoginCommand.autoLogin, args, callback);
}
public static void goMainHomeActivity(Context context, Bundle args, IRouterCallback callback) {
RouterManager.getInstance(ConfigKey.BUNDLE_MAIN_KEY).callUiCommand(context,
RouterMainCommand.goMainHomeActivity, args, callback);
}
}
autoLogin方法先根據(jù)Key獲取RouterManager實(shí)例霎桅∑芤桑看看如何獲取實(shí)例的
public class RouterManager {
public static IRouterManager getInstance(String bundleKey) {
switch (BuildConfig.APP_THIRD_ROUTER_PROVIDER) {
case "arouter":
return ARouterManager.getInstance(bundleKey);
default:
return ARouterManager.getInstance(bundleKey);
}
}
}
RouterManager的規(guī)劃是為了讓模塊間通信的第三方工具(如Arouter)可插拔,即也可以用其它的三方模塊間通信庫來實(shí)現(xiàn)滔驶。目前只實(shí)現(xiàn)了使用Arouter的方式遇革,所以都會(huì)去調(diào)用ArouterMananger的getInstance方法。
public class ARouterManager implements IRouterManager {
private final String TAG = LogUtils.makeLogTag(this.getClass());
private static volatile List<String> mClassNameList = new ArrayList<>();
private static volatile HashMap<String, ARouterManager> mInstanceMap = new HashMap<>();
private String mBundleKey = "";
private String mRemoteAction = "";
static {
try {
mClassNameList = AndroidClassUtils.getFileNameByPackageName(AppUtils.getApplicationContext(),
"com.pine.router.command");
} catch (PackageManager.NameNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
private ARouterManager(@NonNull String bundleKey) {
mBundleKey = bundleKey;
for (int i = 0; i < mClassNameList.size(); i++) {
try {
Class<?> clazz = Class.forName(mClassNameList.get(i));
ARouterRemoteAction remoteAction = clazz.getAnnotation(ARouterRemoteAction.class);
if (remoteAction != null) {
if (mBundleKey.equals(remoteAction.Key())) {
mRemoteAction = remoteAction.RemoteAction();
break;
}
}
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
}
public static ARouterManager getInstance(@NonNull String bundleKey) {
if (mInstanceMap.get(bundleKey) == null) {
synchronized (ARouterManager.class) {
if (mInstanceMap.get(bundleKey) == null) {
mInstanceMap.put(bundleKey, new ARouterManager(bundleKey));
}
}
}
return mInstanceMap.get(bundleKey);
}
……
……
public void callCommand(final String commandType, final Context context, String commandName,
Bundle args, final IRouterCallback callback) {
if (!checkBundleValidity(commandType, context, callback)) {
return;
}
ARouterBundleRemote routerService = ((ARouterBundleRemote) ARouter.getInstance().build(mRemoteAction)
.navigation(context, new NavigationCallback() {
@Override
public void onFound(Postcard postcard) {
LogUtils.d(TAG, "callOpCommand path:'" + postcard.getPath() + "'onFound");
}
@Override
public void onLost(Postcard postcard) {
LogUtils.d(TAG, "callOpCommand path:'" + postcard.getPath() + "'onLost");
if (callback != null && !callback.onFail(IRouterManager.FAIL_CODE_LOST, "onLost")) {
onCommandFail(commandType, context, IRouterManager.FAIL_CODE_LOST, "onLost");
}
}
@Override
public void onArrival(Postcard postcard) {
LogUtils.d(TAG, "callOpCommand path:'" + postcard.getPath() + "'onArrival");
}
@Override
public void onInterrupt(Postcard postcard) {
LogUtils.d(TAG, "callOpCommand path:'" + postcard.getPath() + "'onInterrupt");
if (callback != null && !callback.onFail(IRouterManager.FAIL_CODE_INTERRUPT, "onInterrupt")) {
onCommandFail(commandType, context, IRouterManager.FAIL_CODE_INTERRUPT, "onInterrupt");
}
}
}));
if (routerService != null) {
routerService.call(context, commandName, args, callback);
}
}
}
ARouterManager類承載了主要的實(shí)現(xiàn)方法揭糕÷芸欤可以看到在類初始化的時(shí)候會(huì)去查找“com.pine.router.command”包下的所有類,而在構(gòu)造方法中會(huì)遍歷這些類著角,找到有注解@ARouterRemoteAction的類揪漩,并根據(jù)Key(模塊標(biāo)記)值,找到對(duì)應(yīng)的RemoteAction并保存起來吏口。
看一下RouterLoginCommand這個(gè)類
@ARouterRemoteAction(Key = ConfigKey.BUNDLE_LOGIN_KEY, RemoteAction = "/login/service")
public interface RouterLoginCommand {
String goLoginActivity = "goLoginActivity";
String autoLogin = "autoLogin";
String logout = "logout";
}
因此奄容,這個(gè)getInstance方法實(shí)際上就是獲取Key值對(duì)應(yīng)的ARouterManager對(duì)象,這個(gè)對(duì)象在初始化的時(shí)候保存RemoteAction到mRemoteAction成員變量中产徊。實(shí)際上這個(gè)mRemoteAction就是Arouter的@Route注解的path值昂勒。
在Welcome模塊中獲取到RouterManager對(duì)象后調(diào)用的callXxxCommand,實(shí)際上最后都會(huì)走到RouterManager的callCommand方法舟铜。
callCommand方法就是我們熟悉的Arouter的使用了戈盈,而這個(gè)mRemoteAction對(duì)應(yīng)的@Route注解的類就是Login模塊的LoginARouterRemote。
@Route(path = "/login/service")
public class LoginARouterRemote extends ARouterBundleRemote<LoginRemoteService> {
}
這個(gè)類是一個(gè)空類谆刨,通過@Route注解讓Arouter找到它塘娶,在其父類ARouterBundleRemote中通過泛型找到實(shí)際的服務(wù)實(shí)現(xiàn)類LoginRemoteService。
最后調(diào)用ARouterBundleRemote的call方法痊夭,call方法中通過@RouterCommand注解找到對(duì)應(yīng)的服務(wù)方法刁岸,調(diào)用該方法實(shí)現(xiàn)模塊間的調(diào)用服務(wù)。
本應(yīng)用模板之所以這樣路由的原因她我,主要有兩個(gè):
1. 實(shí)現(xiàn)第三方路由庫的可插拔
2. 進(jìn)一步解耦業(yè)務(wù)代碼和路由代碼
搭架好業(yè)務(wù)模塊的路由功能后难捌,往后的服務(wù)接口的添加只需要兩步:
1. 在router模塊中的RouterXxxCommand中添加一個(gè)表示該服務(wù)接口名的常量yyy
2. 在業(yè)務(wù)模塊中的XxxRemoteService中編寫實(shí)現(xiàn)方法膝宁,并添加注解@RouterCommand,CommandName值為第一步中添加的常量
其它模塊中調(diào)用該服務(wù)接口:
RouterManager.getInstance(ConfigKey.BUNDLE_XXX_KEY).callOpCommand(context,
RouterXxxCommand.yyy, args, callback);
2019-08-26變更:
router模塊的command集移到了base模塊中根吁,通過調(diào)用RouterManager的init方法员淫,傳入command集所在的包名來初始化。從而將command集與router模塊分離击敌,使得router模塊成為方法庫介返,在開發(fā)過程中不再需要頻繁更改。