ARouter的源码阅读笔记

写在前面

这篇文章不讲ARouter的用法, 只是阅读源码, 体会下阿里大神的思想. 所以想了解ARouter怎么用的请绕道走开

一. 从navigation说起

ARouter进行界面跳转的时候就几乎上就一句代码

       ARouter.getInstance().build(ARouterPath.ACTIVITY_PHOTOS)
                    .navigation(this)

这个代码调用后发生了什么, 一个一个顺着看

  • build
    在调用build之后, 其实就生成了一个PostCard对象, PostCard对象中从path解析出来group信息, 其他的什么都没有干
protected Postcard build(String path, String group) {
    
    
     if (TextUtils.isEmpty(path) || TextUtils.isEmpty(group)) {
    
    
         throw new HandlerException(Consts.TAG + "Parameter is invalid!");
     } else {
    
    
         PathReplaceService pService = ARouter.getInstance().navigation(PathReplaceService.class);
         if (null != pService) {
    
    
             path = pService.forString(path);
         }
         return new Postcard(path, group);
     }
 }
  • navigation
protected Object navigation(final Context context, final Postcard postcard, final int requestCode, final NavigationCallback callback) {
    
    
	...
    try {
    
    
    	//补充postcard遗失的信息, 这一步比较重要, 下面单独讲
        LogisticsCenter.completion(postcard);
    } catch (NoRouteFoundException ex) {
    
    
    	...
    }

    if (!postcard.isGreenChannel()) {
    
    
    	... //分主干流程, 忽略
    } 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
            // 下面就从postCard拿到要跳转到目标, 进行跳转即可.
            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;
            ...
}

从上面代码可以看出来, 主要完成了两部

  1. 通过LogisticsCenter.completion(postcard);来完善postcard的信息
  2. 通过_navigation方法进行跳转, 这个和系统进行Activity的跳转没有什么不同

那就看看LogisticsCenter.completion(postcard)做了什么

public synchronized static void completion(Postcard postcard) {
    
    
	//从内存中读取已经加载的postcard数据
    RouteMeta routeMeta = Warehouse.routes.get(postcard.getPath());
    if (null == routeMeta) {
    
        // Maybe its does't exist, or didn't load.
    	//先获取到IRouteGroup, 然后加载到内存
        Class<? extends IRouteGroup> groupMeta = Warehouse.groupsIndex.get(postcard.getGroup());  // Load route meta.
        //如果内存中没有, 那就加载到内存中. 
        ...
     	IRouteGroup iGroupInstance = groupMeta.getConstructor().newInstance();
     	//加载到内存
        iGroupInstance.loadInto(Warehouse.routes);
        Warehouse.groupsIndex.remove(postcard.getGroup());
        completion(postcard);   // Reload
    } else {
    
    
    	//从内宠着你甘家寨各种数据
    	...
        postcard.setDestination(routeMeta.getDestination());
        postcard.setType(routeMeta.getType());
        postcard.setPriority(routeMeta.getPriority());
        postcard.setExtra(routeMeta.getExtra());
    }
}

如上面可以看到

  1. 会先从内存中找route信息
  2. 内存中找不到, 会先找对应的group信息, 也就是IRouteGroup
  3. 找到之后, 会从IRouteGroup中加载对应的route信息
  4. 所以ARouter官方介绍中讲到, 会分组加载,

二. 从ARouter.init说起

在Application onCreate的时候, 需要调用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中, 我们看下这个方法做了什么

public synchronized static void init(Context context, ThreadPoolExecutor tpe) throws HandlerException {
    
    
	...
	//解析Router map信息, 这里的Router Map其实就是要找到的Class路径
    Set<String> routerMap = ClassUtils.getFileNameByPackageName(mContext, ROUTE_ROOT_PAKCAGE);
    for (String className : routerMap) {
    
    
        if (className.startsWith(ROUTE_ROOT_PAKCAGE + DOT + SDK_NAME + SEPARATOR + SUFFIX_ROOT)) {
    
    
            //找到一个名字是com.alibaba.android.arouter.routes.ARouter$$Root$$app
            ((IRouteRoot) (Class.forName(className).getConstructor().newInstance())).loadInto(Warehouse.groupsIndex);
        } 
        ...
}

从上面的代码中可以看到, 关键是找到com.alibaba.android.arouter.routes.ARouter$$Root$$app类, 我们在代码中找到他

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

再看看他加载的class

public class ARouter$$Group$$account implements IRouteGroup {
    
    
  @Override
  public void loadInto(Map<String, RouteMeta> atlas) {
    
    
    atlas.put("/account/bindPhone", RouteMeta.build(RouteType.ACTIVITY, BindPhoneActivity.class, "/account/bindphone", "account", null, -1, -2147483648));
  }
}

可以看到, 调用这个类, 就可以获取到我们注解的Router Activity信息, 在第一章中, 跳转的逻辑就补全了

三. 编译的时候干了什么

从上面看出来, 代码的关键在于编辑的时候生成了com.alibaba.android.arouter.routes.ARouter$$Root$$app类和 com.alibaba.android.arouter.routes.ARouter$$Root$$app类, 这两个类肯定是在编译的阶段生成的.

我们继承ARouter框架的时候, 需要加上下面的配置

kapt 'com.alibaba:arouter-compiler:1.2.2'

这个就是用来生成类文件的, 具体怎么生成, 我们需要download github地址, 看源码
地址

https://github.com/alibaba/ARouter
主要的逻辑是RouteProcess中

@Override
public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
    
    
	...
	//获取Route信息
    Set<? extends Element> routeElements = roundEnv.getElementsAnnotatedWith(Route.class);
    this.parseRoutes(routeElements);
    ...
}

