背景
為什么要重復(fù)造輪子呢钞澳?
- 我認(rèn)為只有站在作者的角度才能更透徹的理解框架的設(shè)計(jì)思想
- 去踩大神們所踩過的坑旧蛾。
- 才能深入的理解框架的所提供的功能
- 學(xué)習(xí)優(yōu)秀的作品中從而提高自己
在開始之前我先提出關(guān)于ARouter的幾個(gè)問題
- 為什么要在module的build.gradle文件中增加下面配置? 它的作用是什么?它跟我們定義的url中的分組有什么關(guān)系谅摄?
javaCompileOptions {
annotationProcessorOptions {
arguments = [moduleName: project.getName()]
}
}
- 有這么一種業(yè)務(wù)場(chǎng)景民宿,新建一個(gè)業(yè)務(wù)組件user往枣,user組件中有頁(yè)面UserActivity,配置url
/user/main
;有一個(gè)服務(wù)接口用爪,其實(shí)現(xiàn)類在app中原押,配置url為/user/info
;代碼如下:
//module:user
@Route(path = "/user/main")
public class UserActivity extends AppCompatActivity {
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.user_activity);
}
}
public interface IUserService extends IProvider {
void test(String s);
}
//module:app
//user服務(wù)
@Route(path = "/user/info")
public class UserServiceImpl implements IUserService {
public void test(String test) {
Log.d("xxxx->",test);
}
}
好了開發(fā)完成,讓我們編譯一下項(xiàng)目看看偎血,編譯結(jié)果如下圖(ps:這里編譯的是我自己的項(xiàng)目诸衔,但效果和ARouter是一樣的):
Why?颇玷?笨农?
讓我們帶著這兩個(gè)問題開始RouterManager之旅。
第一步架構(gòu)設(shè)計(jì)思路(處理頁(yè)面跳轉(zhuǎn))
我們的目標(biāo)是根據(jù)一個(gè)url來(lái)打開指定的頁(yè)面亚隙,該如何做呢磁餐?很簡(jiǎn)單违崇,我們把url和對(duì)應(yīng)的頁(yè)面做一個(gè)對(duì)應(yīng)關(guān)系阿弃,比如放到map中以u(píng)rl為key,對(duì)應(yīng)的頁(yè)面activity為value即可羞延;這樣當(dāng)我們要打開這個(gè)activity時(shí)渣淳,根據(jù)傳給我們的url去map中找到對(duì)應(yīng)的activity,然后調(diào)用startActivity就OK了伴箩。
你可能會(huì)問那我們這個(gè)map該如何維護(hù)呢入愧?我們?cè)趺窗堰@個(gè)對(duì)應(yīng)關(guān)系存到map中呢?總不能手動(dòng)去put吧嗤谚,你別說貌似還真行棺蛛,我們?cè)赼pp啟動(dòng)的時(shí)候先把我的映射關(guān)系手動(dòng)初始化好,這樣在打開頁(yè)面是直接通過url來(lái)獲取就行了巩步。那么問題來(lái)了旁赊,大哥你累不累啊椅野?對(duì)于一個(gè)懶人來(lái)說首先會(huì)想到的是能不能自動(dòng)生成這個(gè)映射關(guān)系表呢终畅?答案是肯定的。
思路總結(jié)
我們可以利用編譯注解的特性竟闪,新增一個(gè)注解,給每個(gè)需要通過url打開的activity加上此注解。在注解處理器中獲取所有被注解的類梆暖,動(dòng)態(tài)生成映射關(guān)系表咐旧,然后在app啟動(dòng)時(shí)把所生成的映射關(guān)系load到內(nèi)存(其實(shí)就是讀到一個(gè)map中)
第二部擼代碼
0x01
首先我們需要?jiǎng)?chuàng)建三個(gè)module,如下圖:
為什么要三個(gè)項(xiàng)目呢理朋?原因如下:
我們需要用到的注解處理器AbstractProcessor是在javax包下赠涮,而android項(xiàng)目中是沒有這個(gè)包的子寓,因此我們需要建一個(gè)java library,也就是router-compiler笋除,它的作用是幫我們動(dòng)態(tài)生成代碼斜友,只存在于編譯期間
既然router-compiler只存在于編譯期間,那我們的注解是需要在項(xiàng)目中用到的垃它,這個(gè)類應(yīng)該放在那里呢鲜屏?這就有了第二個(gè)java library,router-annotation国拇,用來(lái)專門存放我們定義的注解和一些要被打進(jìn)app中代碼洛史。
由于上述兩個(gè)library都是java項(xiàng)目,而我們最終是要用到android工程中的酱吝,因此對(duì)外提供api時(shí)肯定會(huì)用到android工程中的類也殖,如Context。所以就有了第三個(gè)module router-api用于處理生成產(chǎn)物务热。如把生成映射關(guān)系表load到內(nèi)存忆嗜,并提供統(tǒng)一的調(diào)用入口。
0x02
我們先定義我們自己的注解:
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.CLASS)
public @interface Route {
String path();
String group() default "";
String name() default "";
int extras() default Integer.MIN_VALUE;
int priority() default -1;
}
定義自己的route處理器RouterProcessor
@AutoService(Processor.class) //自動(dòng)注冊(cè)注解處理器
@SupportedOptions({Consts.KEY_MODULE_NAME}) //參數(shù)
@SupportedSourceVersion(SourceVersion.RELEASE_7) //指定使用的Java版本
@SupportedAnnotationTypes({ANNOTATION_ROUTER_NAME}) //指定要處理的注解類型
public class RouterProcessor extends AbstractProcessor{
private Map<String,Set<RouteMeta>> groupMap = new HashMap<>(); //收集分組
private Map<String,String> rootMap = new TreeMap<>();
private Filer mFiler;
private Logger logger;
private Types types;
private TypeUtils typeUtils;
private Elements elements;
private String moduleName = "app"; //默認(rèn)app
private TypeMirror iProvider = null; //IProvider類型
//......
其中SupportedAnnotationTypes指定的就是我們上面定義的注解Route
接下來(lái)就是收集所有被注解的類崎岂,生成映射關(guān)系,代碼如下:
public boolean process(Set<? extends TypeElement> set, RoundEnvironment roundEnvironment) {
if(CollectionUtils.isNotEmpty(set)) {
//獲取到所有被注解的類
Set<? extends Element> elementsAnnotatedWith = roundEnvironment.getElementsAnnotatedWith(Route.class);
try {
logger.info(">>> Found routers,start... <<<");
parseRoutes(elementsAnnotatedWith);
} catch (IOException e) {
logger.error(e);
}
return true;
}
return false;
}
獲取完之后交給了parseRoutes方法:
private void parseRoutes(Set<? extends Element> routeElements) throws IOException {
if(CollectionUtils.isNotEmpty(routeElements)) {
logger.info(">>> Found routes, size is " + routeElements.size() + " <<<");
rootMap.clear();
//.......
TypeMirror type_activity = elements.getTypeElement(ACTIVITY).asType();
for (Element element : routeElements) {
TypeMirror tm = element.asType();
Route route = element.getAnnotation(Route.class);
RouteMeta routeMeta;
if(types.isSubtype(tm,type_activity)) { //activity
logger.info(">>> Found activity route: "+ tm.toString() + " <<<");
routeMeta = new RouteMeta(route,element,RouteType.ACTIVITY,null);
} else if(types.isSubtype(tm,iProvider)) { //IProvider
logger.info(">>> Found provider route: " + tm.toString() + " <<<");
routeMeta = new RouteMeta(route,element,RouteType.PROVIDER,null);
} else if(types.isSubtype(tm,type_fragment) || types.isSubtype(tm,type_v4_fragment)) { //Fragment
logger.info(">>> Found fragment route: " + tm.toString() + " <<< ");
routeMeta = new RouteMeta(route,element,RouteType.parse(FRAGMENT),null);
} else {
throw new RuntimeException("ARouter::Compiler >>> Found unsupported class type, type = [" + types.toString() + "].");
}
categories(routeMeta);
}
//.......
這個(gè)方法比較長(zhǎng)捆毫,我們先看看最主要的處理,遍歷routeElements冲甘,判斷當(dāng)前被注解的類的類型绩卤,分別是activity,IProvider,Fragment這三中江醇,也就是說注解Route可以用來(lái)注解activity 濒憋,IProvider,和Fragment(注意這里fragment包括原生包中的和v4包中的fragment)然后根據(jù)類型構(gòu)造出routeMate對(duì)象陶夜,構(gòu)造完之后傳給了categories方法:
private void categories(RouteMeta routeMete) {
if (routeVerify(routeMete)) {
logger.info(">>> Start categories, group = " + routeMete.getGroup() + ", path = " + routeMete.getPath() + " <<<");
//groupMap是一個(gè)全局變量凛驮,用來(lái)按分組存儲(chǔ)routeMeta
Set<RouteMeta> routeMetas = groupMap.get(routeMete.getGroup());
if (CollectionUtils.isEmpty(routeMetas)) {
Set<RouteMeta> routeMetaSet = new TreeSet<>(new Comparator<RouteMeta>() {
@Override
public int compare(RouteMeta r1, RouteMeta r2) {
try {
return r1.getPath().compareTo(r2.getPath());
} catch (NullPointerException npe) {
logger.error(npe.getMessage());
return 0;
}
}
});
routeMetaSet.add(routeMete);
groupMap.put(routeMete.getGroup(), routeMetaSet);
} else {
routeMetas.add(routeMete);
}
} else {
logger.warning(">>> Route meta verify error, group is " + routeMete.getGroup() + " <<<");
}
}
我們看到這個(gè)方法中首先根據(jù)當(dāng)前url分組去groupMap中查找,也就是看是否有該分組律适,如果有取出對(duì)應(yīng)的RouterMeta集合辐烂,把本次生成的routeMeta放進(jìn)去;沒有就新存一個(gè)集合捂贿。
到這里我們已經(jīng)把所有的注解類都獲取到并且已經(jīng)按分組分類纠修。接下來(lái)就是生成java類來(lái)存放這些信息:
這里暫且只看對(duì)activity映射關(guān)系處理的代碼:
// (1)
for (Map.Entry<String, Set<RouteMeta>> entry : groupMap.entrySet()) {
String groupName = entry.getKey();
// (2)
MethodSpec.Builder loadIntoMethodOfGroupBuilder = MethodSpec.methodBuilder(METHOD_LOAD_INTO)
.addAnnotation(Override.class)
.addModifiers(Modifier.PUBLIC)
.addParameter(groupParamSpec);
Set<RouteMeta> groupData = entry.getValue();
for (RouteMeta meta : groupData) {
ClassName className = ClassName.get((TypeElement) meta.getRawType());
//...... (3)
loadIntoMethodOfGroupBuilder.addStatement(
"atlas.put($S," +
"$T.build($T." + meta.getType() + ",$T.class,$S,$S," + (StringUtils.isEmpty(mapBody) ? null : ("new java.util.HashMap<String, Integer>(){{" + mapBodyBuilder.toString() + "}}")) + ", " + meta.getPriority() + "," + meta.getExtra() + "))",
meta.getPath(),
routeMetaCn,
routeTypeCn,
className,
meta.getPath().toLowerCase(),
meta.getGroup().toLowerCase());
}
//Generate groups (4)
String groupFileName = NAME_OF_GROUP + groupName;
JavaFile.builder(PACKAGE_OF_GENERATE_FILE,
TypeSpec.classBuilder(groupFileName)
.addJavadoc(WARNING_TIPS)
.addSuperinterface(ClassName.get(type_IRouteGroup))
.addModifiers(Modifier.PUBLIC)
.addMethod(loadIntoMethodOfGroupBuilder.build())
.build()
).build().writeTo(mFiler);
logger.info(">>> Generated group: " + groupName + "<<<");
rootMap.put(groupName, groupFileName);
}
// (5)
if(MapUtils.isNotEmpty(rootMap)) {
for (Map.Entry<String, String> entry : rootMap.entrySet()) {
loadIntoMethodOfRootBuilder.addStatement("routes.put($S, $T.class)", entry.getKey(), ClassName.get(PACKAGE_OF_GENERATE_FILE, entry.getValue()));
}
}
// ......
// Write root meta into disk. (6)
String rootFileName = NAME_OF_ROOT + moduleName;
JavaFile.builder(PACKAGE_OF_GENERATE_FILE,
TypeSpec.classBuilder(rootFileName)
.addJavadoc(WARNING_TIPS)
.addSuperinterface(ClassName.get(elements.getTypeElement(IROUTE_ROOT)))
.addModifiers(PUBLIC)
.addMethod(loadIntoMethodOfRootBuilder.build())
.build()
).build().writeTo(mFiler);
logger.info(">>> Generated root, name is " + rootFileName + " <<<");
}
現(xiàn)將上述這段代碼解釋如下:
- 遍歷我們之前存儲(chǔ)的groupMap,取出對(duì)應(yīng)的集合,如注釋(1)
- 生成一個(gè)方法體厂僧,并且把集合中的所有映射關(guān)系都put到參數(shù)map中扣草。如 (2)(3)
- 生成java類,類名為RouterManager + moduleName,這里的moduleName就是在build.gradle文件中配置的,如不配置辰妙,活獲取為null 如(4)
- 把每個(gè)分組和所生成的類做個(gè)映射關(guān)系鹰祸,作用就是為了實(shí)現(xiàn)按分組加載功能 如 (5)(6)
下面我們看下一生成的產(chǎn)物
/**
DO NOT EDIT THIS FILE!!! IT WAS GENERATED BY ROUTERMANAGER. */
public class RouterManager$$Root$$app implements IRouteRoot {
@Override
public void loadInto(Map<String, Class<? extends IRouteGroup>> routes) {
routes.put("service", RouterManager$$Group$$service.class);
}
}
存儲(chǔ)分組對(duì)應(yīng)關(guān)系
/**
* DO NOT EDIT THIS FILE!!! IT WAS GENERATED BY ROUTERMANAGER. */
public class RouterManager$$Group$$service implements IRouteGroup {
@Override
public void loadInto(Map<String, RouteMeta> atlas) {
atlas.put("/service/test/main",RouteMeta.build(RouteType.ACTIVITY,OtherActivity.class,"/service/test/main","service",null, -1,-2147483648));
}
}
就這樣映射關(guān)系自動(dòng)生成好了,那么該如何使用呢密浑?下面就讓我隆重介紹一下我們Api
0x03
由于我們的映射關(guān)系表是全局存在的蛙婴,所以肯定需要在Application中做初始化操作,其目的就是把映射關(guān)系load到內(nèi)存尔破,下面讓我們看看具體實(shí)現(xiàn)代碼
首先我們得需要一個(gè)容器來(lái)存儲(chǔ)我們的映射關(guān)系街图,因此就有了Warehouse類
class Warehouse {
// Cache route and metas
static Map<String, Class<? extends IRouteGroup>> groupsIndex = new HashMap<>();
static Map<String, RouteMeta> routes = new HashMap<>();
//......
static void clear() {
providers.clear();
providersIndex.clear();
}
}
我們?cè)诖祟愔袑?shí)例化兩個(gè)map用來(lái)存儲(chǔ)我們的分組信息和每個(gè)分組中的對(duì)應(yīng)關(guān)系信息
groupIndex:用來(lái)存放分組信息,這個(gè)會(huì)優(yōu)先load數(shù)據(jù)
routes:用來(lái)存儲(chǔ)對(duì)應(yīng)關(guān)系數(shù)據(jù)
接下來(lái)我們?cè)贏pp初始化時(shí)會(huì)調(diào)用如下代碼來(lái)初始化:
RouterManager.init(this);
那么我們進(jìn)去init方法中看看具體干了什么懒构?
public static synchronized void init(Application application){
if(!hasInit) {
hasInit = true;
mContext = application;
mHandler = new Handler(Looper.getMainLooper());
logger = new DefaultLogger();
LogisticsCenter.init(mContext,logger);
}
}
可以看到這里最關(guān)鍵的一行代碼是 LogisticsCenter.init(mContext,logger)
那就讓我們繼續(xù)去LogisticsCenter.init(mContext,logger);方法中看看:
public synchronized static void init(Context context, ILogger log) {
logger = log;
Set<String> routeMap;
try {
if(RouterManager.debuggable() || PackageUtils.isNewVersion(context)) { //開發(fā)模式或版本升級(jí)時(shí)掃描本地件
logger.info(TAG,"當(dāng)前環(huán)境為debug模式或者新版本餐济,需要重新生成映射關(guān)系表");
//these class was generated by router-compiler
routeMap = ClassUtils.getFileNameByPackageName(context, Consts.ROUTE_ROOT_PAKCAGE);
if(!routeMap.isEmpty()) {
PackageUtils.put(context,Consts.ROUTER_SP_KEY_MAP,routeMap);
}
PackageUtils.updateVersion(context);
} else{ //讀取緩存
logger.info(TAG,"讀取緩存中的router映射表");
routeMap = PackageUtils.get(context,Consts.ROUTER_SP_KEY_MAP);
}
logger.info(TAG,"router map 掃描完成");
//將分組數(shù)據(jù)加載到內(nèi)存
for (String className : routeMap) {
//Root
if(className.startsWith(Consts.ROUTE_ROOT_PAKCAGE + Consts.DOT + Consts.SDK_NAME + Consts.SEPARATOR + Consts.SUFFIX_ROOT)) {
((IRouteRoot)(Class.forName(className).getConstructor().newInstance())).loadInto(Warehouse.groupsIndex);
}
//......
}
logger.info(TAG,"將映射關(guān)系讀到緩存中");
if(Warehouse.groupsIndex.size() == 0) {
logger.error(TAG,"No mapping files,check your configuration please!");
}
if (RouterManager.debuggable()) {
logger.debug(TAG, String.format(Locale.getDefault(), "LogisticsCenter has already been loaded, GroupIndex[%d], ProviderIndex[%d]", Warehouse.groupsIndex.size(), Warehouse.providersIndex.size()));
}
} catch (Exception e) {
e.printStackTrace();
logger.error(TAG,"RouterManager init logistics center exception! [" + e.getMessage() + "]");
}
}
具體解釋如下:
1)、首先是根據(jù)包名去掃描所有生成的類文件胆剧,并放在routeMap中絮姆。當(dāng)然這里會(huì)根據(jù)版本判斷然后緩存到本地,目的是為了避免重復(fù)掃描
2)秩霍、遍歷掃描到的數(shù)組篙悯,將所有分組信息緩存到Warehouse.groupIndex中
可以看到初始化時(shí)只干了這兩件事,掃描class文件前域,讀取分組信息辕近;仔細(xì)想想你會(huì)發(fā)現(xiàn)這里并沒有去讀取我們的url和activity映射關(guān)系信息韵吨,這就是所謂的按需加載匿垄。
到這里我們所有的準(zhǔn)備工作都已完成了,那么該怎么使用呢归粉?
下面讓我們看看具體的用法
0x04
我們先來(lái)看一段代碼:
RouterManager.getInstance().build("/user/main").navigation(MainActivity.this);
上述代碼是我們打開UserActivty頁(yè)面所使用的方式椿疗,可以發(fā)現(xiàn)這里只傳了一個(gè)url。那就讓我們看看內(nèi)部是如何實(shí)現(xiàn)的糠悼?
首先我們?nèi)uild方法中看看具體的代碼:
public Postcard build(String path) {
if(TextUtils.isEmpty(path)) {
throw new HandlerException("Parameter is invalid!");
} else {
return build(path,extractGroup(path));
}
}
public Postcard build(String path,String group) {
if(TextUtils.isEmpty(path)) {
throw new HandlerException("Parameter is invalid!");
} else {
return new Postcard(path,group);
}
}
發(fā)現(xiàn)這里是一個(gè)重載方法届榄,最后返回的是一個(gè)Postcard對(duì)象,然后調(diào)用Postcard的navigation方法倔喂÷撂酰可以看到這里Postcard其實(shí)只是一個(gè)攜帶數(shù)據(jù)的實(shí)體。下面看看navigation方法:
public Object navigation(Context context) {
return RouterManager.getInstance().navigation(context,this,-1);
}
可以發(fā)現(xiàn)這里只是做了一個(gè)中轉(zhuǎn)席噩,最終調(diào)用的是RouterManager的navigation方法:
Object navigation(final Context context,final Postcard postcard,final int requestCode) {
try {
LogisticsCenter.completion(postcard);
} catch (HandlerException e) {
e.printStackTrace();
return null;
}
final Context currentContext = context == null ? mContext : context;
switch (postcard.getType()) {
case ACTIVITY:
final Intent intent = new Intent(currentContext,postcard.getDestination());
intent.putExtras(postcard.getExtras());
int flags = postcard.getFlags();
if(flags != -1) {
intent.setFlags(flags);
} else if(!(currentContext instanceof Activity)) { //如果當(dāng)前上下文不是activity班缰,則啟動(dòng)activity時(shí)需要new一個(gè)新的棧
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
}
runInMainThread(new Runnable() {
@Override
public void run() {
startActivity(requestCode,currentContext,intent,postcard);
}
});
break;
//......
}
return null;
}
由上述代碼可以看出首先調(diào)用的是LogisticsCenter.completion()方法把postcard對(duì)象傳進(jìn)去,那讓我們先去這個(gè)方法中看個(gè)究竟:
/**
* 填充數(shù)據(jù)
* @param postcard
*/
public synchronized static void completion(Postcard postcard) {
RouteMeta routeMeta = Warehouse.routes.get(postcard.getPath());
if(routeMeta != null) {
postcard.setDestination(routeMeta.getDestination());
postcard.setType(routeMeta.getType());
postcard.setPriority(routeMeta.getPriority());
postcard.setExtra(routeMeta.getExtra());
//......
} else {
Class<? extends IRouteGroup> groupMeta = Warehouse.groupsIndex.get(postcard.getGroup());
if(groupMeta == null) {
throw new NoRouteFoundException("There is no route match the path [" + postcard.getPath() + "], in group [" + postcard.getGroup() + "]");
} else {
try {
//按組加載數(shù)據(jù)悼枢,美其名曰-按需加載
IRouteGroup iRouteGroup = groupMeta.getConstructor().newInstance();
iRouteGroup.loadInto(Warehouse.routes);
Warehouse.groupsIndex.remove(postcard.getGroup());
} catch (Exception e) {
throw new HandlerException("Fatal exception when loading group meta. [" + e.getMessage() + "]");
}
}
completion(postcard); //分組加載完成后重新查找
}
}
這里首先去根據(jù)url去Warehouse.routes中查找對(duì)應(yīng)的RouteMeta信息埠忘,如何是首次調(diào)用的話這里一定是沒有的,所以會(huì)執(zhí)行else方法,else方法里先根據(jù)分組獲取對(duì)應(yīng)的分組class莹妒,然后反射其實(shí)例對(duì)象并調(diào)用loadInfo()方法名船,把該分組中的所有映射關(guān)系讀取到Warehouse.routes中,然后繼續(xù)調(diào)用當(dāng)前方法填充相關(guān)的信息旨怠。
信息填充完成之后繼續(xù)回到navigation方法中:
switch (postcard.getType()) {
case ACTIVITY:
final Intent intent = new Intent(currentContext,postcard.getDestination());
intent.putExtras(postcard.getExtras());
int flags = postcard.getFlags();
if(flags != -1) {
intent.setFlags(flags);
} else if(!(currentContext instanceof Activity)) { //如果當(dāng)前上下文不是activity渠驼,則啟動(dòng)activity時(shí)需要new一個(gè)新的棧
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
}
runInMainThread(new Runnable() {
@Override
public void run() {
startActivity(requestCode,currentContext,intent,postcard);
}
});
break;
//......
}
可以看到這里使用的是常規(guī)的啟動(dòng)方式startActivity去啟動(dòng)一個(gè)新activity。
Ok到此為止整個(gè)流程算是走完了鉴腻,至于傳遞參數(shù)渴邦,獲取fragment,以及服務(wù)IProvider什么的套路都一樣拘哨,這里不再重復(fù)贅述谋梭。
總結(jié)
ARouter的思路很好簡(jiǎn)單,就是通過編譯時(shí)注解生成url與頁(yè)面的映射關(guān)系表倦青,然后在程序啟動(dòng)時(shí)將該映射關(guān)系表load到內(nèi)存中瓮床,使用時(shí)直接去內(nèi)存中查找然后執(zhí)行常規(guī)的頁(yè)面啟動(dòng)方式。
下面我們來(lái)回答前面提出的兩個(gè)問題
第一:為什么要在每個(gè)build.gradle文件中配置一個(gè)moduleName呢产镐?
這是因?yàn)榫幾g時(shí)注解是以module為單位去生成代碼的隘庄,也就是說我們需要給每個(gè)module項(xiàng)目都配置該注解生成器的依賴,為了保證生成java文件的名字不會(huì)重復(fù)需要加上module為后綴癣亚。此配置和分組沒有任何關(guān)系丑掺。只是為了避免生成的分組類重復(fù)。
第二:為什么會(huì)報(bào)多個(gè)類重名的問題述雾?
我們知道Router的映射表有兩張表街州,第一張是用來(lái)存儲(chǔ)分組和分組對(duì)應(yīng)的class的,第二張是用來(lái)存儲(chǔ)每個(gè)分組中具體url映射關(guān)系的玻孟。而在第一個(gè)問題中我們根據(jù)moduleName來(lái)避免存放分組的class重名的問題唆缴。那么每個(gè)分組class本身有沒有重名的可能呢?答案是一定有的黍翎。比如:我們?cè)趗ser組件中配置的url:/user/main分組為user面徽,這個(gè)時(shí)候在編譯user組件時(shí)就會(huì)自動(dòng)生成一個(gè)類名為 RouterManager$$Group$$user
的類,用來(lái)存放所有的以u(píng)ser為分組的頁(yè)面映射關(guān)系匣掸。那么當(dāng)我們?cè)赼pp的中也配置分組名為user的分組后趟紊,編譯app時(shí)就會(huì)在app中生成類名為RouterManager$$Group$$user
的類。而我們app項(xiàng)目是依賴的user組件的碰酝,這就導(dǎo)致有兩個(gè)類名一樣的文件霎匈。編譯時(shí)自然就會(huì)報(bào)錯(cuò)。
對(duì)RouterManager的幾點(diǎn)思考
- RouterManager能否用于夸進(jìn)程調(diào)用:
我認(rèn)為是可以的砰粹,RouterManager的關(guān)系映射表是存在一個(gè)全局靜態(tài)變量中的唧躲,當(dāng)我們需要在其他進(jìn)程訪問時(shí)只需要提供一個(gè)接口來(lái)得到映射關(guān)系即可造挽。
- RouterManager能否在RePlugin中的使用:
答案也是可以的,由于RePlugin采用的是多個(gè)classloader機(jī)制弄痹,這就導(dǎo)致我們?cè)谥黜?xiàng)目的classloader獲取的對(duì)象和在插件classloader中獲取的是兩個(gè)獨(dú)立的對(duì)象饭入,如果想在插件中使用RouterManager去打開一個(gè)宿主的頁(yè)面,直接調(diào)用的話肯定是沒有對(duì)應(yīng)的映射關(guān)系的肛真,因?yàn)樵诓寮铽@取的RouterManager對(duì)象并不是宿主的單例對(duì)象谐丢,而是創(chuàng)建了一個(gè)新的對(duì)象。那怎么辦呢蚓让?答案很簡(jiǎn)單乾忱,我們?cè)诓寮惺褂梅瓷浍@取到宿主的RouterManager實(shí)例即可正常使用。
注:RouterManager框架的思路來(lái)源與ARouter历极,這里只實(shí)現(xiàn)了頁(yè)面跳轉(zhuǎn)窄瘟,fragment獲取和服務(wù)Provider的獲取功能。至于其他的降級(jí)策略趟卸,依賴注入功能就不在一一實(shí)現(xiàn)了
項(xiàng)目源碼請(qǐng)移駕到本人的github倉(cāng)庫(kù)查看:https://github.com/qiangzier/RouterManager