apk生成器软件(生成apk的软件)
本文作者
作者: xiangcman
昨晚加班睡得较晚,早上补觉中,有问题可以留言,睡醒了放出回复。
1
概述
arouter是android实现组件化的路由框架,涉及到的功能有activity、fragment的跳转、跳转带参数、自定义服务、自定义拦截器、拦截下沉、重定向url都是Arouter里面定义的功能,可能用过Arouter的小伙伴们只用过Arouter的跳转以及跳转功能带参数的功能,像它的自定义服务、拦截器、全局降级策略、重定向功能都是很不错的功能,下面我会一一介绍这些功能该怎么使用。
arouter是android实现组件化的路由框架,涉及到的功能有activity、fragment的跳转、跳转带参数、自定义服务、自定义拦截器、拦截下沉、重定向url都是Arouter里面定义的功能,可能用过Arouter的小伙伴们只用过Arouter的跳转以及跳转功能带参数的功能,像它的自定义服务、拦截器、全局降级策略、重定向功能都是很不错的功能,下面我会一一介绍这些功能该怎么使用。
目录
基础依赖
初始化
添加注解
发起路由
添加混淆
使用Gradle实现路由表自动加载
使用IDE插件通过导航的形式到目标类
基础依赖
初始化
添加注解
发起路由
添加混淆
使用Gradle实现路由表自动加载
使用IDE插件通过导航的形式到目标类
展开全文
2
使用介绍
1.基础依赖
1.1.java版本的依赖
在需要使用Arouter的module中添加如下代码:
android {
defaultConfig {
...
javaCompileOptions {
annotationProcessorOptions {
//arouter编译的时候需要的 module名字
arguments = [ AROUTER_MODULE_NAME:project.getName]
}
}
}
}
dependencies {
...
implementation 'com.alibaba:arouter-api:1.5.1'
annotationProcessor 'com.alibaba:arouter-compiler:1.5.1'
}
这里一般习惯的做法是把arouter-api的依赖放在基础服务的module里面,因为既然用到了组件化,那么肯定是所有的module都需要依赖arouter-api库的,而arouter-compiler的依赖需要放到每一个module里面。
1.2.kotlin版本的依赖
plugins {
...
id 'kotlin-kapt'
}
dependencies {
...
implementation 'com.alibaba:arouter-api:1.5.1'
kapt 'com.alibaba:arouter-compiler:1.5.1'
}
注意上面定义plugin的写法是新的androidStudio的写法了,其实kotlin的写法与java的写法就是在编译时注解的依赖形式不一样,其余的都是一样的。
2.初始化
这个很简单,在Application中初始化就可以了:
if(isDebug) { // 这两行必须写在init之前,否则这些配置在init过程中将无效
ARouter.openLog; // 打印日志
ARouter.openDebug; // 开启调试模式(如果在InstantRun模式下运行,必须开启调试模式!线上版本需要关闭,否则有安全风险)
}
ARouter.init(mApplication); // 尽可能早,推荐在Application中初始化
这里简单提一句,当时我们项目为了做Arouter启动优化,Arouter的初始化是花了4秒多,结果把Arouter的初始化放到了欢迎页,结果在线上版本发现了有坑,在进程被kill的情况下,回到当前Activity的时候,会发现Arouter的初始化标记为false,所以思来想去,Arouter的初始化工作还是得放到Application。
因为即使进程被kill掉了,Application还是会初始化Arouter的,所以初始化工作还是得放在Application中,不过官方实现了Gradle插件路由的自动加载功能,后面会说到。
3.添加注解
3.1@Route注解
Route注解是作用在类上面,里面会携带path路径,这里列举Route注解使用的几种情况:
Route注解添加Activity的路由
@Route(path = "/login/loginActivity")
classLoginActivity: AppCompatActivity{
...
}
Route注解添加全局序列化方式
@Route(path = "/yourservicegroupname/json")
classJsonServiceImpl: SerializationService {
lateinitvargson: Gson
overridefun<T : Any?>json2Object(input: String?, clazz: Class< T>?) : T {
returngson.fromJson(input, clazz)
}
overridefuninit(context: Context?) {
gson = Gson
}
overridefunobject2Json(instance: Any?) : String {
returngson.toJson(instance)
}
overridefun<T : Any?>parseObject(input: String?, clazz: Type?) : T {
returngson.fromJson(input, clazz)
}
}
这里用了Route注解定义了SerializationService的序列化的方式,在使用withObject的时候会使用该SerializationService,后面会讲到该情况。
Route注解定义了全局降级策略
@Route(path = "/yourservicegroupname/DegradeServiceImpl")
classDegradeServiceImpl: DegradeService {
overridefunonLost(context: Context, postcard: Postcard) {
Log.d( "DegradeServiceImpl", "没有找到该路由地址: ${postcard.path}" )
}
overridefuninit(context: Context?) {
}
}
上面也是用了Route注解定义了全局降级策略,也就是在找不到的路由表的时候,做相应的处理。
Route注解实现提服务
interfaceHelloService: IProvider{
funsayHello(name: String) :String
}
// 实现接口
@Route(path = "/common/hello", name = "测试服务")
classHelloServiceImpl: HelloService {
overridefunsayHello(name: String) : String {
Log.d( "HelloServiceImpl", "hello, $name" )
return"hello, $name"
}
overridefuninit(context: Context) {}
}
这个例子是官网的写法,意思是通过Route注解实现提供服务,那怎么实现接收服务呢,下面会在另外一种注解的时候讲到。
3.2@Interceptor注解
这个注解可以说非常强大,它能拦截你的路由,什么时候让路由通过什么时候让路由不通过,完全靠该Interceptor注解可以控制。比如我有一个需求,在跳分享的时候,我想看有没有登录,如果没有登录做登录的操作,如果登了了才让分享。如果之前是不是在每一个路由的地方都得判断有没有登录,很繁琐,有了路由拦截器不用在跳转的地方判断。
@Interceptor(priority = 8, name = "登录拦截")
classLoginInterceptor: IInterceptor {
overridefunprocess(postcard: Postcard, callback: InterceptorCallback) {
valpath = postcard.path
if(path == "/share/shareActivity") {
valuserInfo = DataSource.getInstance(ArouterApplication.application).getUserInfo
if(TextUtils.isEmpty(userInfo)) {
callback.onInterrupt(Throwable( "还没有登录,去登陆"))
} else{
callback.onContinue(postcard)
}
} else{
callback.onContinue(postcard)
}
}
overridefuninit(context: Context?) {
// 拦截器的初始化,会在sdk初始化的时候调用该方法,仅会调用一次
Log.d( "LoginInterceptor", "LoginInterceptor初始化了")
}
}
拦截器可以定义优先级,如果有多个拦截器,会依次执行拦截器。
3.3@Autowired注解
Autowired注解是定义在目标页的属性上,通常用来定义目标页接收的值,还可以定义上面说到的接收服务方:
3.3.1Autowired注解接收值
@Autowired(name = "username")
lateinitvarusername: String
@Autowired
lateinitvartestBean: TestBean
@Autowired(name = "listBean")
lateinitvarlistBean: List<TestBean>
上面定义基本类型的值接收,还有自定义bean和集合的接收。
3.3.2Autowired注解接收服务
@Autowired
lateinitvarhelloService: HelloService
@Autowired(name = "/common/hello")
lateinitvarhelloService1: HelloService
lateinitvarhelloService2: HelloService
lateinitvarhelloService3: HelloService
可以看到,上面定义了前面提供的服务,helloService可以直接指向HelloServiceImpl,如果HelloService有多个服务,那Autowired注解需要指定name路由属性,指明是哪一个服务的实例。有人好奇helloService2和helloService3没有用@Autowired注解定义服务的来源,别急,下面会提供服务的来源的:
helloService2 =
ARouter.getInstance.build( "/common/hello").navigation as HelloService
helloService3 = ARouter.getInstance.navigation(HelloService::class.java)
//使用服务
helloService.sayHello( "helloService")
helloService1.sayHello( "helloService1")
helloService2.sayHello( "helloService2")
helloService3.sayHello( "helloService3")
helloService2是通过build指定路由地址的形式,helloService3是通过navigation指定HelloService的class类也能拿到HelloServiceImpl的服务。
上面的@Autowired注解使用都得在类的初始化中使用ARouter.getInstance.inject(this),否则@Autowired注解不会被执行到
3.3预处理服务
预处理服务意思是在路由navigation之前进行干扰路由,通过实现PretreatmentService接口,比如我想干扰在分享之前判断有没有登录,如果没有登录,自行判断逻辑:
@Route(path = "/yourservicegroupname/pretreatmentService")
classPretreatmentServiceImpl: PretreatmentService {
overridefunonPretreatment(context: Context, postcard: Postcard) : Boolean{
if(postcard.path == "/share/ShareActivity") {
valuserInfo = DataSource.getInstance(ArouterApplication.application).getUserInfo
if(TextUtils.isEmpty(userInfo)) {
Toast.makeText(ArouterApplication.application, "你还没有登录", Toast.LENGTH_SHORT).show
returnfalse// 跳转前预处理,如果需要自行处理跳转,该方法返回 false 即可
}
}
returntrue
}
overridefuninit(context: Context) {}
}
其实在这个例子中我演示的拦截器功能和预处理服务功能是一样的,只不过预处理服务是早于拦截器的,等到分析源码的时候我们分析他们的具体区别。
3.4重定义URL跳转
重新定以URL的跳转
// 实现PathReplaceService接口,并加上一个Path内容任意的注解即可
@Route(path = "/yourservicegroupname/pathReplaceService") // 必须标明注解
classPathReplaceServiceImpl: PathReplaceService {
/**
* For normal path.
*
* @parampath raw path
*/
overridefunforString(path: String) : String {
if(path == "/login/loginActivity") {
return"/share/shareActivity"
}
returnpath // 按照一定的规则处理之后返回处理后的结果
}
/**
* For uri type.
*
* @paramuri raw uri
*/
overridefunforUri(uri: Uri?) : Uri? {
returnnull// 按照一定的规则处理之后返回处理后的结果
}
overridefuninit(context: Context?) {
}
}
上面我把登录的路由改成分享的路由,在实际项目中大家看看有什么适用的场景?
4.发起路由
我们先来个最简单的方式:
ARouter.getInstance.build( "/test/activity").navigation
主要是通build方法生成postCard对象,最后调用postCard的navigation方法。
传值写法:
ARouter.getInstance.build( "/test/1")
.withLong( "key1", 666L)
.withString( "key3", "888")
.withObject( "key4", newTest( "Jack", "Rose"))
.navigation
上面能用withObject方法传object是因为在上面定义了JsonServiceImpl序列化方式的路由类。withObejct还可以传集合、map等:
ARouter.getInstance.build( "/share/shareActivity").withString( "username", "zhangsan")
.withObject( "testBean", TestBean( "lisi", 20))
.withObject(
"listBean",
listOf<TestBean>(TestBean( "wanger", 20), TestBean( "xiaoming", 20))
)
.navigation
这里注意了在路由目标类里面定义接收list、map的时候,接收对象的地方不能标注具体的实现类类型,应仅标注为list或map,否则会影响序列化中类型的判断,其他类似情况需要同样处理其他几种序列化的方式也带了,大家自行查看postCard的with** 相关方法:
跳转写法:跳转方法主要指navigation方法,其实说是跳转方法不太准确,因为它不仅仅是跳转用的,比如生成一个interceptor、service等都是通过navigation方法实现的,下一节介绍源码的时候会说到navigation有哪些具体作用
navigation主要有下面几个方法,我们说下NavigationCallback对象,一看就是个回调:
ARouter.getInstance.build( "/share/shareActivity").withString( "username", "zhangsan")
.withObject( "testBean", TestBean( "lisi", 20))
.withObject(
"listBean",
listOf<TestBean>(TestBean( "wanger", 20), TestBean( "xiaoming", 20))
)
.navigation( this, object: NavigationCallback {
overridefunonLost(postcard: Postcard?) {
}
overridefunonFound(postcard: Postcard?) {
}
overridefunonInterrupt(postcard: Postcard?) {
Log.d( "LoginActivity", "还没有登录")
}
overridefunonArrival(postcard: Postcard?) {
}
})
实现了四个方法,onLost是找不到路由,onFound是找到路由,onInterrupt表示路由挂了,默认路由设置的超时时间是300s,onArrival表示路由跳转成功的回调,目前只在startActivity的回调,这个后面源码部分会讲到。
5.混淆
混淆部分就没什么好说的了,因为Arouter是通过反射创建arouter的注解类,所以大部分需要加混淆:
-keep publicclasscom. alibaba. android. arouter. routes.** {*;}
-keep publicclasscom. alibaba. android. arouter. facade.** {*;}
-keep class* implementscom. alibaba. android. arouter. facade. template. ISyringe{*;}
# 如果使用了 byType 的方式获取 Service,需添加下面规则,保护接口
-keep interface* implementscom. alibaba. android. arouter. facade. template. IProvider
# 如果使用了 单类注入,即不定义接口实现 IProvider,需添加下面规则,保护实现
# - keepclass* implementscom. alibaba. android. arouter. facade. template. IProvider
6.使用Gradle实现路由表自动加载
可以说这个功能虽然是选项配置,但是对于arouter启动优化有很大的作用,我们项目在没使用这个gradle自动加载路由插件的时候初始化sdk需要4秒多,用了这个插件之后基本没消耗时间。
它主要是在编译期通过gradle插装把需要依赖arouter注解的类自动扫描到arouter的map管理器里面,在下一章我们通过反编译工具查看它是怎么插装代码的,而传统的是通过扫描dex文件来过滤arouter注解类来添加到map中。
具体使用
//app的module的build.gradle
apply plugin: 'com.alibaba.arouter'
//工程的build.gradle
build {
repositories {
jcenter
}
dependencies {
classpath "com.alibaba:arouter-register:1.0.2"
}
}
7.使用IDE插件通过导航的形式到目标类
在 Android Studio 插件市场中搜索 ARouter Helper, 或者直接下载文档上方 最新版本 中列出的 arouter-idea-plugin zip 安装包手动安装,安装后 插件无任何设置,可以在跳转代码的行首找到一个图标 (navigation) 点击该图标,即可跳转到标识了代码中路径的目标类目前不支持kotlin的图标样式,大家自己尝试下java跳转。
示例代码
https://github.com/xiangcman/ArouterApp
更多文档请走这里arouter官网:
https://github.com/alibaba/ARouter
3
ARouter 源码分析
这节主要围绕Arouter源码的设计,以及通过源码我们能学习到什么,以及如何应对面试过程中Arouter的问题。
目的
源码分析
Arouter路由跳转的设计
Arouter拦截器的设计
Arouter的服务怎么设计的
Arouter的注解属性怎么获取的
Arouter自动加载路由表
思考源码的设计
源码分析
在使用Arouter过程中,我们的module依赖了arouter-api、arouter-compiler,在编译期arouter-compiler通过扫描项目中用到的Route、Autowired、Interceptor等注解来生成对应的class文件,大家如果想学习学习编译期扫描注解生成class文件可以学习apt相关的技术,或者看Arouter官网的arouter-compiler模块怎么扫描注解生成class文件的。
在使用Arouter过程中,我们的module依赖了arouter-api、arouter-compiler,在编译期arouter-compiler通过扫描项目中用到的Route、Autowired、Interceptor等注解来生成对应的class文件,大家如果想学习学习编译期扫描注解生成class文件可以学习apt相关的技术,或者看Arouter官网的arouter-compiler模块怎么扫描注解生成class文件的。
比如我在login模块的LoginActivity中定义如下的注解:
@Route(path = "/login/loginActivity")
classLoginActivity: AppCompatActivity{
}
结果在module的build目录下生成了ARouter$$Root$$loginclass文件,它是实现IRouteRoot接口:
public classARouter$$ Root$$ loginimplementsIRouteRoot{
@Override
public voidloadInto( Map< String, Class<? extendsIRouteGroup>> routes) {
routes.put( "login", ARouter$$Group$$login. class);
}
}
而此处的ARouter$$Group$$login类它是实现了IRouteGroup接口:
publicclassARouter$$ Group$$ loginimplementsIRouteGroup{
@Override
publicvoidloadInto(Map<String, RouteMeta> atlas){
atlas.put( "/login/loginActivity", RouteMeta.build(RouteType.ACTIVITY, LoginActivity.class, "/login/loginactivity", "login", null, - 1, - 2147483648));
}
}
里面把loginActivity的信息通过RouteMeta存到了传进来的map中了。其实我们的ARouter$$Group$$组名类不只是存放了activity的RouteMeta信息,还会有IProvider类型。
如果用Interceptor注解的话,会生成对应的ARouter$$Interceptors$$模块名的class类,我们的JsonServiceImpl它最终是一个IProvider接口,还有DegradeServiceImpl类也是一样的,都会在ARouter$$Interceptors$$yourservicegroupname类中保存了一个RouteMeta信息,而定义的LoginInterceptor最终是被ARouter$$Providers$$app管理的:
publicclassARouter$$ Interceptors$$ appimplementsIInterceptorGroup{
@Override
publicvoidloadInto(Map<Integer, Class<? extends IInterceptor>> interceptors){
interceptors.put( 8, LoginInterceptor.class);
}
}
IInterceptor就没有被包装成RouteMeta对象,上面在ARouter$$Group$$yourservicegroupname中定义的IProvider信息还会在ARouter$$Providers$$app被定义了一遍:
public classARouter$$ Providers$$ appimplementsIProviderGroup{
@Override
public voidloadInto( Map< String, RouteMeta> providers) {
providers.put( "com.alibaba.android.arouter.facade.service.DegradeService", RouteMeta.build(RouteType.PROVIDER, DegradeServiceImpl. class, "/yourservicegroupname/DegradeServiceImpl", "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.facade.service.PathReplaceService", RouteMeta.build(RouteType.PROVIDER, PathReplaceServiceImpl. class, "/yourservicegroupname/pathReplaceService", "yourservicegroupname", null, -1, -2147483648));
providers.put( "com.alibaba.android.arouter.facade.service.PretreatmentService", RouteMeta.build(RouteType.PROVIDER, PretreatmentServiceImpl. class, "/yourservicegroupname/pretreatmentService", "yourservicegroupname", null, -1, -2147483648));
}
}
其实都是我们在app模块中定义的IProvider类型的service,那为什么在ARouter$$Group$$组名定义了IProvider类型,那为什么还需要在ARouter$$Providers$$模块名中还要定义一遍呢,看官莫急,听我细细道来。
小节
所以在apt阶段,生成的class类有Arouter$$Root$$模块名,模块名是在gradle中配置的arg("AROUTER_MODULE_NAME", "${project.getName}")属性,把所有组的信息放到传进来的map中,这个组是通过我们在Route注解的path属性拆分的,比如定义/login/loginActivity,会认为组名是login,Arouter$$Root$$组名放的是该组下,所有的路由表信息,包括route、provider注解通过RouteMeta包装对应的class类信息,provider注解会放在Arouter$$Providers$$模块名下面。
所以在apt阶段,生成的class类有Arouter$$Root$$模块名,模块名是在gradle中配置的arg("AROUTER_MODULE_NAME", "${project.getName}")属性,把所有组的信息放到传进来的map中,这个组是通过我们在Route注解的path属性拆分的,比如定义/login/loginActivity,会认为组名是login,Arouter$$Root$$组名放的是该组下,所有的路由表信息,包括route、provider注解通过RouteMeta包装对应的class类信息,provider注解会放在Arouter$$Providers$$模块名下面。
初始化
我们在上一面使用部分,初始化部分代码如下:
ARouter.init( this)
就一句,那下面追随源码看下:
publicstaticvoidinit(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方法以及_ARouter.afterInit方法,其实我们的入口虽然是ARouter类的,但是真正调用的还是_ARouter类的方法,废话不多说,直接看_ARouter.init方法:
_ARouter.init
protectedstaticsynchronizedbooleaninit(Application application){
mContext = application;
LogisticsCenter.init(mContext, executor);
logger.info(Consts.TAG, "ARouter init success!");
hasInit = true;
mHandler = newHandler(Looper.getMainLooper);
returntrue;
}
最终还是调用了LogisticsCenter的init方法,并且拿到主线的looper给了mHandler,顺着看LogisticsCenter的init方法:
public synchronized static void init(Context context, ThreadPoolExecutor tpe) throws HandlerException {
mContext = context;
executor = tpe;
try{
//如果通过插件获取路由表信息,则该方法registerByPlugin= false
loadRouterMap;
if(registerByPlugin) {
} else{
Set<String> routerMap;
//如果是debuggable= true或者有新的版本
if(ARouter.debuggable || PackageUtils.isNewVersion(context)) {
logger.info(TAG, "Run with debug mode or new install, rebuild router map.");
//加载前缀为com.alibaba.android.arouter.routes的 class类,放到 set集合里面
routerMap = ClassUtils.getFileNameByPackageName(mContext, ROUTE_ROOT_PAKCAGE);
//将过滤到的 class类放到 sp中,方便下次直接取
if(!routerMap.isEmpty) {
context.getSharedPreferences(AROUTER_SP_CACHE_KEY, Context.MODE_PRIVATE).edit.putStringSet(AROUTER_SP_KEY_MAP, routerMap).apply;
}
//更新下最新的版本
PackageUtils.updateVersion(context); //Save newversion name whenrouter map update finishes.
} else{
//直接从sp中取过滤到的 class类
routerMap = newHashSet<>(context.getSharedPreferences(AROUTER_SP_CACHE_KEY, Context.MODE_PRIVATE).getStringSet(AROUTER_SP_KEY_MAP, newHashSet<String>));
}
for(String className : routerMap) {
if(className.startsWith(ROUTE_ROOT_PAKCAGE + DOT + SDK_NAME + SEPARATOR + SUFFIX_ROOT)) {
//将com.alibaba.android.arouter.routes.ARouter$$Root前缀的 class类放到 Warehouse. groupsIndex中
((IRouteRoot) (Class.forName(className).getConstructor.newInstance)).loadInto(Warehouse.groupsIndex);
} elseif(className.startsWith(ROUTE_ROOT_PAKCAGE + DOT + SDK_NAME + SEPARATOR + SUFFIX_INTERCEPTORS)) {
//将com.alibaba.android.arouter.routes.ARouter$$Interceptors前缀的 class类放到 Warehouse. interceptorsIndex中
((IInterceptorGroup) (Class.forName(className).getConstructor.newInstance)).loadInto(Warehouse.interceptorsIndex);
} elseif(className.startsWith(ROUTE_ROOT_PAKCAGE + DOT + SDK_NAME + SEPARATOR + SUFFIX_PROVIDERS)) {
//将com.alibaba.android.arouter.routes.ARouter$$Providers前缀的 class类放到 Warehouse. providersIndex中
((IProviderGroup) (Class.forName(className).getConstructor.newInstance)).loadInto(Warehouse.providersIndex);
}
}
}
} catch(Exception e) {
thrownewHandlerException(TAG + "ARouter init logistics center exception! ["+ e.getMessage + "]");
}
}
在上面init方法中做了几件事:
通过loadRouterMap方法判断是不是通过arouter-register自动加载路由表,如果是通过自动加载的则registerByPlugin=true,这里我们先不管通过arouter-register自动加载的方式,
紧接着通过ClassUtils.getFileNameByPackageName(此处用到了线程池、CountDownLatch面试高频考点)获取到apk中前缀为com.alibaba.android.arouter.routes的类,这里面主要是通过判断是不是支持MultiDex,如果不支持MultiDex,扫描所有的dex文件,然后压缩成zip文件,然后通过DexFile.loadDex转化成DexFile对象,如果支持MultiDex,直接new DexFile,然后循环DexFile拿里面的class文件,然后过滤出com.alibaba.android.arouter.routes前缀的class并返回。
拿到了需要的class类后,放到sp里面,方便下次不去扫描apk拿class,更新版本号
将com.alibaba.android.arouter.routes.ARouter$$Root前缀的class类放到Warehouse.groupsIndex中
将com.alibaba.android.arouter.routes.ARouter$$Interceptors前缀的class类放到Warehouse.interceptorsIndex中
将com.alibaba.android.arouter.routes.ARouter$$Providers前缀的class类放到Warehouse.providersIndex中
_ARouter.afterInit
该方法里面会去拿InterceptorServiceImpl,怎么去拿的,以及里面做了些啥,我们一一来看:
interceptorService= (InterceptorService) ARouter.getInstance.build( "/arouter/service/interceptor").navigation;
ARouter.build
ARouter.getInstance.build("/arouter/service/interceptor")会返回PostCard对象,并给PostCard的group和path属性赋值为arouter、/arouter/service/interceptor:
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);
}
returnbuild( path, extractGroup( path), true);
}
}
先判断有没有设置PathReplaceService类型的路由,如果有会调用forString方法返回新的path,这也就是我们的path替换的IProvider,在第一篇文章我们定义过PathReplaceServiceImpl类:
@Route(path = "/yourservicegroupname/pathReplaceService") // 必须标明注解
classPathReplaceServiceImpl: PathReplaceService {
overridefunforString(path: String) : String {
if(path == "/login/loginActivity") {
return"/share/shareActivity"
}
returnpath // 按照一定的规则处理之后返回处理后的结果
}
overridefunforUri(uri: Uri?) : Uri? {
returnnull// 按照一定的规则处理之后返回处理后的结果
}
overridefuninit(context: Context?) {
}
}
如果有该PathReplaceService会替换掉我们的/login/loginActivity为/share/shareActivity,这里先不管怎么拿到PathReplaceServiceImpl,通过extractGroup方法拿到group:
private StringextractGroup( Stringpath) {
if(TextUtils.isEmpty(path) || !path.startsWith( "/")) {
thrownewHandlerException(Consts.TAG + "Extract the default group failed, the path must be start with '/' and contain more than 2 '/'!");
}
try{
StringdefaultGroup = path.substring( 1, path.indexOf( "/", 1));
if(TextUtils.isEmpty(defaultGroup)) {
thrownewHandlerException(Consts.TAG + "Extract the default group failed! There's nothing between 2 '/'!");
} else{
returndefaultGroup;
}
} catch(Exception e) {
logger.warning(Consts.TAG, "Failed to extract default group! "+ e.getMessage);
returnnull;
}
}
很简单,按照第一个'/'和第二个'/'的字符作为group信息,所以这也是为什么我们在定义path的时候需要两级的path目录。最终会走另一个重载的build方法:
protectedPostcard build( String path, String group, Boolean afterReplace ) {
if(TextUtils.isEmpty(path) || TextUtils.isEmpty( group)) {
thrownewHandlerException(Consts.TAG + "Parameter is invalid!");
} else{
if(!afterReplace) {
PathReplaceService pService = ARouter.getInstance.navigation(PathReplaceService.class);
if( null!= pService) {
path = pService.forString(path);
}
}
returnnewPostcard(path, group);
}
}
所以build过程把传过来的path构造出Postcard对象,给path和group赋值。
PostCard.navigation
navigation有很多重载的方法,最终都会走_Arouter.navigation,其中navigation里面也有两种形式获取到路由表类,我们先介绍activity常规的形式:
protectedObject navigation( finalContext context, finalPostcard postcard, finalint requestCode, finalNavigationCallback callback) {
//这也是个路由表,通过另外一种形式获取PretreatmentService的实例
PretreatmentService pretreatmentService = ARouter.getInstance.navigation(PretreatmentService. class);
//如果onPretreatment返回false就是自己处理路由逻辑,不往下走了
if( null!= pretreatmentService && !pretreatmentService.onPretreatment(context, postcard)) {
returnnull;
}
try{
//最终会走这里
LogisticsCenter.completion(postcard);
} catch(NoRouteFoundException ex) {
logger.warning(Consts.TAG, ex.getMessage);
...
if( null!= callback) {
callback.onLost(postcard);
} else{
// 获取DegradeService的实例
DegradeService degradeService = ARouter.getInstance.navigation(DegradeService. class);
if( null!= degradeService) {
degradeService.onLost(context, postcard);
}
}
returnnull;
}
//省略provider部分逻辑和_navigation部分代码
returnnull;
}
由于navigation代码比较长,我把代码分块来说,上面首先获取PretreatmentService类型的路由表,我们先只说上面传入/arouter/service/interceptor,怎么得到InterceptorService实例的,我们直接看LogisticsCenter.completion:
public synchronized staticvoidcompletion(Postcard postcard) {
//第一次进来是拿不到RouteMeta信息的,因为routes是空的
RouteMeta routeMeta = Warehouse.routes. get(postcard.getPath);
if( null== routeMeta) {
//我们传过来的postcard的group是arouter、path是/arouter/service/interceptor
//我们在groupIndex中找对应的groupMeta,其实看到这的时候,我们默认是没有root为arouter的组,只能去arouter默认提供的root中找
Class<? extendsIRouteGroup> groupMeta = Warehouse.groupsIndex. get(postcard.getGroup); // Load route meta.
if( null== groupMeta) {
} else{
try{
//反射拿到ARouter$$Group$$arouter
IRouteGroup iGroupInstance = groupMeta.getConstructor.newInstance;
//所以最终把InterceptorServiceImpl放到了Warehouse.routes中
iGroupInstance.loadInto(Warehouse.routes);
//用完groupsIndex对应的IRouteGroup信息后,从map中移除掉,下次就直接从routes中去拿了
Warehouse.groupsIndex.remove(postcard.getGroup);
} catch(Exception e) {
thrownewHandlerException(TAG + "Fatal exception when loading group meta. ["+ e.getMessage + "]");
}
//继续走一遍completion,下次会走下面的else
completion(postcard);
}
} else{
//对postCard属性赋值
postcard.setDestination(routeMeta.getDestination);
postcard.setType(routeMeta.getType);
postcard.setPriority(routeMeta.getPriority);
postcard.setExtra(routeMeta.getExtra);
UrirawUri = postcard.getUri;
//默认uri为空
if( null!= rawUri) {
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));
}
// Save params name which need auto inject.
postcard.getExtras.putStringArray(ARouter.AUTO_INJECT, paramsType.keySet.toArray( newString[]{}));
}
// Save raw uri
postcard.withString(ARouter.RAW_URI, rawUri.toString);
}
switch(routeMeta.getType) {
//由于InterceptorServiceImpl是provider类型的
casePROVIDER:
Class<? extendsIProvider> providerMeta = (Class<? extendsIProvider>) routeMeta.getDestination;
//拿对应的provider
IProvider instance = Warehouse.providers. get(providerMeta);
if( null== instance) {
IProvider provider;
try{
//反射创建InterceptorServiceImpl
provider = providerMeta.getConstructor.newInstance;
//调用InterceptorServiceImpl的init方法
provider.init(mContext);
Warehouse.providers.put(providerMeta, provider);
instance = provider;
} catch(Exception e) {
thrownewHandlerException( "Init provider failed! "+ e.getMessage);
}
}
//给postcard赋值
postcard.setProvider(instance);
postcard.greenChannel;
break;
caseFRAGMENT:
postcard.greenChannel;
default:
break;
}
}
}
上面代码还是很清晰的,首先从Warehouse.routes去拿对应的RouteMeta信息,如果没有,就先去Warehouse.groupsIndex中拿,而此时的postCard的group是arouter,我们从下面图看下,拿到的是ARouter$$Group$$arouter的class:
所以此时的iGroupInstance是ARouter$$Group$$arouter,通过反射创建ARouter$$Group$$arouter,紧接着把Warehouse.routes传进它的loadInto方法:
所以我们最终能确定把AutowiredServiceImpl和InterceptorServiceImpl的RouteMeta放进了Warehouse.routes的map中。
注意:上面用完了Warehouse.groupsIndex中对应的group信息后,会从Warehouse.groupsIndex中移除该group的信息。
最后又走一遍completion方法,所以会走else分支,由于我们还在获取InterceptorServiceImpl过程中,它的RouteType=PROVIDER,所以providerMeta是InterceptorServiceImpl类型的,然后去Warehouse.providers拿,此时是空的,所以通过反射创建InterceptorServiceImpl对象,创建完调用InterceptorServiceImpl调用init方法:
@Override
publicvoidinit( finalContext context) {
LogisticsCenter.executor.execute( newRunnable {
@Override
publicvoidrun{
if(MapUtils.isNotEmpty(Warehouse.interceptorsIndex)) {
//遍历我们自己代码里面定义的interceptorsIndex
for(Map.Entry<Integer, Class<? extends IInterceptor>> entry : Warehouse.interceptorsIndex.entrySet) {
Class<? extends IInterceptor> interceptorClass = entry.getValue;
try{
//拿到对应的iInterceptor后
IInterceptor iInterceptor = interceptorClass.getConstructor.newInstance;
//调用init
iInterceptor.init(context);
//把iInterceptor放到interceptors中
Warehouse.interceptors.add(iInterceptor);
} catch(Exception ex) {
thrownewHandlerException(TAG + "ARouter init interceptor error! name = ["+ interceptorClass.getName + "], reason = ["+ ex.getMessage + "]");
}
}
interceptorHasInit = true;
logger.info(TAG, "ARouter interceptors init over.");
synchronized(interceptorInitLock) {
interceptorInitLock.notifyAll;
}
}
}
});
}
上面获取interceptorsIndex,还记得上一节我们定义的LoginInterceptor吗?它会放到interceptorsIndex的map里面,所以这里是拿到所有的IInterceptor,反射创建每一个IInterceptor,调用init方法,添加到Warehouse.interceptors中。
到这里,我们已经清楚了Warehouse.routes存放了AutowiredServiceImpl和InterceptorServiceImpl类型的RouteMeta,Warehouse.providers存放了InterceptorServiceImpl,Warehouse.interceptors存放了所有的IInterceptor。
到这里,我们已经清楚了Warehouse.routes存放了AutowiredServiceImpl和InterceptorServiceImpl类型的RouteMeta,Warehouse.providers存放了InterceptorServiceImpl,Warehouse.interceptors存放了所有的IInterceptor。
创建完InterceptorServiceImpl,我们一下子要回到_ARouter的navigation方法的下半部分代码,上面没有贴出代码:
//如果是`InterceptorServiceImpl`类型的postcard.isGreenChannel是true,除非是activity或fragment类型的
if(!postcard.isGreenChannel) {
interceptorService.doInterceptions(postcard, newInterceptorCallback {
@Override
publicvoidonContinue(Postcard postcard){
_navigation(context, postcard, requestCode, callback);
}
@Override
publicvoidonInterrupt(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);
}
所以上面代码我们直接看_navigation方法:
privateObject _navigation( finalContext context, finalPostcard postcard, finalintrequestCode, finalNavigationCallback callback) {
finalContext currentContext = null== context ? mContext : context;
switch(postcard.getType) {
casePROVIDER:
returnpostcard.getProvider;
}
returnnull;
}
这里先把其他类型给省略掉了,如果是PROVIDER类型的,直接把postCard的provider直接返回,所以我们上面怎么拿到的InterceptorServiceImpl就迎刃而解了。
PostCard. T navigation(Class<? extends T> service)
上面分析的是通过path获取到路由表的实例,还有另外通过传进来的class类型也可以获取,我们就拿上一节用到的JsonServiceImpl怎么拿到的,在PostCard中可以通过withObject传值,其实归功于JsonServiceImpl,可以看下面代码:
publicPostcard withObject( @NullableString key, @NullableObject value) {
serializationService = ARouter.getInstance.navigation(SerializationService. class);
mBundle.putString(key, serializationService.object2Json(value));
returnthis;
}
所以直接看_Arouter.navigation(Class<? extends T> service) 方法:
protected<T> T navigation( Class<? extendsT> service) {
try{
Postcard postcard = LogisticsCenter.buildProvider(service.getName);
if( null== postcard) {
// No service, or this service in old version.
postcard = LogisticsCenter.buildProvider(service.getSimpleName);
}
if( null== postcard) {
returnnull;
}
LogisticsCenter.completion(postcard);
return(T) postcard.getProvider;
} catch(NoRouteFoundException ex) {
logger.warning(Consts.TAG, ex.getMessage);
returnnull;
}
}
直接看LogisticsCenter.buildProvider方法:
publicstaticPostcard buildProvider( String serviceName) {
RouteMeta meta = Warehouse.providersIndex. get(serviceName);
if( null== meta) {
returnnull;
} else{
returnnewPostcard(meta.getPath, meta.getGroup);
}
}
此处是从providersIndex中去拿对应的RouteMeta信息,而providersIndex是在初始化sdk中通过加载ARouter$$Providers$$模块名的loadInto方法,好吧,为了大家能顺着阅读,我把上面的代码重新复制了一份:
public classARouter$$ Providers$$ appimplementsIProviderGroup{
@Override
public voidloadInto( Map< String, RouteMeta> providers) {
providers.put( "com.alibaba.android.arouter.facade.service.DegradeService", RouteMeta.build(RouteType.PROVIDER, DegradeServiceImpl. class, "/yourservicegroupname/DegradeServiceImpl", "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.facade.service.PathReplaceService", RouteMeta.build(RouteType.PROVIDER, PathReplaceServiceImpl. class, "/yourservicegroupname/pathReplaceService", "yourservicegroupname", null, -1, -2147483648));
providers.put( "com.alibaba.android.arouter.facade.service.PretreatmentService", RouteMeta.build(RouteType.PROVIDER, PretreatmentServiceImpl. class, "/yourservicegroupname/pretreatmentService", "yourservicegroupname", null, -1, -2147483648));
}
}
看到了没,SerializationService作为的key时候,指向的是JsonServiceImpl的RouteMeta,所以在navigation方法传class对象的时候,是在providersIndex中先去找,没找到,最终通过navigation传path去找,这也是为什么在ARouter$$Group$$组名和ARouter$$Providers$$模块名中都定义了provider的routeMeta信息,一种通过path来找provider,一种通过class来找provider。
4
Arouter路由跳转的设计
由于activity路由的postCard的isGreenChannel为false,因此在_Arouter.navigation方法中会走如下代码:
上面已经分析了interceptorService是interceptorServiceImpl,因此看doInterceptions方法:
@Override
publicvoiddoInterceptions( finalPostcard postcard, finalInterceptorCallback callback) {
//省略了拦截器的流程,直接看callback.onContinue
callback.onContinue(postcard);
}
如果默认没有拦截器,直接会走callback.onContinue,回到_Arouter的navigation,会走_navigation方法:
privateObject _navigation( finalContext context, finalPostcard postcard, finalintrequestCode, finalNavigationCallback callback) {
finalContext currentContext = null== context ? mContext : context;
switch(postcard.getType) {
caseACTIVITY:
// Build intent
finalIntent intent = newIntent(currentContext, postcard.getDestination);
runInMainThread( newRunnable {
@Override
publicvoidrun{
startActivity(requestCode, currentContext, intent, postcard, callback);
}
});
break;
}
returnnull;
}
所以最终也是通过routeMeta的destination作为目标activity的class跳转。
所以activity最终跳转也是先获取routeMeta,最终完成跳转。
5
Arouter拦截器的设计
这个我们直接看InterceptorServiceImpl的doInterceptions方法:
@Override
publicvoiddoInterceptions( finalPostcard postcard, finalInterceptorCallback callback) {
//如果有interceptors
if( null!= Warehouse.interceptors && Warehouse.interceptors.size > 0) {
//判断拦截器有没有初始化成功
checkInterceptorsInitStatus;
if(!interceptorHasInit) {
callback.onInterrupt( newHandlerException( "Interceptors initialization takes too much time."));
return;
}
LogisticsCenter.executor.execute( newRunnable {
@Override
publicvoidrun{
CancelableCountDownLatch interceptorCounter = newCancelableCountDownLatch(Warehouse.interceptors.size);
try{
_execute( 0, interceptorCounter, postcard);
//拦截器如果超时会回调callback.onInterrupt
interceptorCounter.await(postcard.getTimeout, TimeUnit.SECONDS);
if(interceptorCounter.getCount > 0) {
callback.onInterrupt( newHandlerException( "The interceptor processing timed out."));
} elseif( null!= postcard.getTag) {
callback.onInterrupt( newHandlerException(postcard.getTag.toString));
} else{
callback.onContinue(postcard);
}
} catch(Exception e) {
callback.onInterrupt(e);
}
}
});
}
}
创建CancelableCountDownLatch作为所有拦截器处理完成的标志,处理IInterceptor通过触发process方法,如果想继续处理下一个拦截通过触发InterceptorCallback的onContinue方法,如果想拦截掉路由的处理,通过触发InterceptorCallback的onInterrupt方法。如果所有的拦截都不拦截,则会回调到doInterceptions方法的callback.onContinue(postcard);这一句,而这一句最终回到了_Arouter._navigation方法,走正常的路由处理了。
6
Arouter的服务怎么设计的
上一节我们通过定义了HelloServiceImpl的服务:
@Route(path = "/common/hello", name = "测试服务")
classHelloServiceImpl: HelloService {
overridefunsayHello(name: String) : String {
Log.d( "HelloServiceImpl", "hello, $name" )
return"hello, $name"
}
overridefuninit(context: Context) {}
}
然后我们可以通过传服务的path或class类都可以获取:
helloService2=ARouter.getInstance.build( "/common/hello").navigation as HelloService
helloService3= ARouter.getInstance.navigation(HelloService::class.java)
这也就是我们上面说的Iprovider可以通过两种形式获取,他们分别定义在了ARouter$$Group$$组名、ARouter$$Providers$$模块名中。
7
Arouter的注解属性怎么获取的
我们在定义属性的时候通过Autowired注解赋值,比如我上一节在shareActivity中定义如下属性:
@Autowired(name = "username")
lateinitvarusername: String
@Autowired
lateinitvartestBean: TestBean
@Autowired(name = "listBean")
lateinitvarlistBean: List<TestBean>
会在build下生成如下代码:
publicclassShareActivity$$ ARouter$$ AutowiredimplementsISyringe{
privateSerializationService serializationService;
@Override
publicvoidinject(Object target){
serializationService = ARouter.getInstance.navigation(SerializationService.class);
ShareActivity substitute = (ShareActivity)target;
substitute.username = substitute.getIntent.getExtras == null? substitute.username : substitute.getIntent.getExtras.getString( "username", substitute.username);
if( null!= serializationService) {
substitute.testBean = serializationService.parseObject(substitute.getIntent.getStringExtra( "testBean"), newcom.alibaba.android.arouter.facade.model.TypeWrapper<TestBean>{}.getType);
} else{
}
if( null!= serializationService) {
substitute.listBean = serializationService.parseObject(substitute.getIntent.getStringExtra( "listBean"), newcom.alibaba.android.arouter.facade.model.TypeWrapper<List<TestBean>>{}.getType);
} else{
}
}
}
可以看到在inject方法中,获取了基本类型和对象类型,如果是基本类型,直接给属性赋值,如果是对象类型,先获取SerializationService对应的实例,所以我们想定义对象类型的属性,需要实例化SerializationService类型,那什么时候调用的inject方法,在定义属性的类中有这么一句:
ARouter.getInstance.inject( this)
最终到_Arouter.inject方法:
staticvoidinject( Object thiz) {
AutowiredService autowiredService = ((AutowiredService) ARouter.getInstance.build( "/arouter/service/autowired").navigation);
if( null!= autowiredService) {
autowiredService.autowire(thiz);
}
}
这里拿的AutowiredService是AutowiredServiceImpl,这跟InterceptorServiceImpl获取方式是一样的,这里就不介绍了,直接看AutowiredServiceImpl的doInject方法:
privatevoiddoInject( Object instance, Class<?> parent) {
Class<?> clazz = null== parent ? instance.getClass : parent;
ISyringe syringe = getSyringe(clazz);
//上面就拿到了ShareActivity$$ARouter$$Autowired类,调用inject方法
if( null!= syringe) {
syringe.inject(instance);
}
Class<?> superClazz = clazz.getSuperclass;
if( null!= superClazz && !superClazz.getName.startsWith( "android")) {
doInject(instance, superClazz);
}
}
最终在这里调用了ShareActivity$$ARouter$$Autowired的inject方法。
8
Arouter自动加载路由表
自动加载路由表是通过arouter-register来实现的,主要通过在编译期给LogisticsCenter的loadRouterMap方法插入register方法调用的代码:
我们可以通过反编译工具查看下apk下面的该类:
反编译出来的代码确实在loadRouterMap方法处插入了register方法调用的代码,并且把registerByPlugin置为true,所以最终不会通过扫描dex文件来加载路由表类装载到map中。
那大家想想和普通的通过扫描dex文件加载class有什么区别呢,我们在上面普通加载dex文件可以看到在初始化Arouter sdk的时候是非常慢的,因为它得扫描dex文件,然后加载dex文件里面的所有class,然后过滤出arouter需要的class文件,这还不是算慢得,如果虚拟机不支持MultiDex还会更慢,它会通过压缩所有的dex文件,然后压缩成zip,然后通过DexFile.loadDex转话成dex文件的集合,我们知道在DexFile.loadDex过程中会把普通的dex文件抓话成odex,这个过程是很慢的,关于dex文件转成odex做了些啥大家可以查查,具体我也不是很清楚,哈哈哈。最后通过遍历dex文件,拿到里面的class文件,最后过滤拿到Arouter需要的class,在我们项目中亲测arouter普通初始化是花了4秒多,所以我们可以看到Arouter自动加载路由表的插件对启动优化还是有很大改善的,Arouter自动加载路由表的插件是使用的通过gradle插装技术在编译期插入代码来达到自动加载路由表信息。
那大家想想和普通的通过扫描dex文件加载class有什么区别呢,我们在上面普通加载dex文件可以看到在初始化Arouter sdk的时候是非常慢的,因为它得扫描dex文件,然后加载dex文件里面的所有class,然后过滤出arouter需要的class文件,这还不是算慢得,如果虚拟机不支持MultiDex还会更慢,它会通过压缩所有的dex文件,然后压缩成zip,然后通过DexFile.loadDex转话成dex文件的集合,我们知道在DexFile.loadDex过程中会把普通的dex文件抓话成odex,这个过程是很慢的,关于dex文件转成odex做了些啥大家可以查查,具体我也不是很清楚,哈哈哈。最后通过遍历dex文件,拿到里面的class文件,最后过滤拿到Arouter需要的class,在我们项目中亲测arouter普通初始化是花了4秒多,所以我们可以看到Arouter自动加载路由表的插件对启动优化还是有很大改善的,Arouter自动加载路由表的插件是使用的通过gradle插装技术在编译期插入代码来达到自动加载路由表信息。
关于字节码插装技术我也不是很懂,所以我也会去恶补相关知识去了。
源码总结
在初始化阶段把所有的root、interceptor、provider信息分别存储到groupsIndex、interceptorsIndex、providersIndex中
然后初始化interceptorServiceImpl实例,顺便将interceptor加入到interceptors中。
在加载路由的时候,先去routes中取,如果没取到则去groupsIndex中拿group信息,再拿对应的metaMeta信息,将属性封装到postCard中,最后通过判断metaType是那种类型,做相应类型的处理。
navigation有两种形式获取Iprovider,通过传class或path,如果传的是class则去interceptorsIndex找对应的Iprovder,如果是path则去groupsIndex中找,找到后,最终会保存在providers中。
源码总结
在初始化阶段把所有的root、interceptor、provider信息分别存储到groupsIndex、interceptorsIndex、providersIndex中
然后初始化interceptorServiceImpl实例,顺便将interceptor加入到interceptors中。
在加载路由的时候,先去routes中取,如果没取到则去groupsIndex中拿group信息,再拿对应的metaMeta信息,将属性封装到postCard中,最后通过判断metaType是那种类型,做相应类型的处理。
navigation有两种形式获取Iprovider,通过传class或path,如果传的是class则去interceptorsIndex找对应的Iprovder,如果是path则去groupsIndex中找,找到后,最终会保存在providers中。