看一下parseRoutes的实现

private void parseRoutes(Set<? extends Element> routeElements) throws IOException {
    
    
    if (CollectionUtils.isNotEmpty(routeElements)) {
    
    
    	...
    	//获取Activity的typeMirror
        TypeMirror type_Activity = elementUtils.getTypeElement(ACTIVITY).asType();
       	
        // Interface of ARouter
        TypeElement type_IRouteGroup = elementUtils.getTypeElement(IROUTE_GROUP);
        TypeElement type_IProviderGroup = elementUtils.getTypeElement(IPROVIDER_GROUP);
        ClassName routeMetaCn = ClassName.get(RouteMeta.class);
        ClassName routeTypeCn = ClassName.get(RouteType.class);

        /*
           Build input type, format as :
           ```Map<String, Class<? extends IRouteGroup>>```
         */
        ParameterizedTypeName inputMapTypeOfRoot = ParameterizedTypeName.get(
                ClassName.get(Map.class),
                ClassName.get(String.class),
                ParameterizedTypeName.get(
                        ClassName.get(Class.class),
                        WildcardTypeName.subtypeOf(ClassName.get(type_IRouteGroup))
                )
        );

        /*

          ```Map<String, RouteMeta>```
         */
        ParameterizedTypeName inputMapTypeOfGroup = ParameterizedTypeName.get(
                ClassName.get(Map.class),
                ClassName.get(String.class),
                ClassName.get(RouteMeta.class)
        );

        /*
          Build input param name.
         */
        ParameterSpec rootParamSpec = ParameterSpec.builder(inputMapTypeOfRoot, "routes").build();
        ParameterSpec groupParamSpec = ParameterSpec.builder(inputMapTypeOfGroup, "atlas").build();
        ParameterSpec providerParamSpec = ParameterSpec.builder(inputMapTypeOfGroup, "providers").build();  // Ps. its param type same as groupParamSpec!

        /*
          Build method : 'loadInto'
         */
        MethodSpec.Builder loadIntoMethodOfRootBuilder = MethodSpec.methodBuilder(METHOD_LOAD_INTO)
                .addAnnotation(Override.class)
                .addModifiers(PUBLIC)
                .addParameter(rootParamSpec);

        //  Follow a sequence, find out metas of group first, generate java file, then statistics them as root.
        for (Element element : routeElements) {
    
    
            TypeMirror tm = element.asType();
            Route route = element.getAnnotation(Route.class);
            RouteMeta routeMeta;

            // Activity or Fragment
            if (types.isSubtype(tm, type_Activity)) {
    
    
                // Get all fields annotation by @Autowired
                Map<String, Integer> paramsType = new HashMap<>();
                Map<String, Autowired> injectConfig = new HashMap<>();
                injectParamCollector(element, paramsType, injectConfig);

                if (types.isSubtype(tm, type_Activity)) {
    
    
                    // Activity
                    logger.info(">>> Found activity route: " + tm.toString() + " <<<");
                    routeMeta = new RouteMeta(route, element, RouteType.ACTIVITY, paramsType);
                }
                routeMeta.setInjectConfig(injectConfig);
            } 
            ...
            categories(routeMeta);
        }

        MethodSpec.Builder loadIntoMethodOfProviderBuilder = MethodSpec.methodBuilder(METHOD_LOAD_INTO)
                .addAnnotation(Override.class)
                .addModifiers(PUBLIC)
                .addParameter(providerParamSpec);

        Map<String, List<RouteDoc>> docSource = new HashMap<>();

        // Start generate java source, structure is divided into upper and lower levels, used for demand initialization.
        for (Map.Entry<String, Set<RouteMeta>> entry : groupMap.entrySet()) {
    
    
            String groupName = entry.getKey();

            MethodSpec.Builder loadIntoMethodOfGroupBuilder = MethodSpec.methodBuilder(METHOD_LOAD_INTO)
                    .addAnnotation(Override.class)
                    .addModifiers(PUBLIC)
                    .addParameter(groupParamSpec);

            List<RouteDoc> routeDocList = new ArrayList<>();

            // Generate groups
            String groupFileName = NAME_OF_GROUP + groupName;
            // 写入文件
            JavaFile.builder(PACKAGE_OF_GENERATE_FILE,
                    TypeSpec.classBuilder(groupFileName)
                            .addJavadoc(WARNING_TIPS)
                            .addSuperinterface(ClassName.get(type_IRouteGroup))
                            .addModifiers(PUBLIC)
                            .addMethod(loadIntoMethodOfGroupBuilder.build())
                            .build()
            ).build().writeTo(mFiler);

            logger.info(">>> Generated group: " + groupName + "<<<");
            rootMap.put(groupName, groupFileName);
            docSource.put(groupName, routeDocList);
        }

四: 缺点

ARouter的优点不必说了.读完整个代码之后, 可以发现ARouter的一些缺点

  1. Fragment没有办法startActivityForResult
  2. 采用注解, 编译阶段生成代码, 且没有增量编译的部分, 导致编译速度减慢.

目前只看到这两个缺点, 其他的暂时没有发现.

五. 我学到了什么

下面这两小段总结放到新的博客里面吧.

1. 怎么使用编译时注解

2. 怎么应用启动的时候从dex包里面遍历class

猜你喜欢

转载自blog.csdn.net/weixin_43662090/article/details/110931437