一图抵千言《ARouter简明扼要原理分析》

2022-03-29 15:15:04 浏览数 (1)

配置

Kotlin项目:

module App:

代码语言:javascript复制
apply plugin: 'kotlin-kapt'

defaultConfig{
 javaCompileOptions {
   annotationProcessorOptions {
   //AROUTER_MODULE_NAME必配项 用于拼接生成文件名 AROUTER_GENERATE_DOC 
   // AROUTER_GENERATE_DOC = enable 生成Json文档
   // 生成的文档路径 : build/generated/source/apt/(debug or release)/com/alibaba/android/arouter/docs/arouter-map-of-${moduleName}.json
   arguments = [AROUTER_MODULE_NAME:project_name,AROUTER_GENERATE_DOC:"enable"]
     }
   }
}

dependencies{
  api 'com.alibaba:arouter-api:1.5.0'
  kapt 'com.alibaba:arouter-compiler:1.2.2'
}
代码语言:javascript复制
//项目根目录build.gradle
dependencies {
  classpath "com.alibaba:arouter-register:1.0.2"
}

源码流程分析

三个关键阶段

ARouter源码分析.png

自定义处理器工作流程:

整体流程.png

自定义处理器源码分析:结构图

ARoute注解源码解析-1.png

生成类的关系

调用类:

代码语言:javascript复制
@Route(path = "/kotlin/test")
class KotlinTestActivity : Activity() {

    @Autowired
    @JvmField var name: String? = null
    @Autowired
    @JvmField var age: Int? = 0

    override fun onCreate(savedInstanceState: Bundle?) {
        ARouter.getInstance().inject(this)  // Start auto inject.

        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_kotlin_test)

        content.text = "name = $name, age = $age"
    }
}

ARouter生成类:

代码语言:javascript复制
public class KotlinTestActivity$$ARouter$$Autowired implements ISyringe {
  private SerializationService serializationService;

  @Override
  public void inject(Object target) {
    serializationService = ARouter.getInstance().navigation(SerializationService.class);
    KotlinTestActivity substitute = (KotlinTestActivity)target;
    substitute.name = substitute.getIntent().getExtras() == null ? substitute.name : substitute.getIntent().getExtras().getString("name", substitute.name);
    substitute.age = substitute.getIntent().getIntExtra("age", substitute.age);
  }
}

ARouter.getInstance().inject(this)

这段代码最终会利用当前类名和规则,拼接成KotlinTestActivity$$ARouter$$Autowired的全类名,然后利用反射传进对象。然后执行inject(this); 然后里面会初始化传输字段序列化服务,然后强转target,开始赋值数据

生成类文件的关系

RouteProcessor生成文件.png

由此可总结出下面整体工作流程

ARouter整体工作流程

整体工作流程.png

运行时原理分析

初始化工作流程分析
代码语言:javascript复制
  //初始化
  ARouter.init(getApplication());
  
  _ARouter.init(application)   

  LogisticsCenter.init(mContext, executor)  

LogisticsCenter.init(mContext, executor):

代码语言:javascript复制
public synchronized static void init(Context context, ThreadPoolExecutor tpe) throws HandlerException {
        mContext = context;
        executor = tpe;

        try {
            long startInit = System.currentTimeMillis();
            //load by plugin first
            loadRouterMap();
            if (registerByPlugin) {
                logger.info(TAG, "Load router map by arouter-auto-register plugin.");
            } else {
                //1 生成文件所有文件的全类名字符串集合
                Set<String> routerMap;

                // It will rebuild router map every times when debuggable.
                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.
                    //2. 赋值集合
                    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>()));
                }

                logger.info(TAG, "Find router map finished, map size = "   routerMap.size()   ", cost "   (System.currentTimeMillis() - startInit)   " ms.");
                startInit = System.currentTimeMillis();

                 //3. 遍历集合
                for (String className : routerMap) {
                // 这里装在了3中类别的文件: (1) Root文件
                    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);
                        //(2) Interceptor 文件
                    } else if (className.startsWith(ROUTE_ROOT_PAKCAGE   DOT   SDK_NAME   SEPARATOR   SUFFIX_INTERCEPTORS)) {
                        // Load interceptorMeta
                        ((IInterceptorGroup) (Class.forName(className).getConstructor().newInstance())).loadInto(Warehouse.interceptorsIndex);
                         // //(3) Provider 文件  
                    } else if (className.startsWith(ROUTE_ROOT_PAKCAGE   DOT   SDK_NAME   SEPARATOR   SUFFIX_PROVIDERS)) {
                        // Load providerIndex
                        ((IProviderGroup) (Class.forName(className).getConstructor().newInstance())).loadInto(Warehouse.providersIndex);
                    }
                }
            }

            logger.info(TAG, "Load root element finished, cost "   (System.currentTimeMillis() - startInit)   " ms.");

            if (Warehouse.groupsIndex.size() == 0) {
                logger.error(TAG, "No mapping files were found, check your configuration please!");
            }

            if (ARouter.debuggable()) {
                logger.debug(TAG, String.format(Locale.getDefault(), "LogisticsCenter has already been loaded, GroupIndex[%d], InterceptorIndex[%d], ProviderIndex[%d]", Warehouse.groupsIndex.size(), Warehouse.interceptorsIndex.size(), Warehouse.providersIndex.size()));
            }
        } catch (Exception e) {
            throw new HandlerException(TAG   "ARouter init logistics center exception! ["   e.getMessage()   "]");
        }
    }

