這是我參與8月更文挑戰的第2天,活動詳情查看:8月更文挑戰html
凡是A,B無依賴關係,且想要互相通訊的,其最基本的原理就是經過一個彼此都依賴的第三方C,不論是binder,socket,file,provider仍是EventBus,Arouter等,都是這個原理,若是有人說不,那麼要麼是槓精,要麼是想引人注意,要麼就是放棄了治療。java
咱們假設現有: app,login,以及common三個module,其中app跟login無關聯,而且都依賴於common,其中app module中有個MainActivity,login module想要調用MainActivity,根據上述,只能經過共同依賴的common 或 其餘公有依賴來實現。android
map.put("main",MainActivity.class)
複製代碼
Class<? extends Activity> mainActivityClass= map.get("main")
startActivity(new Intent(this,mainActivityClass))
複製代碼
核心就兩點: 注入 和 獲取。說白了就是: 你把本身想讓別人用的放進公共倉庫中(注入),並提供一個憑證(這裏的憑證就是字符串"main"),也就是key,我想要的時候就拿着憑證從公共倉庫中取出來(獲取),公共倉庫就是彼此都能訪問的,那確定是彼此都依賴的。git
接着,咱們來看Arouter的實現。github
經過Arouter的實現思路跟上述是同樣的,只不過Arouter更簡單,更省事,更"愚人",它把咱們的"注入"和"獲取"都實現了(開發把本身當白癡調API就好了),而且還加了不少優化,可是道理都是同樣的,咱們來看下。api
1 在gradle文件添加依賴:緩存
若是是java則添加:markdown
api 'com.alibaba:arouter-api:1.5.0'
annotationProcessor 'com.alibaba:arouter-compiler:1.2.2'
android {
defaultConfig {
javaCompileOptions {
annotationProcessorOptions {
arguments = [AROUTER_MODULE_NAME: project.getName()]
}
}
}
}
複製代碼
若是是kotlin,則添加:app
api 'com.alibaba:arouter-api:1.5.0'
kapt 'com.alibaba:arouter-compiler:1.2.2'
android {
defaultConfig {
kapt {
arguments {
arg("AROUTER_MODULE_NAME", project.getName())
}
}
}
}
複製代碼
java和kotlin的區別,就是kotlin使用kapt關鍵字,別的都同樣。socket
2 在Application的onCreate()裏面初始化
// 初始化(注入)
ARouter.init(this);
複製代碼
3 定義path,也就是憑證
@Route(path = "/app/activity_main") // 這個就是憑證,也就是key
public class MainActivity {}
複製代碼
4 根據path啓動對應的Activity,也就是獲取
ARouter.getInstance().build(path).navigation(); // 根據憑證path獲取並啓動Activity
複製代碼
其中ARouter.init(this)就是注入的過程,注入的key就是咱們經過@Route(path)定義的path;而後就拿着path去調用navigation()來獲取並啓動對應的Activity了。
上面只是簡單示例Arouter的使用,不過多介紹,本篇重點是講解原理。想看詳細使用能夠訪問: Arouter官網。
那麼,Arouter的注入是怎麼作的呢,獲取又是怎麼獲取的呢,且看下文。
apt技術: apt技術就是 先設定一套代碼模版,而後在類加載期間,動態根據指定的代碼模版生成一個.java文件,這個文件在運行時能夠直接訪問,能夠看這裏加深瞭解。
以下圖:
因此,當咱們在gralde中添加了Arouter的依賴後,那麼在編譯時就會 在對應module的 /build/generated/source/kapt/debug/ 下生成 "com.alibaba.android.arouter.routes" 目錄,Arouter生成的代碼都放在這裏,好比:
// 這一個IRouteRoot,看名字"ARouter$$Root$$app",其中"ARouter$$Root"是前綴,"app"是group名字,也就是path裏面以"/"分隔獲得的第一個字符串,而後經過"$$"鏈接,
// 那麼這玩意兒的完整類名就是"com.alibaba.android.arouter.routes.ARouter$$Root$$app"
public class ARouter$$Root$$app implements IRouteRoot {
// 參數是一個map,value類型是 IRouteGroup
@Override
public void loadInto(Map<String, Class<? extends IRouteGroup>> routes) {
// "app"就是@Route(path = "/app/activity_main") 中的"app",在Arouter中叫作group,是以path中的"/"分隔獲得的
// 這個的value是:ARouter$$Group$$app.class,也就是下面的類
routes.put("app", ARouter$$Group$$app.class);
}
}
// 這是一個IRouteGroup,同理,前綴是"ARouter$$Group"
public class ARouter$$Group$$app implements IRouteGroup {
@Override
public void loadInto(Map<String, RouteMeta> atlas) {
// "app/activity_main"就是咱們經過@Route指定的path,後面RouteMeta保存了要啓動的組件類型,以及對應的.class文件
// 這個 RouteMeta.build()的參數很重要,後面要用到
atlas.put("/app/activity_main", RouteMeta.build(RouteType.ACTIVITY, MainActivity.class, "/app/activity_main", "app", null, -1, -2147483648));
}
}
// RouteMeta.build()方法,參數後面有用
// type就是: RouteType.ACTIVITY,
// destination就是MainActivity.class,
// path就是"/app/activity_main",
// group就是"app"
// paramsType是null
// priority是-1
// extra是-2147483648
public static RouteMeta build(RouteType type, Class<?> destination, String path, String group, Map<String, Integer> paramsType, int priority, int extra) {
return new RouteMeta(type, null, destination, null, path, group, paramsType, priority, extra);
}
複製代碼
注意,上述代碼所有是在app module中的build()中生成的。也就是說,這些代碼對於login module來講,是徹底透明的,不可達的。
並且,咱們發現生成的.java文件,都有個共同的前綴"ARouter Root"。又由於它們是在Arouter生成的目錄下面,因此它們的完整類名都有個前綴:"com.alibaba.android.arouter.routes.ARouter$$"。好,如今假設編譯完了,咱們啓動app。
如今咱們已經編譯完了,直接點擊run啓動了app,如今來到了運行時,此時咱們已經在/build/generated/source/kapt/debug/ 下生成了 "com.alibaba.android.arouter.routes"目錄,而且裏面還有一堆Arouter生成的代碼。
接下來代碼順序執行,跑到了Application的onCreate()裏面,因而就執行了初始化:
// 初始化,此時會調用編譯時生成的那一堆代碼,來"注入"須要的相關信息
Arouter.init(this)
// 調到了這裏
protected static synchronized boolean init(Application application) {
// 保存了mContext,後面有用
mContext = application;
// 初始化
LogisticsCenter.init(mContext, executor);
hasInit = true;
mHandler = new Handler(Looper.getMainLooper());
return true;
}
複製代碼
咱們跟着代碼,發現最終調了:
// executor是內置的一個線程池
LogisticsCenter.init(mContext, executor);
複製代碼
接下來咱們就來看這個代碼,這裏刪除了日誌以及部分次要邏輯:
public synchronized static void init(Context context, ThreadPoolExecutor tpe) throws HandlerException {
mContext = context;
executor = tpe;
try {
Set<String> routerMap;
// 若是是debugable()或者更新了app的版本
if (ARouter.debuggable() || PackageUtils.isNewVersion(context)) {
// 那麼就會從新獲取全部的class,因此,當你的Arouter出現了route not found時候,更新版本號 或者 開啓Arouter的debug就ok了。
// 這裏會獲取全部"com.alibaba.android.arouter.routes"目錄下的class文件的類名。
routerMap = ClassUtils.getFileNameByPackageName(mContext, ROUTE_ROOT_PAKCAGE);
// 這裏緩存到SharedPreferences裏面,方便下次獲取。
if (!routerMap.isEmpty()) {
context.getSharedPreferences(AROUTER_SP_CACHE_KEY, Context.MODE_PRIVATE).edit().putStringSet(AROUTER_SP_KEY_MAP, routerMap).apply();
}
// 將app的版本號緩存到SharedPreferences,方便下次使用。
PackageUtils.updateVersion(context);
} else {
// 若是版本號沒有更新,而且沒開啓debug,則從緩存中取出以前緩存的全部class
routerMap = new HashSet<>(context.getSharedPreferences(AROUTER_SP_CACHE_KEY, Context.MODE_PRIVATE).getStringSet(AROUTER_SP_KEY_MAP, new HashSet<String>()));
}
// 遍歷剛剛拿到的全部類名,而且反射調用它們的loadInto()方法,那麼app module中的那些生成的類,它們的loadinto()就被調用了,而且注入到參數裏面了。
for (String className : routerMap) {
// 拼接的字符串其實就是"com.alibaba.android.arouter.routes.ARouter$$Root",這不就是編譯時生成的那個"ARouter$$Root$$app"的前綴嗎。
if (className.startsWith(ROUTE_ROOT_PAKCAGE + DOT + SDK_NAME + SEPARATOR + SUFFIX_ROOT)) {
// 因而,調用了它的loadInto(map),也就等價於調用了:map.put("app", ARouter$$Group$$app.class),這個鍵值對 就放在了Warehouse.groupsIndex裏面。
((IRouteRoot) (Class.forName(className).getConstructor().newInstance())).loadInto(Warehouse.groupsIndex);
// 這個是"com.alibaba.android.arouter.routes.ARouter$$Interceptors"
} else if (className.startsWith(ROUTE_ROOT_PAKCAGE + DOT + SDK_NAME + SEPARATOR + SUFFIX_INTERCEPTORS)) {
((IInterceptorGroup) (Class.forName(className).getConstructor().newInstance())).loadInto(Warehouse.interceptorsIndex);
// 這個是"com.alibaba.android.arouter.routes.ARouter$$Providers"
} else if (className.startsWith(ROUTE_ROOT_PAKCAGE + DOT + SDK_NAME + SEPARATOR + SUFFIX_PROVIDERS)) {
((IProviderGroup) (Class.forName(className).getConstructor().newInstance())).loadInto(Warehouse.providersIndex);
}
}
} catch (Exception e) {
throw new HandlerException(TAG + "ARouter init logistics center exception! [" + e.getMessage() + "]");
}
}
複製代碼
根據上述,咱們知道:
咱們看下Warehouse的代碼:
class Warehouse {
// Cache route and metas
// 這個就是咱們剛剛注入的那個map,果真接收一個IRouteGroup,對上了。
static Map<String, Class<? extends IRouteGroup>> groupsIndex = new HashMap<>();
static Map<String, RouteMeta> routes = new HashMap<>();
// Cache provider
static Map<Class, IProvider> providers = new HashMap<>();
static Map<String, RouteMeta> providersIndex = new HashMap<>();
// Cache interceptor
static Map<Integer, Class<? extends IInterceptor>> interceptorsIndex = new UniqueKeyTreeMap<>("More than one interceptors use same priority [%s]");
static List<IInterceptor> interceptors = new ArrayList<>();
static void clear() {
routes.clear();
groupsIndex.clear();
providers.clear();
providersIndex.clear();
interceptors.clear();
interceptorsIndex.clear();
}
}
複製代碼
接下來咱們看下獲取全部"com.alibaba.android.arouter.routes"目錄下的class文件路徑的邏輯,重點!
// 咱們傳進來的packageName是 "com.alibaba.android.arouter.routes"
public static Set<String> getFileNameByPackageName(Context context, final String packageName) throws PackageManager.NameNotFoundException, IOException, InterruptedException {
final Set<String> classNames = new HashSet<>();
// 獲取全部dex文件的路徑,重點,下面有
List<String> paths = getSourcePaths(context);
final CountDownLatch parserCtl = new CountDownLatch(paths.size());
// 遍歷全部dex文件的路徑
for (final String path : paths) {
DefaultPoolExecutor.getInstance().execute(new Runnable() {
@Override
public void run() {
DexFile dexfile = null;
try {
// 是不是".zip"文件
if (path.endsWith(EXTRACTED_SUFFIX)) {
// 若是是.zip文件,就調用loadDex來加載
dexfile = DexFile.loadDex(path, path + ".tmp", 0);
} else {
// 不然,直接根據路徑建立便可
dexfile = new DexFile(path);
}
// 遍歷dexfile下面的元素
Enumeration<String> dexEntries = dexfile.entries();
while (dexEntries.hasMoreElements()) {
String className = dexEntries.nextElement();
// 若是是以"com.alibaba.android.arouter.routes"開頭,就添加
if (className.startsWith(packageName)) {
classNames.add(className);
}
}
} catch (Throwable ignore) {
Log.e("ARouter", "Scan map file in dex files made error.", ignore);
} finally {
if (null != dexfile) {
try {
dexfile.close();
} catch (Throwable ignore) {
}
}
parserCtl.countDown();
}
}
});
}
parserCtl.await();
// 返回
return classNames;
}
複製代碼
上面代碼的邏輯很簡單:
根據上面章節,咱們又知道,ARouter在編譯時生成的文件都是以"com.alibaba.android.arouter.routes"爲前綴的,因此這個函數的結果就是獲取全部Arouter編譯時生成的文件名。
而後咱們來看,怎麼獲取全部DexFile文件路徑:
public static List<String> getSourcePaths(Context context) throws PackageManager.NameNotFoundException, IOException {
ApplicationInfo applicationInfo = context.getPackageManager().getApplicationInfo(context.getPackageName(), 0);
File sourceApk = new File(applicationInfo.sourceDir);
List<String> sourcePaths = new ArrayList<>();
// 添加apk的默認路徑,能夠理解爲apk文件的路徑
sourcePaths.add(applicationInfo.sourceDir);
// EXTRACTED_NAME_EXT 就是 ".classes",因此這個結果相似於 "test.classes"這樣
String extractedFilePrefix = sourceApk.getName() + EXTRACTED_NAME_EXT;
// 若是開啓了MultiDex,那麼就遍歷獲取每個dex文件的路徑
if (!isVMMultidexCapable()) {
// 獲取全部Dex文件的總數
int totalDexNumber = getMultiDexPreferences(context).getInt(KEY_DEX_NUMBER, 1);
File dexDir = new File(applicationInfo.dataDir, SECONDARY_FOLDER_NAME);
// 遍歷獲取路徑
for (int secondaryNumber = 2; secondaryNumber <= totalDexNumber; secondaryNumber++) {
//EXTRACTED_SUFFIX 就是 ".zip",因此fileName就相似於 test.classes2.zip, test.classes3.zip這樣
String fileName = extractedFilePrefix + secondaryNumber + EXTRACTED_SUFFIX;
File extractedFile = new File(dexDir, fileName);
if (extractedFile.isFile()) {
// 添加路徑
sourcePaths.add(extractedFile.getAbsolutePath());
} else {
throw new IOException("Missing extracted secondary dex file '" + extractedFile.getPath() + "'");
}
}
}
// 返回
return sourcePaths;
}
複製代碼
上述代碼的邏輯很簡單,就是獲取app對應的全部的Dex文件的路徑,其實Android的代碼打包出來就是一堆Dex文件,能夠當作是.class文件的合集。也就是說,咱們寫代碼到打包的時候,是:.java -> .class -> .dex這樣的包裝,而如今,咱們要反過來,從 .dex -> .class這樣搞回去,固然咱們只須要獲得.class就足矣。
好,咱們再來回顧一下流程:
好,如今咱們的注入過程就完事了,說白了就是: app包名 -> 獲取.dex -> 獲取.class -> 找對應的.class -> 反射調用方法 -> 存入Warehouse中,這個過程就是注入,Warehouse就是倉庫,裏面保存了須要的key和.class,好,咱們來看獲取的過程。
調用的代碼很簡單:
// 這裏的path就是咱們經過@Route指定的,也就是"/app/activity_main"
ARouter.getInstance().build(path).navigation();
複製代碼
其中Arouter.getInstance()很簡單,就是個單例,我看看build(path)函數:
public Postcard build(String path) {
// 就一行代碼
return _ARouter.getInstance().build(path);
}
// 調到了這裏
protected Postcard build(String path) {
if (TextUtils.isEmpty(path)) {
throw new HandlerException(Consts.TAG + "Parameter is invalid!");
} else {
//...省略一些代碼
// 到這裏,extractGroup(path)是獲取group名字的,path是"/app/activity_main",那麼group就是app
return build(path, extractGroup(path));
}
}
// 獲取group名字,參數就是"/app/activity_main"
private String extractGroup(String path) {
// 校驗path的合法性,好比:若是不是以"/"開頭,就報錯
if (TextUtils.isEmpty(path) || !path.startsWith("/")) {
throw new HandlerException(Consts.TAG + "Extract the default group failed, the path must be start with '/' and contain more than 2 '/'!");
}
try {
// 獲取group名字,結果就是"app"
String defaultGroup = path.substring(1, path.indexOf("/", 1));
if (TextUtils.isEmpty(defaultGroup)) {
throw new HandlerException(Consts.TAG + "Extract the default group failed! There's nothing between 2 '/'!");
} else {
return defaultGroup;
}
} catch (Exception e) {
logger.warning(Consts.TAG, "Failed to extract default group! " + e.getMessage());
return null;
}
}
// 接着走build,咱們已經知道參數是("/app/activity_main","app")了
protected Postcard build(String path, String group) {
if (TextUtils.isEmpty(path) || TextUtils.isEmpty(group)) {
throw new HandlerException(Consts.TAG + "Parameter is invalid!");
} else {
// ... 省略一些代碼
// 直接建立了個玩意,咱們就叫它明信片吧
return new Postcard(path, group);
}
}
// 來看下明信片的構造
public Postcard(String path, String group) {
// 調下面
this(path, group, null, null);
}
// 最終走到這裏
public Postcard(String path, String group, Uri uri, Bundle bundle) {
// 保存了path,就是"/app/activity_main"
setPath(path);
// 保存了group,就是"app"
setGroup(group);
// uri是null !!!
setUri(uri);
// 建立了個Bundle()
this.mBundle = (null == bundle ? new Bundle() : bundle);
}
複製代碼
好,完事,如今咱們知道,ARouter.getInstance().build(path);最終是建立了個Postcard,保存了path和group,而後咱們看下Postcard的navigation()函數:
public Object navigation() {
// 這裏的參數傳個null
return navigation(null);
}
// 參數是null
public Object navigation(Context context) {
// 重載調用,這個context是null
return navigation(context, null);
}
// 通過一番調用,最終走到這裏
public Object navigation(Context context, NavigationCallback callback) {
// 這裏將this做爲參數調用下去,this就是Postcard,包含了剛剛的path和group。
return ARouter.getInstance().navigation(context, this, -1, callback);
}
// 來到了這裏
public Object navigation(Context mContext, Postcard postcard, int requestCode, NavigationCallback callback) {
// 又是個甩鍋函數
return _ARouter.getInstance().navigation(mContext, postcard, requestCode, callback);
}
// 接着來到了這裏
protected Object navigation(final Context context, final Postcard postcard, final int requestCode, final NavigationCallback callback) {
//...
try {
// 核心函數1
LogisticsCenter.completion(postcard);
} catch (NoRouteFoundException ex) {
if (null != callback) {
callback.onLost(postcard);
} else {
DegradeService degradeService = ARouter.getInstance().navigation(DegradeService.class);
if (null != degradeService) {
degradeService.onLost(context, postcard);
}
}
return null;
}
if (null != callback) {
callback.onFound(postcard);
}
// 判斷是否須要調用攔截器
if (!postcard.isGreenChannel()) {
// 須要調用攔截器
interceptorService.doInterceptions(postcard, new InterceptorCallback() {
@Override
public void onContinue(Postcard postcard) {
_navigation(context, postcard, requestCode, callback);
}
@Override
public void onInterrupt(Throwable exception) {
if (null != callback) {
callback.onInterrupt(postcard);
}
logger.info(Consts.TAG, "Navigation failed, termination by interceptor : " + exception.getMessage());
}
});
} else {
// 不須要調用攔截器
// 核心函數2
return _navigation(context, postcard, requestCode, callback);
}
return null;
}
複製代碼
接着,咱們來看那兩個核心函數:
// 核心函數1
public synchronized static void completion(Postcard postcard) {
if (null == postcard) {
throw new NoRouteFoundException(TAG + "No postcard!");
}
// 先從Warehouse.routes裏面獲取RouteMeta,咱們上述代碼的經歷只用到了Warehouse.groupsIndex,因此確定是null
// 第二次過來了,如今Warehouse.routes有值了,就是根據path拿到的。
RouteMeta routeMeta = Warehouse.routes.get(postcard.getPath());
if (null == routeMeta) {
// 接着跑這裏,從Warehouse.groupsIndex獲取IRouteGroup的class!終於用到咱們前面注入的玩意兒了,postcard.getGroup()就是"app",
// 而咱們前面調過 Warehouse.groupsIndex.put("app", ARouter$$Group$$app.class),這裏就直接取出來了。
Class<? extends IRouteGroup> groupMeta = Warehouse.groupsIndex.get(postcard.getGroup());
if (null == groupMeta) {
throw new NoRouteFoundException(TAG + "There is no route match the path [" + postcard.getPath() + "], in group [" + postcard.getGroup() + "]");
} else {
try {
// 開始反射了
IRouteGroup iGroupInstance = groupMeta.getConstructor().newInstance();
// 調了loadInfo,咱們回去看下ARouter$$Group$$app.class的loadInto方法:
// atlas.put("/app/activity_main", RouteMeta.build(RouteType.ACTIVITY, MainActivity.class, "/app/activity_main", "app", null, -1, -2147483648));
// 直接put了,key是"/app/activity_main",跟Postcard的path同樣,這下就放在Warehouse.routes裏面了,下次就能拿到了。
iGroupInstance.loadInto(Warehouse.routes);
// 把group扔掉,group的意義就是用來拿route的,如今拿到了已經沒用了,刪除省內存。
Warehouse.groupsIndex.remove(postcard.getGroup());
} catch (Exception e) {
throw new HandlerException(TAG + "Fatal exception when loading group meta. [" + e.getMessage() + "]");
}
// 又調了本身,回到這個函數頭從新看
completion(postcard);
}
} else {
// 第二次進來,跑這裏,設置一堆屬性,還記得很重要的那一堆參數嗎
// RouteMeta.build(RouteType.ACTIVITY, MainActivity.class, "/app/activity_main", "app", null, -1, -2147483648)
postcard.setDestination(routeMeta.getDestination()); // MainActivity.class
postcard.setType(routeMeta.getType()); // RouteType.ACTIVITY
postcard.setPriority(routeMeta.getPriority()); // -1
postcard.setExtra(routeMeta.getExtra()); // -2147483648
// uri是null,不用看
Uri rawUri = postcard.getUri();
if (null != rawUri) { // Try to set params into bundle.
Map<String, String> resultMap = TextUtils.splitQueryParameters(rawUri);
Map<String, Integer> paramsType = routeMeta.getParamsType();
if (MapUtils.isNotEmpty(paramsType)) {
for (Map.Entry<String, Integer> params : paramsType.entrySet()) {
setValue(postcard,
params.getValue(),
params.getKey(),
resultMap.get(params.getKey()));
}
postcard.getExtras().putStringArray(ARouter.AUTO_INJECT, paramsType.keySet().toArray(new String[]{}));
}
postcard.withString(ARouter.RAW_URI, rawUri.toString());
}
// 根據類型執行邏輯,咱們的類型是RouteType.ACTIVITY,下面好像都沒有
switch (routeMeta.getType()) {
case PROVIDER:
Class<? extends IProvider> providerMeta = (Class<? extends IProvider>) routeMeta.getDestination();
IProvider instance = Warehouse.providers.get(providerMeta);
if (null == instance) { // There's no instance of this provider
IProvider provider;
try {
provider = providerMeta.getConstructor().newInstance();
provider.init(mContext);
Warehouse.providers.put(providerMeta, provider);
instance = provider;
} catch (Exception e) {
throw new HandlerException("Init provider failed! " + e.getMessage());
}
}
postcard.setProvider(instance);
postcard.greenChannel();
break;
case FRAGMENT:
postcard.greenChannel();
default:
break;
}
}
}
複製代碼
核心函數1搞完了,總共跑了兩次,
好,如今咱們的Warehouse.routes有數據了,而且參數postcard有destination和type了,因而接着執行核心函數2:
// 核心函數2
private Object _navigation(final Context context, final Postcard postcard, final int requestCode, final NavigationCallback callback) {
// 咱們知道參數context是null,因而就取Arouter.init(context)的context,也就是application
final Context currentContext = null == context ? mContext : context;
// 直接根據類型執行邏輯了
switch (postcard.getType()) {
case ACTIVITY: // 這就是咱們的類型
// 建立intent,destination就是MainActivity.class,咱們在覈心函數1裏面指定過了
final Intent intent = new Intent(currentContext, postcard.getDestination());
intent.putExtras(postcard.getExtras());
// Flags,咱們沒有設置,就是-1
int flags = postcard.getFlags();
if (-1 != flags) {
intent.setFlags(flags);
} else if (!(currentContext instanceof Activity)) { // 咱們的context不是Activity
// 因而就添加FLAG_ACTIVITY_NEW_TASK這個Flag(不然用application啓動Activity,有的版本會崩潰),若是navigation()傳遞了Activity做爲context,就不會添加這個flag
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
}
// 設置Action,咱們是沒有的
String action = postcard.getAction();
if (!TextUtils.isEmpty(action)) {
intent.setAction(action);
}
// 切換到UI線程去啓動Activity,Activity啓動了,完事。
runInMainThread(new Runnable() {
@Override
public void run() {
startActivity(requestCode, currentContext, intent, postcard, callback);
}
});
break;
case PROVIDER:
return postcard.getProvider();
case BOARDCAST:
case CONTENT_PROVIDER:
case FRAGMENT:
Class fragmentMeta = postcard.getDestination();
try {
Object instance = fragmentMeta.getConstructor().newInstance();
if (instance instanceof Fragment) {
((Fragment) instance).setArguments(postcard.getExtras());
} else if (instance instanceof android.support.v4.app.Fragment) {
((android.support.v4.app.Fragment) instance).setArguments(postcard.getExtras());
}
return instance;
} catch (Exception ex) {
logger.error(Consts.TAG, "Fetch fragment instance error, " + TextUtils.formatStackTrace(ex.getStackTrace()));
}
case METHOD:
case SERVICE:
default:
return null;
}
return null;
}
複製代碼
核心函數2的邏輯很簡答,直接用postcard裏面的type去執行對應邏輯,而後執行到了Activity分支,因而就取出destination,也就是MainActivity.class,來啓動。
好,咱們來小結一下:
咱們經過path去navigation(),此path="app/activity_main",此時會根據這個path構造一個postCard,其中以group="app"(以"/"分割獲得的),而後以group(也就是"app")從Werehouse的groupIndex裏面獲取值,就獲得了"Arouter app.class"這個class對象, 接着,使用反射建立一個實例,並調用loadInto()函數,因而就執行到了:
atlas.put("/app/activity_main", RouteMeta.build(RouteType.ACTIVITY, MainActivity.class, "/app/activity_main", "app", null, -1, -2147483648));
複製代碼
此後,咱們的Werehouse的routes裏面就有了:
{"/app/activity_main", RouteMeta.build(RouteType.ACTIVITY, MainActivity.class, "/app/activity_main", "app"},
複製代碼
接着,根據postCard的類型去進行switch-case,當case到RouteType.ACTIVITY時,就進行Activity的啓動,此時咱們有了對應Activity的class,若是navigation(context)傳遞了context,則就用這個context來啓動Activity,不然就用Arouter.init(context)這個context來啓動,若是這個context不是activity,則會添加Intent.FLAG_ACTIVITY_NEW_TASK這個flag來啓動Activity。
咱們一路追蹤了Arouter的源碼流程:
總之,這跟咱們一開始用的common公共依賴是同樣的,Arouter自己也是被app和login兩個module依賴的,因此Arouter自己就是第三方公共依賴。
因此,一個大思想就是: 頂層兩個互不相通的模塊想要通訊,能夠藉助公共依賴的底層模塊來進行,這裏的模塊是廣義的泛指,好比兩個應用程序互相通訊能夠藉助系統程序,兩個activity互相通訊能夠藉助application,甚至兩個類互相通訊能夠藉助父類。兩個小技術就是Apt和類加載,固然也能夠不用這兩個技術,技術是手段,思想是目標,只要能實現目標,什麼都無所謂。