阿里ARouter全面全面全面解析(使用介绍+源码分析+设计思路)

目录

概述

使用介绍

1.基础依赖

1.1.java版本的依赖

1.2.kotlin版本的依赖

2.初始化

3.添加注解

3.1@Route注解

3.2@Interceptor注解

3.3@Autowired注解

3.3预处理服务

3.4重定义URL跳转

4.发起路由

5.混淆

6.使用Gradle实现路由表自动加载

7.使用IDE插件通过导航的形式到目标类

ARouter 源码分析

源码分析

初始化

ARouter.build

PostCard.navigation

PostCard. T navigation(Class service)

Arouter路由跳转的设计

Arouter拦截器的设计

Arouter的服务怎么设计的

Arouter的注解属性怎么获取的

Arouter自动加载路由表

源码总结


概述

arouter是android实现组件化的路由框架,涉及到的功能有activity、fragment的跳转、跳转带参数、自定义服务、自定义拦截器、拦截下沉、重定向url都是Arouter里面定义的功能,可能用过Arouter的小伙伴们只用过Arouter的跳转以及跳转功能带参数的功能,像它的自定义服务、拦截器、全局降级策略、重定向功能都是很不错的功能,下面我会一一介绍这些功能该怎么使用。

使用介绍

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")
class LoginActivity : AppCompatActivity() {
    ...
}

Route注解添加全局序列化方式

@Route(path = "/yourservicegroupname/json")
class JsonServiceImpl : SerializationService {
    lateinit var gson: Gson
    override fun <T : Any?> json2Object(input: String?, clazz: Class<T>?): T {
        return gson.fromJson(input, clazz)
    }

    override fun init(context: Context?) {
        gson = Gson()
    }

    override fun object2Json(instance: Any?): String {
        return gson.toJson(instance)
    }

    override fun <T : Any?> parseObject(input: String?, clazz: Type?): T {
        return gson.fromJson(input, clazz)
    }

}

这里用了Route注解定义了SerializationService的序列化的方式,在使用withObject的时候会使用该SerializationService,后面会讲到该情况。

Route注解定义了全局降级策略

@Route(path = "/yourservicegroupname/DegradeServiceImpl")
class DegradeServiceImpl : DegradeService {
    override fun onLost(context: Context, postcard: Postcard) {
        Log.d("DegradeServiceImpl", "没有找到该路由地址:${postcard.path}")
    }

    override fun init(context: Context?) {
    }

}

上面也是用了Route注解定义了全局降级策略,也就是在找不到的路由表的时候,做相应的处理。

Route注解实现提供服务

interface HelloService:IProvider{
    fun sayHello(name:String):String
}

// 实现接口
@Route(path = "/common/hello", name = "测试服务")
class HelloServiceImpl : HelloService {
    override fun sayHello(name: String): String {
        Log.d("HelloServiceImpl", "hello, $name")
        return "hello, $name"
    }

    override fun init(context: Context) {}
}

这个例子是官网的写法,意思是通过Route注解实现提供服务,那怎么实现接收服务呢,下面会在另外一种注解的时候讲到。

3.2@Interceptor注解

这个注解可以说非常强大,它能拦截你的路由,什么时候让路由通过什么时候让路由不通过,完全靠该Interceptor注解可以控制。比如我有一个需求,在跳分享的时候,我想看有没有登录,如果没有登录做登录的操作,如果登了了才让分享。如果之前是不是在每一个路由的地方都得判断有没有登录,很繁琐,有了路由拦截器不用在跳转的地方判断。

@Interceptor(priority = 8, name = "登录拦截")
class LoginInterceptor : IInterceptor {
    override fun process(postcard: Postcard, callback: InterceptorCallback) {
        val path = postcard.path
        if (path == "/share/shareActivity") {
            val userInfo = DataSource.getInstance(ArouterApplication.application).getUserInfo()
            if (TextUtils.isEmpty(userInfo)) {
                callback.onInterrupt(Throwable("还没有登录,去登陆"))
            } else {
                callback.onContinue(postcard)
            }
        }else{
            callback.onContinue(postcard)
        }
    }