可以看出没加载AutoWired文件,也就是说@AutoWired注解字段 在inject()时去创建对象赋值的。 反射找到对象并将Warehouse中的结合作为参数传递进入,把信息装载到内存。注意,这里只是加载了信息,但信息里面的具体内容并未创建。什么意思呢?以Provider为例:

代码语言:javascript复制
public class ARouter$$Providers$$modulejava implements IProviderGroup {
  @Override
  public void loadInto(Map<String, RouteMeta> providers) {
    providers.put("com.alibaba.android.arouter.demo.service.HelloService", RouteMeta.build(RouteType.PROVIDER, HelloServiceImpl.class, "/yourservicegroupname/hello", "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.demo.module1.testservice.SingleService", RouteMeta.build(RouteType.PROVIDER, SingleService.class, "/yourservicegroupname/single", "yourservicegroupname", null, -1, -2147483648));
  }
}

反射生成ARouter$$Providers$$modulejava对象,但我们用的是com.alibaba.android.arouter.demo.service.HelloServiceHelloService这个具体类。但这个类这是并未实例化,只有用到的时候才回去实例化创建。其他同理。

LogisticsCenter.init(mContext, executor): 还用到两个重要的类和方法: ClassUtils/Warehouse 1. ClassUtils.getFileNameByPackageName(mContext, ROUTE_ROOT_PAKCAGE);

代码语言:javascript复制
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() {
                //Dex文件
                    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();
                            //核心判断 packageName是我们上面传入的参数ROUTE_ROOT_PAKCAGE = "com.alibaba.android.arouter.routes"
                            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();
                    }
                }
            });
        }

        parserCtl.await();

        Log.d(Consts.TAG, "Filter "   classNames.size()   " classes by packageName <"   packageName   ">");
        return classNames;
    }

如果初始化加载流程没有走完,路由操作将会阻塞,直到加载流程完成

Warehouse 相当于一个加载信息装载的容器类

代码语言:javascript复制
class Warehouse {
    // Cache route and metas  
    //groupsIndex 装载ARouter$$Root$$moduleName 中的Root文件  <groupName,Group.class>
    static Map<String, Class<? extends IRouteGroup>> groupsIndex = new HashMap<>();
    //routes 按需加载完成后 把加载的数据存到routes 集合中,等项目中用到的时候,找到集合中需要的元素 再去实例化对象
    static Map<String, RouteMeta> routes = new HashMap<>();

    // Cache provider
   //项目中用到的时候,找到集合中需要的元素 再去实例化对象 
    static Map<Class, IProvider> providers = new HashMap<>();
    // //每个服务的原始信息加载完成后存放到这里
    static Map<String, RouteMeta> providersIndex = new HashMap<>();

    // Cache interceptor   
    static Map<Integer, Class<? extends IInterceptor>> interceptorsIndex = new UniqueKeyTreeMap<>("More than one interceptors use same priority [%s]");
    static List<IInterceptor> interceptors = new ArrayList<>();

    static void clear() {
        routes.clear();
        groupsIndex.clear();
        providers.clear();
        providersIndex.clear();
        interceptors.clear();
        interceptorsIndex.clear();
    }
}

整体工作流程.png

路由过程源码分析
代码语言:javascript复制
    ARouter.getInstance()
                        .build("/kotlin/test")
                        .withString("name", "老王")
                        .withInt("age", 23)
                        .navigation();

.build("/kotlin/test") 查找分组,构建Postcard对象。

