怎么看网站的备案信息,天津网站备案去哪,品牌高端网站设计,100个商业经典案例前言#xff1a;ARouter是一个用于 Android App 进行组件化改造的路由框架 —— 支持模块间的路由、通信、解耦。 一、路由简介#xff1a;
路由#xff1a;就是通过互联的网络把信息从源地址传输到目的地址的活动。完成路由这个操作的实体设备就是 路由器#xff08;Rout… 前言ARouter是一个用于 Android App 进行组件化改造的路由框架 —— 支持模块间的路由、通信、解耦。 一、路由简介
路由就是通过互联的网络把信息从源地址传输到目的地址的活动。完成路由这个操作的实体设备就是 路由器Router。
二、原理解析
2.1 使用步骤
使用ARouter在进行Activity跳转初始化ARouter、添加注解Route、发起路由跳转。
步骤1初始化ARouter
ARouter.init(mApplication); // 尽可能早推荐在Application中初始化步骤2注册Activity路由
// moduleA 在支持路由的页面上添加注解 路径注意至少需有两级/xx/xx
Route(path /test/activity1, name 测试用 Activity)
public class Test1Activity extends BaseActivity {Autowiredint age 10;protected void onCreate(Bundle savedInstanceState) {ARouter.getInstance().inject(this);}
}步骤3通过path发起路由跳转
// moduleB 没有依赖 moduleA
ARouter.getInstance().build(/test/activity).navigation();这样就使得 没有依赖moduleA的moduleB能跳转到moduleA的Activity了。 那么 ARouter 是如何做到只通过简单2步 就完成 解耦组件间的路由操作呢我们通过源码一步步理解。
2.2 架构解析
ARouter的源码架构
app:是ARouter提供的一个测试Demoarouter-annotation这个lib模块中声明了很多注解信息和一些枚举类arouter-apiARouter的核心api转换过程的核心操作都在这个模块里面arouter-compilerAPT处理器自动生成路由表的过程就是在这里面实现的arouter-gradle-plugin这是一个编译期使用的Plugin插件主要作用是用于编译器自动加载路由表节省应用的启动时间。
基础概念
1PostCard(明信片)ARouter就是使用PostCard这个类来存储寄件人和收件人信息的。
java
复制代码
public final class Postcard extends RouteMeta {// Baseprivate Uri uri; //如果使用Uri方式发起luyouprivate Object tag; // A tag prepare for some thing wrong. inner params, DO NOT USE!private Bundle mBundle; // 需要传递的参数使用bundle存储private int flags 0; // 启动Activity的标志如NEW_FALGprivate int timeout 300; // 路由超时private IProvider provider; // 使用IProvider的方式跳转private boolean greenChannel; //绿色通道可以不经过拦截器private SerializationService serializationService; //序列化服务serializationService需要传递Object自定义类型对象就需要实现这个服务private Context context; // May application or activity, check instance type before use it.private String action; //Activity跳转的Action// Animationprivate Bundle optionsCompat; // The transition animation of activityprivate int enterAnim -1;private int exitAnim -1;...
}PostCard继承了RouteMeta
java
复制代码
public class RouteMeta {private RouteType type; // 路由类型如ActivityFragmentProvider等private Element rawType; // 路由原始类型在编译时用来判断private Class? destination; // 目的Class对象private String path; // 路由注册的pathprivate String group; // 路由注册的group分组private int priority -1; // 路由执行优先级priority越低优先级越高这个一般在拦截器中使用private int extra; // Extra dataprivate MapString, Integer paramsType; // 参数类型例如activity中使用Autowired的参数类型private String name; //路由名字用于生成javadocprivate MapString, Autowired injectConfig; // 参数配置对应paramsType.}RouteMeta:主要存储的是一些目的对象的信息这些对象是在路由注册的时候才会生成。
2Interceptor拦截器 ARouter中所有的路由调用过程在到达目的地前都会先经过自定义的一系列拦截器实现一些AOP切面编程。
java
复制代码
public interface IInterceptor extends IProvider {/*** The operation of this interceptor.** param postcard meta* param callback cb*/void process(Postcard postcard, InterceptorCallback callback);
}IInterceptor是一个接口继承了IProvider所以其也是一个服务类型只需要实现process方法就可以实现拦截操作。
3greenChannel:绿色通道 设置了绿色通道的跳转过程可以不经过拦截器
4Warehouse路由仓库 Warehouse意为仓库用于存放被 Route、Interceptor标注的 路由相关的信息也就是我们关注的destination等信息它存着所有路由信息。
java
复制代码
class Warehouse {//所有IRouteGroup实现类的class对象是在ARouter初始化中赋值key是path第一级//IRouteGroup实现类是编译时生成代表一个组即path第一级相同的所有路由包括Activity和Provider服务static MapString, Class? extends IRouteGroup groupsIndex new HashMap(); //所有路由元信息是在completion中赋值key是path//首次进行某个路由时就会加载整个group的路由即IRouteGroup实现类中所有路由信息。包括Activity和Provider服务static MapString, RouteMeta routes new HashMap();//所有服务provider实例在completion中赋值key是IProvider实现类的classstatic MapClass, IProvider providers new HashMap();//所有provider服务的元信息(实现类的class对象)是在ARouter初始化中赋值key是IProvider实现类的全类名。//主要用于使用IProvider实现类的class发起的获取服务的路由例如ARouter.getInstance().navigation(HelloService.class)static MapString, RouteMeta providersIndex new HashMap();//所有拦截器实现类的class对象是在ARouter初始化时收集到key是优先级static MapInteger, Class? extends IInterceptor interceptorsIndex new UniqueKeyTreeMap(...);//所有拦截器实例是在ARouter初始化完成后立即创建static ListIInterceptor interceptors new ArrayList();
...
}2.3 源码分析
下面我们分别来分析使用过程 步骤1分析ARouter.init(this)
/*** 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.);}
}调用了_ARouter同名init方法
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;
}内部初始化了一些mContextmHandler以及字段信息 最重要的是LogisticsCenter.init(mContext, executor)进入看看
/*** LogisticsCenter init, load all metas in memory. Demand initialization*/
public synchronized static void init(Context context, ThreadPoolExecutor tpe) throws HandlerException {try {//使用AGP插件进行路由表的自动加载loadRouterMap();//如果registerByPlugin被设置为true说明使用的是插件加载直接跳过if (registerByPlugin) {logger.info(TAG, Load router map by arouter-auto-register plugin.);} else {//如果是false则调用下面步骤加载SetString routerMap;// 如果是debug模式或者是新版本的则每次都会去加载routerMap这会是一个耗时操作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();}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 HashSetString()));}//这里循环获取routerMap中的信息for (String className : routerMap) {//如果className com.alibaba.android.arouter.routes.ARouter$$Root格式则将路由组信息添加到Warehouse.groupsIndex中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);//如果className com.alibaba.android.arouter.routes.ARouter$$Interceptors格式则将拦截器信息添加到Warehouse.interceptorsIndex中} else if (className.startsWith(ROUTE_ROOT_PAKCAGE DOT SDK_NAME SEPARATOR SUFFIX_INTERCEPTORS)) {// Load interceptorMeta((IInterceptorGroup) (Class.forName(className).getConstructor().newInstance())).loadInto(Warehouse.interceptorsIndex);//如果className com.alibaba.android.arouter.routes.ARouter$$Providers格式则将服务Provider信息添加到Warehouse.providersIndex中} else if (className.startsWith(ROUTE_ROOT_PAKCAGE DOT SDK_NAME SEPARATOR SUFFIX_PROVIDERS)) {// Load providerIndex((IProviderGroup) (Class.forName(className).getConstructor().newInstance())).loadInto(Warehouse.providersIndex);}}}} catch (Exception e) {throw new HandlerException(TAG ARouter init logistics center exception! [ e.getMessage() ]);}
}ARouter的init操作总结 1.优先使用插件加载路由表信息到仓库中如果没有使用插件则使用包名com.alibaba.android.arouter.routes去dex文件中查找对应的类对象 查找到后保存到sp文件中非debug或者新版本的情况下下次就直接使用sp文件中缓存的类信息即可。 2.查找到对应的类文件后使用反射调用对应的类的loadInto方法将路由组拦截器以及服务Provider信息加载到Warehouse仓库中
步骤2分析注册Activity路由 我们注册的ActivityProvider等路由信息在编译期被注解处理器处理生成对应的路由表路由表在ARouter初始化的时候被加载到Warehouse中
步骤3分析通过path启动对应的Activity
ARouter.getInstance().build(/test/activity2).navigation();getInstance() 做了init检查并创建了一个ARouter对象
public static ARouter getInstance() {if (!hasInit) {throw new InitException(ARouter::Init::Invoke init(context) first!);} else {if (instance null) {synchronized (ARouter.class) {if (instance null) {instance new ARouter();}}}return instance;}
}build()这里创建了一个Postcard传入path和group
public Postcard build(String path) {return _ARouter.getInstance().build(path);
}
调用了_ARouter的同名build方法
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);}return build(path, extractGroup(path), true);}
}
1.使用PathReplaceService可以替换原path为新的path
继续看build方法
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()执行_ARouter中的同名navigation方法内容如下 1.预处理服务 2.完善PostCard信息 3.如果是非绿色通道则使用拦截器处理请求 4.调用_navigation()处理
直接看 _navigation() 方法
private Object _navigation(final Postcard postcard, final int requestCode, final NavigationCallback callback) {final Context currentContext postcard.getContext();switch (postcard.getType()) {case ACTIVITY:// Build intentfinal 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_TASKif (!(currentContext instanceof Activity)) {intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);}// Set ActionsString action postcard.getAction();if (!TextUtils.isEmpty(action)) {intent.setAction(action);}// Navigation in main looper.runInMainThread(new Runnable() {Overridepublic 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的type来处理不同的请求了
1.Activity直接跳转2.FragmentProviderBroadcaseReceiver和ContentProvider直接返回类的实例对象。
inject简介 通过Autowired注解的属性通过调用ARouter.getInstance().inject(this);可以实现自动注入。我们知道原生的Activity传递数据是通过Bundle携带的。因此ARouter的数据传递肯定也是基于Bundle并实现了自动赋值的功能。
//生成类的类名 目标类名 $$ARouter$$Autowired(固定后缀)
public class BlankFragment$$ARouter$$Autowired implements ISyringe {private SerializationService serializationService;Overridepublic void inject(Object target) {serializationService ARouter.getInstance().navigation(SerializationService.class);//强转成目标类BlankFragment substitute (BlankFragment)target;//给每个注解了Autowired的属性赋值//这里的key为属性名或者注解中给定的namesubstitute.name substitute.getArguments().getString(name);// 如果存在Bundle无法携带的类型则会通过SerializationService序列化成json的String传递SerializationService没有提供默认实现需要用户自己实现if (null ! serializationService) {substitute.obj serializationService.parseObject(substitute.getArguments().getString(obj), new com.alibaba.android.arouter.facade.model.TypeWrapperTestObj(){}.getType());} else {Log.e(ARouter::, You want automatic inject the field obj in class BlankFragment , then you should implement SerializationService to support object auto inject!);}if (null substitute.obj) {Log.e(ARouter::, The field obj is null, in class BlankFragment.class.getName() !);}}
}前面分析navigation的时候我们看到了将Uri参数存入Bundle的过程或者由用户手动调用withXX存入bundle此处则是将Bundle中的数据取出并赋值给Autowired注解的属性。
Arouter流程图简介
三、Arouter常见面试问题
3.1 使用方法
在Android开发中使用ARouter进行应用内跳转非常简单。首先需要在要跳转的Activity上添加Route注解。其次在应用启动时初始化ARouter通常是在Application的onCreate方法中完成。初始化时可以根据需要开启日志和调试模式。最后通过ARouter的getInstance().build(path).navigation()方法进行跳转其中path是目标Activity的路由地址1。
3.2 简述原理
ARouter的原理主要包括两个步骤注册子模块信息到路由表和寻址操作。
注册子模块信息到路由表这一步并不是手动完成的而是采用编译器APT技术在编译的时候扫描自定义注解通过注解获取子模块信息并自动注册到路由表中去。寻址操作在需要跳转的时候ARouter会根据提供的路由地址在路由表中寻找到对应的子模块信息并完成交互实现跳转。
3.3、ARouter 通信示意图 3.4、ARouter 的底层实现原理
ARouter的底层实现原理主要包括通过编译时注解和注解处理器APT生成路由表记录页面与URL的映射关系应用启动时通过反射加载路由表到内存当需要路由跳转时根据URL在路由表中查找对应的页面信息创建Intent并设置参数最后通过startActivity实现页面跳转。这种方式实现了模块间的解耦使得不同模块间可以相互独立、互不依赖地进行通信和跳转。