    override fun init(context: Context?) {
        // 拦截器的初始化,会在sdk初始化的时候调用该方法,仅会调用一次
        Log.d("LoginInterceptor", "LoginInterceptor初始化了")
    }

}

拦截器可以定义优先级,如果有多个拦截器,会依次执行拦截器。

3.3@Autowired注解

Autowired注解是定义在目标页的属性上,通常用来定义目标页接收的值,还可以定义上面说到的接收服务方

3.3.1Autowired注解接收值

@Autowired(name = "username")
lateinit var username: String
@Autowired(name = "testBean")
lateinit var testBean: TestBean
@Autowired(name ="listBean" )
lateinit var listBean: List<TestBean>

上面定义基本类型的值接收,还有自定义bean和集合的接收。

3.3.2Autowired注解接收服务

@Autowired
lateinit var helloService: HelloService
@Autowired(name = "/common/hello")
lateinit var helloService1: HelloService
lateinit var helloService2: HelloService
lateinit var helloService3: 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")
class PretreatmentServiceImpl : PretreatmentService {
    override fun onPretreatment(context: Context, postcard: Postcard): Boolean {
        if (postcard.path == "/share/ShareActivity") {
            val userInfo = DataSource.getInstance(ArouterApplication.application).getUserInfo()
            if (TextUtils.isEmpty(userInfo)) {
                Toast.makeText(ArouterApplication.application, "你还没有登录", Toast.LENGTH_SHORT).show()
                return false// 跳转前预处理,如果需要自行处理跳转,该方法返回 false 即可
            }
        }
        return true
    }

    override fun init(context: Context) {}
}

其实在这个例子中我演示的拦截器功能和预处理服务功能是一样的,只不过预处理服务是早于拦截器的,等到分析源码的时候我们分析他们的具体区别。

3.4重定义URL跳转

重新定以URL的跳转

// 实现PathReplaceService接口,并加上一个Path内容任意的注解即可

@Route(path = "/yourservicegroupname/pathReplaceService") // 必须标明注解
class PathReplaceServiceImpl : PathReplaceService {
    /**
     * For normal path.
     *
     * @param path raw path
     */
    override fun forString(path: String): String {
        if (path == "/login/loginActivity") {
            return "/share/shareActivity"
        }
        return path // 按照一定的规则处理之后返回处理后的结果
    }

    /**
     * For uri type.
     *
     * @param uri raw uri
     */
    override fun forUri(uri: Uri?): Uri? {
        return null // 按照一定的规则处理之后返回处理后的结果
    }