代码语言:javascript复制
   protected Postcard build(String path) {
        if (TextUtils.isEmpty(path)) {
            throw new HandlerException(Consts.TAG   "Parameter is invalid!");
        } else {
        // 重定向路由路径Service 如果想自定义 则实现PathReplaceService 
            PathReplaceService pService = ARouter.getInstance().navigation(PathReplaceService.class);
            if (null != pService) {
            //通过forString(path) 返回修改后的Path
                path = pService.forString(path);
            }
            //继续往下走 
            return build(path, extractGroup(path), true);
        }
    }   

extractGroup(path):

代码语言:javascript复制
 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 {
        //关键代码 defaultGroup 默认分组 以路由路径 第一个节点为分组名称
            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;
        }
    }

build(path, extractGroup(path), true);:

代码语言:javascript复制
  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);
        }
    }

创建:Postcard对象

.withString("name", "老王").withInt("age", 23)

构建参数mBundle对象。

.navigation(); 运行在异步线程中

代码语言:javascript复制
 protected Object navigation(final Context context, final Postcard postcard, final int requestCode, final NavigationCallback callback) {
        PretreatmentService pretreatmentService = ARouter.getInstance().navigation(PretreatmentService.class);
        if (null != pretreatmentService && !pretreatmentService.onPretreatment(context, postcard)) {
            // Pretreatment failed, navigation canceled.
            return null;
        }

        // Set context to postcard.
        postcard.setContext(null == context ? mContext : context);

        try {
        //给Postcard赋值其他数据
            LogisticsCenter.completion(postcard);
        } catch (NoRouteFoundException ex) {//出现异常的情况
            logger.warning(Consts.TAG, ex.getMessage());

            if (debuggable()) {
                // Show friendly tips for user.
                runInMainThread(new Runnable() {
                    @Override
                    public void run() {
                        Toast.makeText(mContext, "There's no route matched!n"  
                                " Path = ["   postcard.getPath()   "]n"  
                                " Group = ["   postcard.getGroup()   "]", Toast.LENGTH_LONG).show();
                    }
                });
            }

            if (null != callback) {
            // 路由回调 如果不为空
                callback.onLost(postcard);
            } else {
                // No callback for this invoke, then we use the global degrade service.
                //反射创建全局降级服务  回调 onLost方法
                DegradeService degradeService = ARouter.getInstance().navigation(DegradeService.class);
                if (null != degradeService) {
                    degradeService.onLost(context, postcard);
                }
            }

            return null;
        }

        if (null != callback) {
        //回调路由成功的方法
            callback.onFound(postcard);
        }
         //如果绿色通道为false 则添加拦截器 
        if (!postcard.isGreenChannel()) {   // It must be run in async thread, maybe interceptor cost too mush time made ANR.
        // 运行在线程池中
            interceptorService.doInterceptions(postcard, new InterceptorCallback() {
                /**
                 * Continue process
                 *
                 * @param postcard route meta
                 */
                @Override
                public void onContinue(Postcard postcard) {
                //继续处理完成后继续向下执行 
                    _navigation(postcard, requestCode, callback);
                }

                /**
                 * Interrupt process, pipeline will be destory when this method called.
                 *
                 * @param exception Reson of interrupt.
                 */
                @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(postcard, requestCode, callback);
        }

        return null;
    }

LogisticsCenter.completion(postcard);

代码语言:javascript复制
public synchronized static void completion(Postcard postcard) {
        if (null == postcard) {
            throw new NoRouteFoundException(TAG   "No postcard!");
        }
        //routes是加载信息 但为实例化对象 之前提过  所以这里第一次肯定为空
        RouteMeta routeMeta = Warehouse.routes.get(postcard.getPath());
        if (null == routeMeta) {
            // Maybe its does't exist, or didn't load.
            if (!Warehouse.groupsIndex.containsKey(postcard.getGroup())) {
                throw new NoRouteFoundException(TAG   "There is no route match the path ["   postcard.getPath()   "], in group ["   postcard.getGroup()   "]");
            } else {
                // Load route and cache it into memory, then delete from metas.
                try {
                    if (ARouter.debuggable()) {
                        logger.debug(TAG, String.format(Locale.getDefault(), "The group [%s] starts loading, trigger by [%s]", postcard.getGroup(), postcard.getPath()));
                    }
                    //对Warehouse.routes初始化 实例化分组对象 获取数据
                    addRouteGroupDynamic(postcard.getGroup(), null);

                    if (ARouter.debuggable()) {
                        logger.debug(TAG, String.format(Locale.getDefault(), "The group [%s] has already been loaded, trigger by [%s]", postcard.getGroup(), postcard.getPath()));
                    }
                } catch (Exception e) {
                    throw new HandlerException(TAG   "Fatal exception when loading group meta. ["   e.getMessage()   "]");
                }
                //重新调用自己
                completion(postcard);   // Reload
            }
        } else {
        //给postcard赋值其他信息
            postcard.setDestination(routeMeta.getDestination());
            postcard.setType(routeMeta.getType());
            postcard.setPriority(routeMeta.getPriority());
            postcard.setExtra(routeMeta.getExtra());

            Uri rawUri = postcard.getUri();
            if (null != rawUri) {   // Try to set params into bundle.
                Map<String, String> resultMap = TextUtils.splitQueryParameters(rawUri);
                Map<String, Integer> paramsType = routeMeta.getParamsType();

                if (MapUtils.isNotEmpty(paramsType)) {
                    // Set value by its type, just for params which annotation by @Param
                    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());
            }
             //类型判断 PROVIDER类型实例化对象 PROVIDER/FRAGMENT默认开启通道,不经过拦截
            switch (routeMeta.getType()) {
                case PROVIDER:  // if the route is provider, should find its instance
                    // Its provider, so it must implement IProvider
                    Class<? extends IProvider> providerMeta = (Class<? extends IProvider>) routeMeta.getDestination();
                    IProvider instance = Warehouse.providers.get(providerMeta);
                    if (null == instance) { // There's no instance of this provider
                        IProvider provider;
                        try {
                            provider = providerMeta.getConstructor().newInstance();
                            provider.init(mContext);
                            Warehouse.providers.put(providerMeta, provider);
                            instance = provider;
                        } catch (Exception e) {
                            logger.error(TAG, "Init provider failed!", e);
                            throw new HandlerException("Init provider failed!");
                        }
                    }
                    postcard.setProvider(instance);
                    postcard.greenChannel();    // Provider should skip all of interceptors
                    break;
                case FRAGMENT:
                    postcard.greenChannel();    // Fragment needn't interceptors
                default:
                    break;
            }
        }
    }

 public synchronized static void addRouteGroupDynamic(String groupName, IRouteGroup group) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
        if (Warehouse.groupsIndex.containsKey(groupName)){
            // If this group is included, but it has not been loaded
            // load this group first, because dynamic route has high priority.
            //对Warehouse.routes初始化 实例化分组对象 获取数据
            Warehouse.groupsIndex.get(groupName).getConstructor().newInstance().loadInto(Warehouse.routes);
            Warehouse.groupsIndex.remove(groupName);
        }

        // cover old group.
        if (null != group) {
            group.loadInto(Warehouse.routes);
        }
    }

_navigation(postcard, requestCode, callback);

代码语言:javascript复制
  private Object _navigation(final Postcard postcard, final int requestCode, final NavigationCallback callback) {
        final Context currentContext = postcard.getContext();

//判断路由类型
        switch (postcard.getType()) {
            case ACTIVITY:
                // Build intent
                final Intent intent = new Intent(currentContext, postcard.getDestination());
                intent.putExtras(postcard.getExtras());

                // Set flags.
                int flags = postcard.getFlags();
                if (0 != flags) {
                    intent.setFlags(flags);
                }

                // Non activity, need FLAG_ACTIVITY_NEW_TASK
                if (!(currentContext instanceof Activity)) {
                //context如果是Application 那么新建任务栈
                    intent.addFlags(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() {
                    //Activity则调用startActivity
                        startActivity(requestCode, currentContext, intent, postcard, callback);
                    }
                });

                break;
            case PROVIDER:
            //返回LogisticsCenter.completion(postcard)方法中创建的对象
                return postcard.getProvider();
            case BOARDCAST:
            case CONTENT_PROVIDER:
            case FRAGMENT:
                Class<?> fragmentMeta = postcard.getDestination();
                try {
                    Object instance = fragmentMeta.getConstructor().newInstance();
                    if (instance instanceof Fragment) {
                        ((Fragment) instance).setArguments(postcard.getExtras());
                    } else if (instance instanceof android.support.v4.app.Fragment) {
                        ((android.support.v4.app.Fragment) instance).setArguments(postcard.getExtras());
                    }
 //反射返回fragment实例
                    return instance;
                } catch (Exception ex) {
                    logger.error(Consts.TAG, "Fetch fragment instance error, "   TextUtils.formatStackTrace(ex.getStackTrace()));
                }
            case METHOD:
            case SERVICE:
            default:
                return null;
        }

        return null;
    }

路由结束。

整体工作流程(2).png

图片.png

0 人点赞