ARouter源码简析一:初始化流程

ARouter源码简析二:navigation过程

Arouter简单使用


@Route(path = "/image/imageActivity")
class MainActivity : AppCompatActivity() {
    @Autowired(name = "tip")
    @JvmField
    var mTip: String? = ""
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        ARouter.getInstance().inject(this)
        setContentView(R.layout.layout_activity_main)
        findViewById<TextView>(R.id.mTvImageModule).setOnClickListener {
            ARouter.getInstance().build("/login/loginActivity").navigation(this)
        }
    }
}

使用@Route标识路由(至少2级 /image/imageActivity),第一级代表的分组。编译后生成文件

Arouter编译生成文件

在通过注解处理器在build目录生成了相关文件

Group

public class ARouter$$Group$$image implements IRouteGroup {
  @Override
  public void loadInto(Map<String, RouteMeta> atlas) {
    atlas.put("/image/imageActivity", RouteMeta.build(RouteType.ACTIVITY, MainActivity.class, "/image/imageactivity", "image", new java.util.HashMap<String, Integer>(){{put("tip", 8); }}, -1, -2147483648));
  }
}

我们之前@Route注解的/image/imageActivity,调用loadInto,将该映射信息载入内存

Providers

public class ARouter$$Providers$$imagemodule implements IProviderGroup {
  @Override
  public void loadInto(Map<String, RouteMeta> providers) {
  }
}

代表定义的IProvider,我没有定义IProvider接口,所以这几loadInto是空方法

Root

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

该类将image组下的类信息保存起来,也就是上图中的ARouter$Group$Image类,后续可以有用到具体跳转时候再找到ARouter$Group$image类,然后调用loadInto,避免初始化时候一股脑将所有映射类加载到内存。

问题1:app如何找到不同模块Arouter生成的类的?

答案:通过扫描dex文件,找到目标包名的所有类 看一下初始化过程,最终走到LogisticsCenter的init

public synchronized static void init(Context context, ThreadPoolExecutor tpe) throws HandlerException {
	//省略代码
	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 HashSet<String>()));
                }
//省略代码
}

ClassUtils.getFileNamePackageName(),这个就是从dex文件查找目标类的过程,匹配条件是
ROUTE_ROOT_PAKCAGE 包名开头的类
ROUTE_ROOT_PAKCAGE = com.alibaba.android.arouter.routes
可以看到这个包名就是所有ARouter通过注解处理器生成的类的包名,可以看到上图build下的文件目录,生成类的包名就是这个,再看ClassUtil的查找过程

 public static Set<String> getFileNameByPackageName(Context context, final String packageName) throws PackageManager.NameNotFoundException, IOException, InterruptedException {
        final Set<String> classNames = new HashSet<>();

        List<String> paths = getSourcePaths(context);
        final CountDownLatch parserCtl = new CountDownLatch(paths.size());

        for (final String path : paths) {
            DefaultPoolExecutor.getInstance().execute(new Runnable() {
                @Override
                public void run() {
                    DexFile dexfile = null;

                    try {
                        if (path.endsWith(EXTRACTED_SUFFIX)) {
                            //NOT use new DexFile(path), because it will throw "permission error in /data/dalvik-cache"
                            dexfile = DexFile.loadDex(path, path + ".tmp", 0);
                        } else {
                            dexfile = new DexFile(path);
                        }

                        Enumeration<String> dexEntries = dexfile.entries();
                        while (dexEntries.hasMoreElements()) {
                            String className = dexEntries.nextElement();
                            if (className.startsWith(packageName)) {
                                classNames.add(className);
                            }
                        }
                    } catch (Throwable ignore) {
                        Log.e("ARouter", "Scan map file in dex files made error.", ignore);
                    } finally {
                        if (null != dexfile) {
                            try {
                                dexfile.close();
                            } catch (Throwable ignore) {
                            }
                        }

                        parserCtl.countDown();
                    }
                }
            });
        }
        //省略代码
        return classNames;
    }

可以清晰看到,这个过程就是在遍历Dex文件中的Class文件,找过所有目标类后返回目标类名的列表

回到LogisticsCenter的init方法,看下后续步骤

问题2:ARouter是直接将所有注解路由加载进内存吗?

答案:不是,ARouter一开始只是把分组信息,拦截器和Provider加载进来,并没有调用ARouter$Group$XX类的loadInto方法,使用到具体跳转导航,再把分组下的注册跳转信息通过loadInto加载到内存。

for (String className : routerMap) {
                    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);
                    } else if (className.startsWith(ROUTE_ROOT_PAKCAGE + DOT + SDK_NAME + SEPARATOR + SUFFIX_INTERCEPTORS)) {
                        // Load interceptorMeta
                        ((IInterceptorGroup) (Class.forName(className).getConstructor().newInstance())).loadInto(Warehouse.interceptorsIndex);
                    } else if (className.startsWith(ROUTE_ROOT_PAKCAGE + DOT + SDK_NAME + SEPARATOR + SUFFIX_PROVIDERS)) {
                        // Load providerIndex
                        ((IProviderGroup) (Class.forName(className).getConstructor().newInstance())).loadInto(Warehouse.providersIndex);
                    }
                }

routerMap就是之前查找dex文件找到的目标类类名的集合,遍历集合,然后通过反射调用目标类loadInto方法,

  1. 调用ARouter$Group$xx的loadInto,将组信息加载进内存
  2. 调用ARouter$Interceptors$xx,将拦截器信息加载进内存
  3. 调用ARouter$Providers$xx,将 IProvider信息加载进内存 调用的都是ARouter注解处理器生成的文件
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值