    override fun init(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", new Test("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 {
        override fun onLost(postcard: Postcard?) {
        }
        override fun onFound(postcard: Postcard?) {
        }
        override fun onInterrupt(postcard: Postcard?) {
            Log.d("LoginActivity", "还没有登录")
        }
        override fun onArrival(postcard: Postcard?) {
        }
    })

实现了四个方法,onLost是找不到路由,onFound是找到路由,onInterrupt表示路由挂了,默认路由设置的超时时间是300s,onArrival表示路由跳转成功的回调,目前只在startActivity的回调,这个后面源码部分会讲到。

5.混淆

混淆部分就没什么好说的了,因为Arouter是通过反射创建arouter的注解类,所以大部分需要加混淆:

-keep public class com.alibaba.android.arouter.routes.**{*;}
-keep public class com.alibaba.android.arouter.facade.**{*;}
-keep class * implements com.alibaba.android.arouter.facade.template.ISyringe{*;}

# 如果使用了 byType 的方式获取 Service,需添加下面规则,保护接口
-keep interface * implements com.alibaba.android.arouter.facade.template.IProvider

# 如果使用了 单类注入,即不定义接口实现 IProvider,需添加下面规则,保护实现
# -keep class * implements com.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
buildscript {
    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

ARouter 源码分析

这节主要围绕Arouter源码的设计,以及通过源码我们能学习到什么,以及如何应对面试过程中Arouter的问题。

源码分析

在使用Arouter过程中,我们的module依赖了arouter-api、arouter-compiler,在编译期arouter-compiler通过扫描项目中用到的Route、Autowired、Interceptor等注解来生成对应的class文件,大家如果想学习学习编译期扫描注解生成class文件可以学习apt相关的技术,或者看Arouter官网的arouter-compiler模块怎么扫描注解生成class文件的。

比如我在login模块的LoginActivity中定义如下的注解:

@Route(path = "/login/loginActivity")
class LoginActivity : AppCompatActivity() {
}


结果在module的build目录下生成了ARouter$$Root$$loginclass文件,它是实现IRouteRoot接口:

public class ARouter$$Root$$login implements IRouteRoot {
  @Override
  public void loadInto(Map<String, Class<? extends IRouteGroup>> routes) {
    routes.put("login", ARouter$$Group$$login.class);
  }
}

而此处的ARouter$$Group$$login类它是实现了IRouteGroup接口:

public class ARouter$$Group$$login implements IRouteGroup {

  @Override
  public void loadInto(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管理的:

public class ARouter$$Interceptors$$app implements IInterceptorGroup {

  @Override
  public void loadInto(Map<Integer, Class<? extends IInterceptor>> interceptors) {
    interceptors.put(8, LoginInterceptor.class);
  }
}

IInterceptor就没有被包装成RouteMeta对象,上面在ARouter$$Group$$yourservicegroupname中定义的IProvider信息还会在ARouter$$Providers$$app被定义了一遍:

public class ARouter$$Providers$$app implements IProviderGroup {
  @Override
  public void loadInto(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$$模块名下面。

初始化

我们在上一面使用部分,初始化部分代码如下:

ARouter.init(this)

就一句,那下面追随源码看下:


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方法以及_ARouter.afterInit方法,其实我们的入口虽然是ARouter类的,但是真正调用的还是_ARouter类的方法,废话不多说,直接看_ARouter.init方法:

_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;
}

最终还是调用了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 new version name when router map update finishes.
            } else {
                //直接从sp中取过滤到的class类
                routerMap = new HashSet<>(context.getSharedPreferences(AROUTER_SP_CACHE_KEY, Context.MODE_PRIVATE).getStringSet(AROUTER_SP_KEY_MAP, new HashSet<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);
                } else if (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);
                } else if (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) {
        throw new HandlerException(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);
        }
        return build(path, extractGroup(path), true);
    }
}

先判断有没有设置PathReplaceService类型的路由,如果有会调用forString方法返回新的path,这也就是我们的path替换的IProvider,在之前我们定义过PathReplaceServiceImpl类:

@Route(path = "/yourservicegroupname/pathReplaceService") // 必须标明注解
class PathReplaceServiceImpl : PathReplaceService {

    override fun forString(path: String): String {
        if (path == "/login/loginActivity") {
            return "/share/shareActivity"
        }
        return path // 按照一定的规则处理之后返回处理后的结果
    }

    override fun forUri(uri: Uri?): Uri? {
        return null // 按照一定的规则处理之后返回处理后的结果
    }

    override fun init(context: Context?) {
    }
}

如果有该PathReplaceService会替换掉我们的/login/loginActivity为/share/shareActivity,这里先不管怎么拿到PathReplaceServiceImpl,通过extractGroup方法拿到group:

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 {
        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;
    }
}

很简单,按照第一个'/'和第二个'/'的字符作为group信息,所以这也是为什么我们在定义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);
    }
}

所以build过程把传过来的path构造出Postcard对象,给path和group赋值。

PostCard.navigation

navigation有很多重载的方法,最终都会走_Arouter.navigation,其中navigation里面也有两种形式获取到路由表类,我们先介绍activity常规的形式:

protected Object navigation(final Context context, final Postcard postcard, final int requestCode, final NavigationCallback callback) {
    //这也是个路由表,通过另外一种形式获取PretreatmentService的实例
    PretreatmentService pretreatmentService = ARouter.getInstance().navigation(PretreatmentService.class);
    //如果onPretreatment返回false就是自己处理路由逻辑,不往下走了
    if (null != pretreatmentService && !pretreatmentService.onPretreatment(context, postcard)) {
        return null;
    }

    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);
            }
        }

        return null;
    }
    //省略provider部分逻辑和_navigation部分代码

    return null;
}

