一、ARoute 初始化源码分析
引入了 ARoute 的应用 , 一般会在主应用的 Application 中的 onCreate 方法中初始化 ARoute ;
package kim.hsl.component; import android.app.Application; import com.alibaba.android.arouter.launcher.ARouter; public class MyApplication extends Application { public static MyApplication mApplication; public MyApplication() { super(); mApplication = this; } public static MyApplication getInstance() { return mApplication; } @Override public void onCreate() { super.onCreate(); if (isDebug()) { // 这两行必须写在init之前,否则这些配置在init过程中将无效 ARouter.openLog(); // 打印日志 ARouter.openDebug(); // 开启调试模式(如果在InstantRun模式下运行,必须开启调试模式!线上版本需要关闭,否则有安全风险) } ARouter.init(this); // 尽可能早,推荐在Application中初始化 } public boolean isDebug(){ return BuildConfig.DEBUG; } }
其中
ARouter.init(this);
1
是 ARoute 初始化的核心方法 , 其调用了 com.alibaba.android.arouter.launcher.ARouter 类的 init 方法 , com.alibaba.android.arouter.launcher.ARouter 类是一个空壳类 , 实际的代码逻辑都定义在 com.alibaba.android.arouter.launcher._ARouter 类中 ;
package com.alibaba.android.arouter.launcher; public final class ARouter { /** * Init, it must be call before used router. */ 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."); } } }
在 ARoute 类的 init 方法中调用了 _ARoute 的 init 方法
_ARouter.init(application);
1
_ARoute 相关源码如下 :
final class _ARouter { protected static synchronized boolean init(Application application) { mContext = application; LogisticsCenter.init(mContext, executor); logger.info(Consts.TAG, "ARouter init success!"); hasInit = true; mHandler = new Handler(Looper.getMainLooper()); return true; } }
其中的
LogisticsCenter.init(mContext, executor);
1
是核心的逻辑 , 在该方法中 , 加载了路由表 ,
路由表类是 注解处理器 在编译时生成的类 , 生成的目录是 " D:\002_Project\002_Android_Learn\ComponentDemo\app\build\generated\ap_generated_sources\debug\out\com\alibaba\android\arouter\routes " ;
生成的路由表示例 :
package com.alibaba.android.arouter.routes; import com.alibaba.android.arouter.facade.enums.RouteType; import com.alibaba.android.arouter.facade.model.RouteMeta; import com.alibaba.android.arouter.facade.template.IRouteGroup; import java.lang.Override; import java.lang.String; import java.util.Map; import kim.hsl.component.MainActivity; /** * DO NOT EDIT THIS FILE!!! IT WAS GENERATED BY AROUTER. */ public class ARouter$$Group$$app implements IRouteGroup { @Override public void loadInto(Map<String, RouteMeta> atlas) { atlas.put("/app/MainActivity", RouteMeta.build(RouteType.ACTIVITY, MainActivity.class, "/app/mainactivity", "app", null, -1, -2147483648)); } }
生成的对应的 Root 表 , 作为路由表的索引 ;
package com.alibaba.android.arouter.routes; import com.alibaba.android.arouter.facade.template.IRouteGroup; import com.alibaba.android.arouter.facade.template.IRouteRoot; import java.lang.Class; import java.lang.Override; import java.lang.String; import java.util.Map; /** * DO NOT EDIT THIS FILE!!! IT WAS GENERATED BY AROUTER. */ public class ARouter$$Root$$app implements IRouteRoot { @Override public void loadInto(Map<String, Class<? extends IRouteGroup>> routes) { routes.put("app", ARouter$$Group$$app.class); } }
LogisticsCenter.init(mContext, executor) 方法执行的操作就是创建 ARouter$$Group$$app 类 , 调用该类的 loadInto 方法 , 导入路由表 , 将路由表加载到内存中 ;
二、ARoute 界面跳转源码分析
ARoute 使用时的示例如下 , 在该 Activity 类中 , 涉及到注解使用 , 界面跳转 ; package kim.hsl.component; import androidx.appcompat.app.AppCompatActivity; import android.os.Bundle; import android.util.Log; import android.view.View; import com.alibaba.android.arouter.facade.annotation.Route; import com.alibaba.android.arouter.launcher.ARouter; // 在支持路由的页面上添加注解(必选) // 这里的路径需要注意的是至少需要有两级,/xx/xx @Route(path = "/app/MainActivity") public class MainActivity extends AppCompatActivity { private static final String TAG = "MainActivity"; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); } public void onClick(View view) { Log.i(TAG, "跳转到 Module1"); ARouter.getInstance().build("/module1/Module1Activity").navigation(); finish(); } }
当调用到
ARouter.getInstance().build("/module1/Module1Activity").navigation();
1
代码进行界面跳转时 ,
public final class ARouter { /** * Build the roadmap, draw a postcard. * * @param path Where you go. */ public Postcard build(String path) { return _ARouter.getInstance().build(path); } }
ARouter.getInstance().build 方法实际上调用的是 _ARoute 的 build(String path) 方法 , 在 该方法中又调用了 build 的重载函数 build(String path, String group, Boolean afterReplace) , 最终返回了一个跳卡 Postcard 对象 ;
final class _ARouter { /** * Build postcard by path and default group */ protected Postcard build(String path) { if (TextUtils.isEmpty(path)) { // 不能传入空字符串地址 , 否则直接报异常 throw new HandlerException(Consts.TAG + "Parameter is invalid!"); } else { PathReplaceService pService = ARouter.getInstance().navigation(PathReplaceService.class); if (null != pService) { path = pService.forString(path); } // 核心是调用了 build 的重载函数 // extractGroup 函数的作用明显是取出路径中的分组 , 就是前两个 / 之间的字符串内容 return build(path, extractGroup(path), true); } } /** * Build postcard by path and group */ 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); } } }
跳转的实际操作是调用的跳卡的 navigation 方法 , 此时该跳卡中 , 只有 path 路由地址 , group 路由分子名称 , 其它信息还是空的 ;
public final class Postcard extends RouteMeta { public Postcard(String path, String group) { this(path, group, null, null); } /** * Navigation to the route with path in postcard. * No param, will be use application context. */ public Object navigation() { return navigation(null); } /** * Navigation to the route with path in postcard. * * @param context Activity and so on. */ public Object navigation(Context context) { return navigation(context, null); } /** * Navigation to the route with path in postcard. * * @param context Activity and so on. */ public Object navigation(Context context, NavigationCallback callback) { return ARouter.getInstance().navigation(context, this, -1, callback); } }
其中的 NavigationCallback 参数 , 传入一个监听回调接口 , 可以监听跳转操作是否完成了 ;
/** * Callback after navigation. * * @author Alex <a href="mailto:zhilong.liu@aliyun.com">Contact me.</a> * @version 1.0 * @since 2016/9/22 14:15 */ public interface NavigationCallback { /** * Callback when find the destination. * * @param postcard meta */ void onFound(Postcard postcard); /** * Callback after lose your way. * * @param postcard meta */ void onLost(Postcard postcard); /** * Callback after navigation. * * @param postcard meta */ void onArrival(Postcard postcard); /** * Callback on interrupt. * * @param postcard meta */ void onInterrupt(Postcard postcard); }
调用 Postcard 跳卡对象的 navigation() 方法 , 之后调用了一系列的 navigation 重载方法 , navigation(Context context) , navigation(Context context, NavigationCallback callback) , 最终又调用了 ARouter.getInstance().navigation(context, this, -1, callback) 方法 ;
在 ARouter 的 navigation 方法中 , 依旧是调用了 _ARoute 的 navigation 方法 ;
public final class ARouter { /** * Launch the navigation. * * @param mContext . * @param postcard . * @param requestCode Set for startActivityForResult * @param callback cb */ public Object navigation(Context mContext, Postcard postcard, int requestCode, NavigationCallback callback) { return _ARouter.getInstance().navigation(mContext, postcard, requestCode, callback); } }
跳卡 Postcard 对象中目前有 路由地址 , 路由分组 两个信息 , 先调用
LogisticsCenter.completion(postcard);
1
函数, 将跳卡 Postcard 对象补全 , 最重要的是该路由地址对应的 Class 类对象 , 通过之前 ARoute 初始化到内存的路由表补充跳卡中的数据 ;
然后判断该跳转是否是绿色通道 , 如果是继续执行跳转 ;
如果不是 , 则触发拦截器 , 拦截器判定未通过 , 则中断跳转 , 拦截器判定通过 , 则继续执行跳转 ;
这里的拦截器一般用于权限鉴定 , 比如用户是否购买会员 , 是否购买服务 , 是否拥有权限等等 ;
拦截器的详细用法自行去 GitHub 上查看 ;
最终调用 _navigation 方法进行跳转 ;
在 _navigation 方法中 , 跳卡 Postcard 的 Activity 跳转就是 创建 Intent , 并执行 startActivity 方法进行界面跳转 ;
final class _ARouter { /** * Use router navigation. * * @param context Activity or null. * @param postcard Route metas * @param requestCode RequestCode * @param callback cb */ 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; } try { // 补充跳卡数据 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. 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()) { // 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(context, 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(context, postcard, requestCode, callback); } return null; } private Object _navigation(final Context context, final Postcard postcard, final int requestCode, final NavigationCallback callback) { final Context currentContext = null == context ? mContext : context; 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 (-1 != flags) { intent.setFlags(flags); } else if (!(currentContext instanceof Activity)) { // Non activity, need less one flag. intent.setFlags(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() { 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; } }
插个插曲 , 分析下从内存中查找路由信息并填充到 跳卡 Postcard 对象的过程 ;
在 LogisticsCenter 中获取跳卡 Postcard 完整数据的方法 , 首先从 Warehouse.routes 中获取相关数据 , 之前 ARoute 的 init 初始化方法中将路由表加载到了内存中的该 Warehouse 对应的静态成员中 , 这里直接从该静态成员中获取 路由 数据 ;
如果从 Warehouse 中获取 路由信息 失败 , 说明路由表还没有加载 , 那么先加载路由表 , 路由表加载成功后 , 再获取跳卡对应的路由信息 , 并填充到 跳卡 Postcard 对象中 ;
public class LogisticsCenter { /** * Completion the postcard by route metas * * @param postcard Incomplete postcard, should complete by this method. */ public synchronized static void completion(Postcard postcard) { if (null == postcard) { throw new NoRouteFoundException(TAG + "No postcard!"); } RouteMeta routeMeta = Warehouse.routes.get(postcard.getPath()); if (null == routeMeta) { // Maybe its does't exist, or didn't load. // 如果路由表还没有加载 , 此时加载路由表 Class<? extends IRouteGroup> groupMeta = Warehouse.groupsIndex.get(postcard.getGroup()); // Load route meta. // 如果再次加载失败 , 直接报异常退出 if (null == groupMeta) { 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())); } IRouteGroup iGroupInstance = groupMeta.getConstructor().newInstance(); // 将路由表加载到内存中 iGroupInstance.loadInto(Warehouse.routes); // 将路由表的索引信息从内存中移除 , 节省内存 Warehouse.groupsIndex.remove(postcard.getGroup()); 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.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()); } 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) { throw new HandlerException("Init provider failed! " + e.getMessage()); } } postcard.setProvider(instance); postcard.greenChannel(); // Provider should skip all of interceptors break; case FRAGMENT: postcard.greenChannel(); // Fragment needn't interceptors default: break; } } } }