0、相關文章
1、簡單說一下使用ARouter跳轉到一個Activity的流程
我們先寫一個測試項目,如下:
有三個module:app、base、module-test1,其中app依賴base和test1,test1也依賴base。
base下面寫了一個BaseConstant類,用於存放公共字段
public class BaseConstant {
public static final String AROUTER_PATH_MODULE1_TEST1 = "/module1/Module1Test1Activity";
public static final String AROUTER_PATH_MODULE1_TEST2 = "/module1/Module1Test2Activity";
public static final String AROUTER_PATH_MODULE1_WEBVIEW = "/module1/TestWebViewActivity";
public static final String AROUTER_PATH_MODULE1_TEST_INTERCEPTOR = "/module1/TestInterceptorActivity";
}
test1的Module1Test1Activity.java
@Route(path = BaseConstant.AROUTER_PATH_MODULE1_TEST1)
public class Module1Test1Activity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_module1_test1);
}
}
app的MainActivity.java(只有一個跳轉,其他略過)
//不攜帶參數跳轉
findViewById(R.id.btn1).setOnClickListener(v -> {
// 1. 應用內簡單的跳轉
ARouter.getInstance()
.build(BaseConstant.AROUTER_PATH_MODULE1_TEST1)
.navigation();
});
1.1、運行項目
項目成功運行後,發現項目自動生成了一些代碼:
自動生成的代碼:
public class ARouter$$Group$$module1 implements IRouteGroup {
@Override
public void loadInto(Map<String, RouteMeta> atlas) {
atlas.put("/module1/Module1Test1Activity", RouteMeta.build(RouteType.ACTIVITY, Module1Test1Activity.class,
"/module1/module1test1activity", "module1", null, -1, -2147483648));
}
}
public class ARouter$$Providers$$moduletest1 implements IProviderGroup {
@Override
public void loadInto(Map<String, RouteMeta> providers) {
}
}
public class ARouter$$Root$$moduletest1 implements IRouteRoot {
@Override
public void loadInto(Map<String, Class<? extends IRouteGroup>> routes) {
routes.put("module1", ARouter$$Group$$module1.class);
}
}
主要看第一個類,這裏把路徑“/module1/Module1Test1Activity” 放到了一個map中。
1.2、初始化
public class App extends Application {
@Override
public void onCreate() {
super.onCreate();
// 儘可能早,推薦在Application中初始化
ARouter.init(this);
}
}
我們知道,ARouter框架使用的第一個步驟,是要先初始化,也就是調用:ARouter.init( Application.this );這個API,那麼,它的初始化究竟是做了那些東西?我們先點進源碼看看:
public static void init(Application application) {
if (!hasInit) {
logger = _ARouter.logger;
_ARouter.logger.info(Consts.TAG, "ARouter init start.");
hasInit = _ARouter.init(application);
if (hasInit) {
_ARouter.afterInit();
}
_ARouter.logger.info(Consts.TAG, "ARouter init over.");
}
}
那麼,實際上它調用的是綠色矩形內的代碼,綠色矩形內有兩個初始化,一個是 _ARouter.init(application),還有一個是 _ARouter.afterInit( )。
1.2.1、_ARouter . init()
我們首先點進 _ARouter . init()看看,
A:紅色箭頭的是類註釋,翻譯過來就是:ARouter核心( 外觀模式 )
B:綠色箭頭代表的是一個線程池,對線程池概念不是很清楚的朋友可以點進 必須要理清的Java線程池 先了解一下
C:藍色箭頭代表的是 這個_ARouter init()實際是調用的LogisticsCenter裏面的init方法。
首先,什麼是外觀模式?
簡單點理解就是,通過創建一個統一的類,用來包裝子系統中一個或多個複雜的類,客戶端可以通過調用外觀類的方法來調用內部子系統中所有方法。大概意思就是這樣,想深入理解的話可以自行查閱資料。
其次,這個線程池做了什麼功能?
點進去DefaultPoolExecutor這個類看看:
由源碼得知就是創建了一個數組阻塞隊列的線程池
最後,我們點進LogisticsCenter,看看裏面的init方法執行了什麼操作,該方法裏面主要有兩個核心代碼段:
第一個核心代碼段:
if (ARouter.debuggable() || PackageUtils.isNewVersion(context)) {
logger.info(TAG, "Run with debug mode or new install, rebuild router map.");
// These class was generated by arouter-compiler.
routerMap = ClassUtils.getFileNameByPackageName(mContext, ROUTE_ROOT_PAKCAGE);
if (!routerMap.isEmpty()) {
context.getSharedPreferences(AROUTER_SP_CACHE_KEY, Context.MODE_PRIVATE).edit()
.putStringSet(AROUTER_SP_KEY_MAP, routerMap).apply();
}
// Save new version name when router map update finishes.
PackageUtils.updateVersion(context);
} else {
logger.info(TAG, "Load router map from cache.");
routerMap = new HashSet<>(context.getSharedPreferences(AROUTER_SP_CACHE_KEY, Context.MODE_PRIVATE)
.getStringSet(AROUTER_SP_KEY_MAP, new HashSet<String>()));
}
從以上代碼可知,如果是debug模式或者第一次啓動,則走if模塊,否則走else模塊。
在if模塊中,獲取arouter-compiler生成的文件,然後將該文件,存儲在sp中,下次啓動應用的時候,直接從sp緩存中讀取。
第二個核心代碼段:
for (String className : routerMap) {
if (className.startsWith(ROUTE_ROOT_PAKCAGE + DOT + SDK_NAME + SEPARATOR + SUFFIX_ROOT)) {
// This one of root elements, load root.
((IRouteRoot) (Class.forName(className).getConstructor().newInstance())).loadInto(Warehouse.groupsIndex);
} else if (className.startsWith(ROUTE_ROOT_PAKCAGE + DOT + SDK_NAME + SEPARATOR + SUFFIX_INTERCEPTORS)) {
// Load interceptorMeta
((IInterceptorGroup) (Class.forName(className).getConstructor().newInstance()))
.loadInto(Warehouse.interceptorsIndex);
} else if (className.startsWith(ROUTE_ROOT_PAKCAGE + DOT + SDK_NAME + SEPARATOR + SUFFIX_PROVIDERS)) {
// Load providerIndex
((IProviderGroup) (Class.forName(className).getConstructor().newInstance()))
.loadInto(Warehouse.providersIndex);
}
}
首先遍歷arouter-compiler生成的文件,將他們按照類型分別存儲到Warehouse的對應字段中。也就是,如果類名前綴符合文件拼接規則,比如爲com.alibaba.android.arouter.routes.ARouter$$Root的文件,就將其添加到具體的Warehouse裏面的集合中。也就是對應的這裏
Warehouse又是什麼?點進源碼看看
其中,Warehouse的類註釋寫的非常好,翻譯過來就是:路由元數據和其他數據的存儲。這個類本質就是路由文件映射表。裏面提供了各種HashMap集合(Map不允許重複的key),去存儲SP存儲的值。
綜上,針對 _ARouter init( ) 這個初始化的源碼我們可以得知以下:
- 初始化這一操作,表面上是_ARouter ,實則是LogisticsCenter 在幫我們管理邏輯
- LogisticsCenter 內部通過先存儲SP,然後遍歷、匹配(滿足條件則添加到具體的集合中,按照文件的前綴不同,將他們添加到路由映射表Warehouse的groupsIndex、interceptorsIndex、providersIndex 中)
- 具體的路由清單是Warehouse ,不僅保存實例,還給我們提供了緩存。也就是說 同一個目標(RouteMeta、IProvider、IInterceptor)僅會在第一次使用的時候創建一次,然後緩存起來。後面都是直接使用的緩存。
1.2.2、_ARouter.afterInit()
static void afterInit() {
// Trigger interceptor init, use byName.
interceptorService = (InterceptorService) ARouter.getInstance()
.build("/arouter/service/interceptor").navigation();
}
首先,它實例化了一個InterceptorService。這裏的InterceptorService下面會說。 這裏的build方法,最終返回的是一個Postcard對象:
從源碼可以得知,實際上它返回的卻是,_ARouter.getInstance().build(path)這個方法,這個方法是一個方法重載(一般用的最多的就是這一個,也就是默認分組,不進行自定義分組),跟進去看看(源碼很長,分爲以下兩個截圖):
1.2.2.1、build
讓我們綠色箭頭,其中,navigation(clazz)這種方式是屬於根據類型查找,而build(path)是根據名稱進行查找。如果應用中沒有實現PathReplaceService這個接口,則pService=null。PathReplaceService可以對所有的路徑進行預處理,然後返回一個新的值(返回一個新的String和Uri)。綠色箭頭有一個extractGroup()方法,點進去看看:
extractGroup(path)這個方法,核心邏輯是紅色矩形內的代碼,這個方法主要是獲取分組名稱。切割path字符串,默認爲path中第一部分爲組名。這就證明了如果我們不自定義分組,默認就是第一個分號的內容。
發現這裏有一個PathReplaceService(也就是紅色矩形),這個PathReplaceService又是什麼,點進去看看
這個類註釋翻譯過來就是:預處理路徑。這個接口是IProvider的子類。
分析完了build,我們在看看navigation(Postcard.java)
1.2.2.2、navigation(Postcard.java)
繼續點進源碼看看,發現進入了_ARouter這個類裏面的navigation方法:
紅色矩形代表的意思是,如果(兩個)路徑沒寫對,ARouter會Toast提示客戶端,路徑不對。
紅色矩形代表的是忽略攔截器。interceptorService實例化對象的時機,是在_ARouter這類中的afterInit( )進行實例化的。這幅圖中的藍色矩形和箭頭的方法真實邏輯是調用了下圖的方法:
通過這段代碼我們可以得知:
- 1、如果navigation()不傳入Activity作爲context,則使用Application作爲context
- 2、內部使用了Intent來進行傳遞
- 3、如果在跳轉時,設置了flags,且沒有設置Activity作爲context,則下面的startActivity()方法會發生錯誤,因爲缺少Activity的Task棧;
- 4、Fragment的判斷根據版本不同進行了相應的判斷
繼續看這個圖
如果(兩個)路徑匹配的話,會執行到截圖中的藍色矩形,點進藍色矩形裏面去看看(也就是 LogisticsCenter.completion( Postcard )):
1.3、跳轉