由于navigation代码比较长,我把代码分块来说,上面首先获取PretreatmentService类型的路由表,我们先只说上面传入/arouter/service/interceptor,怎么得到InterceptorService实例的,我们直接看LogisticsCenter.completion:

public synchronized static void completion(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<? extends IRouteGroup> 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) {
                throw new HandlerException(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());

        Uri rawUri = 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(new String[]{}));
            }

            // Save raw uri
            postcard.withString(ARouter.RAW_URI, rawUri.toString());
        }

        switch (routeMeta.getType()) {
            //由于InterceptorServiceImpl是provider类型的
            case PROVIDER:
                Class<? extends IProvider> providerMeta = (Class<? extends IProvider>) 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) {
                        throw new HandlerException("Init provider failed! " + e.getMessage());
                    }
                }
                //给postcard赋值
                postcard.setProvider(instance);
                postcard.greenChannel();
                break;
            case FRAGMENT:
                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
public void init(final Context context) {
    LogisticsCenter.executor.execute(new Runnable() {
        @Override
        public void run() {
            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) {
                        throw new HandlerException(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。

创建完InterceptorServiceImpl,我们一下子要回到_ARouter的navigation方法的下半部分代码,上面没有贴出代码:

//如果是`InterceptorServiceImpl`类型的postcard.isGreenChannel()是true,除非是activity或fragment类型的
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 {
    return _navigation(context, postcard, requestCode, callback);
}


所以上面代码我们直接看_navigation方法:

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 PROVIDER:
            return postcard.getProvider();

    }

    return null;
}
这里先把其他类型给省略掉了,如果是PROVIDER类型的,直接把postCard的provider直接返回,所以我们上面怎么拿到的InterceptorServiceImpl就迎刃而解了。

PostCard. T navigation(Class<? extends T> service)

上面分析的是通过path获取到路由表的实例,还有另外通过传进来的class类型也可以获取,我们就拿上一节用到的JsonServiceImpl怎么拿到的,在PostCard中可以通过withObject传值,其实归功于JsonServiceImpl,可以看下面代码:

public Postcard withObject(@Nullable String key, @Nullable Object value) {
    serializationService = ARouter.getInstance().navigation(SerializationService.class);
    mBundle.putString(key, serializationService.object2Json(value));
    return this;
}

所以直接看_Arouter.navigation(Class<? extends T> service) 方法:

protected <T> T navigation(Class<? extends T> 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) {
            return null;
        }
        LogisticsCenter.completion(postcard);
        return (T) postcard.getProvider();
    } catch (NoRouteFoundException ex) {
        logger.warning(Consts.TAG, ex.getMessage());
        return null;
    }
}

直接看LogisticsCenter.buildProvider方法:

public static Postcard buildProvider(String serviceName) {
    RouteMeta meta = Warehouse.providersIndex.get(serviceName);
    if (null == meta) {
        return null;
    } else {
        return new Postcard(meta.getPath(), meta.getGroup());
    }
}

此处是从providersIndex中去拿对应的RouteMeta信息,而providersIndex是在初始化sdk中通过加载ARouter$$Providers$$模块名的loadInto方法,好吧,为了大家能顺着阅读,我把上面的代码重新复制了一份:

public class ARouter$$Providers$$app implements IProviderGroup {
  @Override
  public void loadInto(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。

Arouter路由跳转的设计

由于activity路由的postCard的isGreenChannel()为false,因此在_Arouter.navigation方法中会走如下代码:

上面已经分析了interceptorService是interceptorServiceImpl,因此看doInterceptions方法:

@Override
public void doInterceptions(final Postcard postcard, final InterceptorCallback callback) {
   //省略了拦截器的流程,直接看callback.onContinue
   callback.onContinue(postcard);
}

如果默认没有拦截器,直接会走callback.onContinue,回到_Arouter的navigation,会走_navigation方法:

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());
            runInMainThread(new Runnable() {
                @Override
                public void run() {
                    startActivity(requestCode, currentContext, intent, postcard, callback);
                }
            });

            break;

    }
    return null;
}

