配置
Kotlin項目:
module App:
apply plugin: 'kotlin-kapt'
defaultConfig{
javaCompileOptions {
annotationProcessorOptions {
//AROUTER_MODULE_NAME必配項 用于拼接生成文件名 AROUTER_GENERATE_DOC
// AROUTER_GENERATE_DOC = enable 生成Json文檔
// 生成的文檔路徑 : build/generated/source/apt/(debug or release)/com/alibaba/android/arouter/docs/arouter-map-of-${moduleName}.json
arguments = [AROUTER_MODULE_NAME:project_name,AROUTER_GENERATE_DOC:"enable"]
}
}
}
dependencies{
api 'com.alibaba:arouter-api:1.5.0'
kapt 'com.alibaba:arouter-compiler:1.2.2'
}
//項目根目錄build.gradle
dependencies {
classpath "com.alibaba:arouter-register:1.0.2"
}
源碼流程分析
三個關鍵階段
自定義處理器工作流程:
自定義處理器源碼分析:結構圖
生成類的關系
調用類:
@Route(path = "/kotlin/test")
class KotlinTestActivity : Activity() {
@Autowired
@JvmField var name: String? = null
@Autowired
@JvmField var age: Int? = 0
override fun onCreate(savedInstanceState: Bundle?) {
ARouter.getInstance().inject(this) // Start auto inject.
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_kotlin_test)
content.text = "name = $name, age = $age"
}
}
ARouter生成類:
public class KotlinTestActivity$$ARouter$$Autowired implements ISyringe {
private SerializationService serializationService;
@Override
public void inject(Object target) {
serializationService = ARouter.getInstance().navigation(SerializationService.class);
KotlinTestActivity substitute = (KotlinTestActivity)target;
substitute.name = substitute.getIntent().getExtras() == null ? substitute.name : substitute.getIntent().getExtras().getString("name", substitute.name);
substitute.age = substitute.getIntent().getIntExtra("age", substitute.age);
}
}
ARouter.getInstance().inject(this)
這段代碼最終會利用當前類名和規則,拼接成KotlinTestActivity$$ARouter$$Autowired
的全類名,然后利用反射傳進對象。然后執行inject(this); 然后里面會初始化傳輸字段序列化服務,然后強轉target,開始賦值數據
生成類文件的關系
由此可總結出下面整體工作流程
ARouter整體工作流程
運行時原理分析
初始化工作流程分析
//初始化
ARouter.init(getApplication());
_ARouter.init(application)
LogisticsCenter.init(mContext, executor)
LogisticsCenter.init(mContext, executor):
public synchronized static void init(Context context, ThreadPoolExecutor tpe) throws HandlerException {
mContext = context;
executor = tpe;
try {
long startInit = System.currentTimeMillis();
//load by plugin first
loadRouterMap();
if (registerByPlugin) {
logger.info(TAG, "Load router map by arouter-auto-register plugin.");
} else {
//1 生成文件所有文件的全類名字符串集合
Set<String> routerMap;
// It will rebuild router map every times when debuggable.
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.
//2. 賦值集合
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();
}
PackageUtils.updateVersion(context); // Save new version name when router map update finishes.
} 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>()));
}
logger.info(TAG, "Find router map finished, map size = " + routerMap.size() + ", cost " + (System.currentTimeMillis() - startInit) + " ms.");
startInit = System.currentTimeMillis();
//3. 遍歷集合
for (String className : routerMap) {
// 這里裝在了3中類別的文件: (1) Root文件
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);
//(2) Interceptor 文件
} else if (className.startsWith(ROUTE_ROOT_PAKCAGE + DOT + SDK_NAME + SEPARATOR + SUFFIX_INTERCEPTORS)) {
// Load interceptorMeta
((IInterceptorGroup) (Class.forName(className).getConstructor().newInstance())).loadInto(Warehouse.interceptorsIndex);
// //(3) Provider 文件
} else if (className.startsWith(ROUTE_ROOT_PAKCAGE + DOT + SDK_NAME + SEPARATOR + SUFFIX_PROVIDERS)) {
// Load providerIndex
((IProviderGroup) (Class.forName(className).getConstructor().newInstance())).loadInto(Warehouse.providersIndex);
}
}
}
logger.info(TAG, "Load root element finished, cost " + (System.currentTimeMillis() - startInit) + " ms.");
if (Warehouse.groupsIndex.size() == 0) {
logger.error(TAG, "No mapping files were found, check your configuration please!");
}
if (ARouter.debuggable()) {
logger.debug(TAG, String.format(Locale.getDefault(), "LogisticsCenter has already been loaded, GroupIndex[%d], InterceptorIndex[%d], ProviderIndex[%d]", Warehouse.groupsIndex.size(), Warehouse.interceptorsIndex.size(), Warehouse.providersIndex.size()));
}
} catch (Exception e) {
throw new HandlerException(TAG + "ARouter init logistics center exception! [" + e.getMessage() + "]");
}
}
可以看出沒加載AutoWired文件,也就是說@AutoWired注解字段 在inject()
時去創建對象賦值的。
反射找到對象并將Warehouse中的集合作為參數傳遞進入,把信息裝載到內存。注意,這里只是加載了信息,但信息里面的具體內容并未創建。什么意思呢?以Provider為例:
public class ARouter$$Providers$$modulejava implements IProviderGroup {
@Override
public void loadInto(Map<String, RouteMeta> providers) {
providers.put("com.alibaba.android.arouter.demo.service.HelloService", RouteMeta.build(RouteType.PROVIDER, HelloServiceImpl.class, "/yourservicegroupname/hello", "yourservicegroupname", null, -1, -2147483648));
providers.put("com.alibaba.android.arouter.facade.service.SerializationService", RouteMeta.build(RouteType.PROVIDER, JsonServiceImpl.class, "/yourservicegroupname/json", "yourservicegroupname", null, -1, -2147483648));
providers.put("com.alibaba.android.arouter.demo.module1.testservice.SingleService", RouteMeta.build(RouteType.PROVIDER, SingleService.class, "/yourservicegroupname/single", "yourservicegroupname", null, -1, -2147483648));
}
}
反射生成ARouter$$Providers$$modulejava
對象,但我們用的是com.alibaba.android.arouter.demo.service.HelloService
HelloService這個具體類。但這個類這是并未實例化,只有用到的時候才回去實例化創建。其他同理。
LogisticsCenter.init(mContext, executor):
還用到兩個重要的類和方法: ClassUtils
/Warehouse
1. ClassUtils.getFileNameByPackageName(mContext, ROUTE_ROOT_PAKCAGE);
public static Set<String> getFileNameByPackageName(Context context, final String packageName)
throws PackageManager.NameNotFoundException, IOException, InterruptedException {
final Set<String> classNames = new HashSet<>();
List<String> paths = getSourcePaths(context);
//線程同步
final CountDownLatch parserCtl = new CountDownLatch(paths.size());
for (final String path : paths) {
DefaultPoolExecutor.getInstance().execute(new Runnable() {
@Override
public void run() {
//Dex文件
DexFile dexfile = null;
try {
if (path.endsWith(EXTRACTED_SUFFIX)) {
//NOT use new DexFile(path), because it will throw "permission error in /data/dalvik-cache"
dexfile = DexFile.loadDex(path, path + ".tmp", 0);
} else {
dexfile = new DexFile(path);
}
Enumeration<String> dexEntries = dexfile.entries();
while (dexEntries.hasMoreElements()) {
String className = dexEntries.nextElement();
//核心判斷 packageName是我們上面傳入的參數ROUTE_ROOT_PAKCAGE = "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();
Log.d(Consts.TAG, "Filter " + classNames.size() + " classes by packageName <" + packageName + ">");
return classNames;
}
如果初始化加載流程沒有走完,路由操作將會阻塞,直到加載流程完成
Warehouse
相當于一個加載信息裝載的容器類
class Warehouse {
// Cache route and metas
//groupsIndex 裝載ARouter$$Root$$moduleName 中的Root文件 <groupName,Group.class>
static Map<String, Class<? extends IRouteGroup>> groupsIndex = new HashMap<>();
//routes 按需加載完成后 把加載的數據存到routes 集合中,等項目中用到的時候,找到集合中需要的元素 再去實例化對象
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();
}
}
初始化總結:
- 通過
ClassUtils.getFileNameByPackageName
方法,加載所有生成的dex文件,遍歷每個dex中的java文件,并進行com.alibaba.android.arouter.routes
包名匹配,這必然是ARouter幫我們生成的 -
CountDownLatch parserCtl
開啟線程池,進行同步操作,在沒有遍歷所有生成的java并裝載進Set集合
,navigation路由操作將會阻塞 - 遍歷Set集合 對
Warehouse
中的三個重要Map進行賦值,可以看到Warehouse
中都是static
修飾的,這樣它就像個倉庫,對外提供數據,同時通過反射調用方法也能看出,這里只是對信息進行加載,里面Group
組中的RouteMate并未創建
路由過程源碼分析
ARouter.getInstance()
.build("/kotlin/test")
.withString("name", "老王")
.withInt("age", 23)
.navigation();
.build("/kotlin/test")
查找分組,構建Postcard對象。
protected Postcard build(String path) {
if (TextUtils.isEmpty(path)) {
throw new HandlerException(Consts.TAG + "Parameter is invalid!");
} else {
// 重定向路由路徑Service 如果想自定義 則實現PathReplaceService
PathReplaceService pService = ARouter.getInstance().navigation(PathReplaceService.class);
if (null != pService) {
//通過forString(path) 返回修改后的Path
path = pService.forString(path);
}
//繼續往下走
return build(path, extractGroup(path), true);
}
}
extractGroup(path):
private String extractGroup(String 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 {
//關鍵代碼 defaultGroup 默認分組 以路由路徑 第一個節點為分組名稱
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(path, extractGroup(path), true);:
protected Postcard build(String path, String group, Boolean afterReplace) {
if (TextUtils.isEmpty(path) || TextUtils.isEmpty(group)) {
throw new HandlerException(Consts.TAG + "Parameter is invalid!");
} else {
if (!afterReplace) {
PathReplaceService pService = ARouter.getInstance().navigation(PathReplaceService.class);
if (null != pService) {
path = pService.forString(path);
}
}
return new Postcard(path, group);
}
}
創建:Postcard
對象
.withString("name", "老王").withInt("age", 23)
構建參數mBundle對象。
.navigation();
運行在異步線程中
protected Object navigation(final Context context, final Postcard postcard, final int requestCode, final NavigationCallback callback) {
PretreatmentService pretreatmentService = ARouter.getInstance().navigation(PretreatmentService.class);
if (null != pretreatmentService && !pretreatmentService.onPretreatment(context, postcard)) {
// Pretreatment failed, navigation canceled.
return null;
}
// Set context to postcard.
postcard.setContext(null == context ? mContext : context);
try {
//給Postcard賦值其他數據
LogisticsCenter.completion(postcard);
} catch (NoRouteFoundException ex) {//出現異常的情況
logger.warning(Consts.TAG, ex.getMessage());
if (debuggable()) {
// Show friendly tips for user.
runInMainThread(new Runnable() {
@Override
public void run() {
Toast.makeText(mContext, "There's no route matched!\n" +
" Path = [" + postcard.getPath() + "]\n" +
" Group = [" + postcard.getGroup() + "]", Toast.LENGTH_LONG).show();
}
});
}
if (null != callback) {
// 路由回調 如果不為空
callback.onLost(postcard);
} else {
// No callback for this invoke, then we use the global degrade service.
//反射創建全局降級服務 回調 onLost方法
DegradeService degradeService = ARouter.getInstance().navigation(DegradeService.class);
if (null != degradeService) {
degradeService.onLost(context, postcard);
}
}
return null;
}
if (null != callback) {
//回調路由成功的方法
callback.onFound(postcard);
}
//如果綠色通道為false 則添加攔截器
if (!postcard.isGreenChannel()) { // It must be run in async thread, maybe interceptor cost too mush time made ANR.
// 運行在線程池中
interceptorService.doInterceptions(postcard, new InterceptorCallback() {
/**
* Continue process
*
* @param postcard route meta
*/
@Override
public void onContinue(Postcard postcard) {
//繼續處理完成后繼續向下執行
_navigation(postcard, requestCode, callback);
}
/**
* Interrupt process, pipeline will be destory when this method called.
*
* @param exception Reson of interrupt.
*/
@Override
public void onInterrupt(Throwable exception) {
if (null != callback) {
//打斷路由 停止路由
callback.onInterrupt(postcard);
}
logger.info(Consts.TAG, "Navigation failed, termination by interceptor : " + exception.getMessage());
}
});
} else {
//繼續處理完成后繼續向下執行
return _navigation(postcard, requestCode, callback);
}
return null;
}
LogisticsCenter.completion(postcard);
public synchronized static void completion(Postcard postcard) {
if (null == postcard) {
throw new NoRouteFoundException(TAG + "No postcard!");
}
//routes是加載信息 但為實例化對象 之前提過 所以這里第一次肯定為空
RouteMeta routeMeta = Warehouse.routes.get(postcard.getPath());
if (null == routeMeta) {
// Maybe its does't exist, or didn't load.
if (!Warehouse.groupsIndex.containsKey(postcard.getGroup())) {
throw new NoRouteFoundException(TAG + "There is no route match the path [" + postcard.getPath() + "], in group [" + postcard.getGroup() + "]");
} else {
// Load route and cache it into memory, then delete from metas.
try {
if (ARouter.debuggable()) {
logger.debug(TAG, String.format(Locale.getDefault(), "The group [%s] starts loading, trigger by [%s]", postcard.getGroup(), postcard.getPath()));
}
//對Warehouse.routes初始化 實例化分組對象 獲取數據
addRouteGroupDynamic(postcard.getGroup(), null);
if (ARouter.debuggable()) {
logger.debug(TAG, String.format(Locale.getDefault(), "The group [%s] has already been loaded, trigger by [%s]", postcard.getGroup(), postcard.getPath()));
}
} catch (Exception e) {
throw new HandlerException(TAG + "Fatal exception when loading group meta. [" + e.getMessage() + "]");
}
//重新調用自己
completion(postcard); // Reload
}
} else {
//給postcard賦值其他信息
postcard.setDestination(routeMeta.getDestination());
postcard.setType(routeMeta.getType());
postcard.setPriority(routeMeta.getPriority());
postcard.setExtra(routeMeta.getExtra());
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)) {
// Set value by its type, just for params which annotation by @Param
for (Map.Entry<String, Integer> params : paramsType.entrySet()) {
setValue(postcard,
params.getValue(),
params.getKey(),
resultMap.get(params.getKey()));
}
// Save params name which need auto inject.
postcard.getExtras().putStringArray(ARouter.AUTO_INJECT, paramsType.keySet().toArray(new String[]{}));
}
// Save raw uri
postcard.withString(ARouter.RAW_URI, rawUri.toString());
}
//類型判斷 PROVIDER類型實例化對象 PROVIDER/FRAGMENT默認開啟通道,不經過攔截
switch (routeMeta.getType()) {
case PROVIDER: // if the route is provider, should find its instance
// Its provider, so it must implement IProvider
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) {
logger.error(TAG, "Init provider failed!", e);
throw new HandlerException("Init provider failed!");
}
}
postcard.setProvider(instance);
postcard.greenChannel(); // Provider should skip all of interceptors
break;
case FRAGMENT:
postcard.greenChannel(); // Fragment needn't interceptors
default:
break;
}
}
}
public synchronized static void addRouteGroupDynamic(String groupName, IRouteGroup group) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
if (Warehouse.groupsIndex.containsKey(groupName)){
// If this group is included, but it has not been loaded
// load this group first, because dynamic route has high priority.
//對Warehouse.routes初始化 實例化分組對象 獲取數據
Warehouse.groupsIndex.get(groupName).getConstructor().newInstance().loadInto(Warehouse.routes);
Warehouse.groupsIndex.remove(groupName);
}
// cover old group.
if (null != group) {
group.loadInto(Warehouse.routes);
}
}
_navigation(postcard, requestCode, callback);
private Object _navigation(final Postcard postcard, final int requestCode, final NavigationCallback callback) {
final Context currentContext = postcard.getContext();
//判斷路由類型
switch (postcard.getType()) {
case ACTIVITY:
// Build intent
final Intent intent = new Intent(currentContext, postcard.getDestination());
intent.putExtras(postcard.getExtras());
// Set flags.
int flags = postcard.getFlags();
if (0 != flags) {
intent.setFlags(flags);
}
// Non activity, need FLAG_ACTIVITY_NEW_TASK
if (!(currentContext instanceof Activity)) {
//context如果是Application 那么新建任務棧
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
}
// Set Actions
String action = postcard.getAction();
if (!TextUtils.isEmpty(action)) {
intent.setAction(action);
}
// Navigation in main looper.
runInMainThread(new Runnable() {
@Override
public void run() {
//Activity則調用startActivity
startActivity(requestCode, currentContext, intent, postcard, callback);
}
});
break;
case PROVIDER:
//返回LogisticsCenter.completion(postcard)方法中創建的對象
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());
}
//反射返回fragment實例
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;
}
路由結束。
注意:通過ARouter源碼分析,IProvider接口類型文件會生成兩份,一份是ARouter{groupName},繼承IRouteGroup,另一份是ARouter
{groupName},繼承IProviderGroup。類文件的內容是一摸一樣的。為什么要這么設計呢?個人理解。答案是RoutProcesser里面不僅收集@Route注解的類,并聲稱信息。同時還會生成json說明文件。所以ARouter
{groupName}類型是json文件生成用的。是運行時調用數據收集用的。