所以最终也是通过routeMeta的destination作为目标activity的class跳转。

所以activity最终跳转也是先获取routeMeta,最终完成跳转。

Arouter拦截器的设计

这个我们直接看InterceptorServiceImpl的doInterceptions方法:

@Override
public void doInterceptions(final Postcard postcard, final InterceptorCallback callback) {
    //如果有interceptors
    if (null != Warehouse.interceptors && Warehouse.interceptors.size() > 0) {
        //判断拦截器有没有初始化成功
        checkInterceptorsInitStatus();

        if (!interceptorHasInit) {
            callback.onInterrupt(new HandlerException("Interceptors initialization takes too much time."));
            return;
        }

        LogisticsCenter.executor.execute(new Runnable() {
            @Override
            public void run() {
                CancelableCountDownLatch interceptorCounter = new CancelableCountDownLatch(Warehouse.interceptors.size());
                try {
                    _execute(0, interceptorCounter, postcard);
                    //拦截器如果超时会回调callback.onInterrupt
                    interceptorCounter.await(postcard.getTimeout(), TimeUnit.SECONDS);
                    if (interceptorCounter.getCount() > 0) {
                        callback.onInterrupt(new HandlerException("The interceptor processing timed out."));
                    } else if (null != postcard.getTag()) {
                        callback.onInterrupt(new HandlerException(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方法,走正常的路由处理了。

Arouter的服务怎么设计的

上一节我们通过定义了HelloServiceImpl的服务:

@Route(path = "/common/hello", name = "测试服务")
class HelloServiceImpl : HelloService {
    override fun sayHello(name: String): String {
        Log.d("HelloServiceImpl", "hello, $name")
        return "hello, $name"
    }

    override fun init(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$$模块名中。

Arouter的注解属性怎么获取的

我们在定义属性的时候通过Autowired注解赋值,比如我上一节在shareActivity中定义如下属性:

@Autowired(name = "username")
lateinit var username: String
@Autowired
lateinit var testBean: TestBean
@Autowired(name ="listBean" )
lateinit var listBean: List<TestBean>

会在build下生成如下代码:

public class ShareActivity$$ARouter$$Autowired implements ISyringe {
  private SerializationService serializationService;

  @Override
  public void inject(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"), new com.alibaba.android.arouter.facade.model.TypeWrapper<TestBean>(){}.getType());
    } else {
    }
    if (null != serializationService) {
      substitute.listBean = serializationService.parseObject(substitute.getIntent().getStringExtra("listBean"), new com.alibaba.android.arouter.facade.model.TypeWrapper<List<TestBean>>(){}.getType());
    } else { 
    }
  }
}

可以看到在inject方法中,获取了基本类型和对象类型,如果是基本类型,直接给属性赋值,如果是对象类型,先获取SerializationService对应的实例,所以我们想定义对象类型的属性,需要实例化SerializationService类型,那什么时候调用的inject方法,在定义属性的类中有这么一句:

ARouter.getInstance().inject(this)

最终到_Arouter.inject方法:

static void inject(Object thiz) {

    AutowiredService autowiredService = ((AutowiredService) ARouter.getInstance().build("/arouter/service/autowired").navigation());
    if (null != autowiredService) {
        autowiredService.autowire(thiz);
    }
}

这里拿的AutowiredService是AutowiredServiceImpl,这跟InterceptorServiceImpl获取方式是一样的,这里就不介绍了,直接看AutowiredServiceImpl的doInject方法:

private void doInject(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方法。

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插装技术在编译期插入代码来达到自动加载路由表信息。

关于字节码插装技术我也不是很懂,所以我也会去恶补相关知识去了。

源码总结

  • 在初始化阶段把所有的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中。

  • 7
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值