整理:ARouter原理

  • Post author:
  • Post category:其他



总结:

ARouter 是通过注解的方式结合android提供的启动Activity的API实现页面的跳转及参数的传递的。


ARouter 提供三种注解类型:

@Route:注解跳转页面或是需要实例化的的类

@Interceptor:注解自定义拦截器

@AutoWired:注解需要自动赋值的成员变量


ARouter的工作过程可以分为三个部分:

(1)编译期通过注解处理器生成相关的中间类;

(2)程序启动时初始化ARouter;

(3)通过ARouter实例解析路由地址实现页面跳转及参数专递。


在编译期会生成几个关键的中间类:

(1)IRouteRoot接口的实现类:

每个moudle下都会生成一个该类型的实现类,通过module名来区分的,该类的作用是将每个module下所有的分组按照组名和对应分组的实现类(IRouteGroup接口的实现类)的Class对象做一个映射,然后保存在一个全局的groupIndex的map表中。

/**
 * DO NOT EDIT THIS FILE!!! IT WAS GENERATED BY AROUTER. */
public class ARouter$$Root$$app implements IRouteRoot {
  @Override
  public void loadInto(Map<String, Class<? extends IRouteGroup>> routes) {
    routes.put("app", ARouter$$Group$$app.class);
    routes.put("yourservicegroupname", ARouter$$Group$$yourservicegroupname.class);
  }
}

(2)IInterceptorGroup接口的实现类:

该类的作用是将各个module下的自定义的interceptor 按照优先级和interceptor的Class对象做一个映射,然后保存在一个全局的interceptorIndex的map表中。

public interface IInterceptorGroup {
    /**
     * Load interceptor to input
     *
     * @param interceptor input
     */
    void loadInto(Map<Integer, Class<? extends IInterceptor>> interceptor);
}

(3)IProviderGroup接口的实现类:

该类的作用的是将项目中自定义的提供序列化功能的类的相关信息以RouteMeta类的对象保存在全局的providerIndex的map表中。

public interface IProviderGroup {
    /**
     * Load providers map to input
     *
     * @param providers input
     */
    void loadInto(Map<String, RouteMeta> providers);
}

(4)IRouteGroup接口的实现类:

该类的作用是将每个分组下所有route地址对应页面的相关信息分别封装在RouteMeta对象中,然后将route地址与对应的RouteMeta对象做一个映射保存在全局的routes map表中。

/**
 * DO NOT EDIT THIS FILE!!! IT WAS GENERATED BY AROUTER. */
public class ARouter$$Group$$app implements IRouteGroup {
  @Override
  public void loadInto(Map<String, RouteMeta> atlas) {
    atlas.put("/app/ble", RouteMeta.build(RouteType.ACTIVITY, BleBluetoothTestActivity.class, "/app/ble", "app", null, -1, -2147483648));
    atlas.put("/app/fake", RouteMeta.build(RouteType.ACTIVITY, FakeActivity.class, "/app/fake", "app", null, -1, -2147483648));
    atlas.put("/app/simpleFragment03", RouteMeta.build(RouteType.FRAGMENT, SimpleViewpagerFragment02.class, "/app/simplefragment03", "app", null, -1, -2147483648));
    atlas.put("/app/test01", RouteMeta.build(RouteType.ACTIVITY, Test01SchemeFilterActivity.class, "/app/test01", "app", null, -1, -2147483648));
    atlas.put("/app/test02", RouteMeta.build(RouteType.ACTIVITY, Test02SchemeFilterActivity.class, "/app/test02", "app", new java.util.HashMap<String, Integer>(){{put("obj", 11); put("name", 8); put("list", 11); put("boy", 0); put("map", 11); put("age", 3); }}, -1, -2147483648));
    atlas.put("/app/webview", RouteMeta.build(RouteType.ACTIVITY, TestWebViewActivity.class, "/app/webview", "app", null, -1, -2147483648));
  }
}


程序启动时初始化ARouter


初始化时ARouter的init()方法首先会在app内查找指定package包目录下所有class类文件,然后在这些class中对三种类型的class进行初始化:

(1)是IRouter接口的实现类;

(2)是IInterceptorGroup接口的实现类;

(3)是IProviderGroup的实现类;

BaseApplicationLike

 ARouter.init(this);

ARouter

 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:

(ARouter core (Facade patten) 这个核心类是使用外观模式来实现的)

 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

LogisticsCenter contains all of the map.

  1. Creates instance when it is first used.
  2. Handler Multi-Module relationship map(*)
  3. Complex logic to solve duplicate group definition
public synchronized static void init(Context context, ThreadPoolExecutor tpe) throws HandlerException {
        mContext = context;
        executor = tpe;

        try {
            long startInit = System.currentTimeMillis();
            //billy.qi modified at 2017-12-06
            //load by plugin first
            loadRouterMap();
            if (registerByPlugin) {
                logger.info(TAG, "Load router map by arouter-auto-register plugin.");
            } else {
                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.
                    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();

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

            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() + "]");
        }
    }

最终都是交由

LogisticsCenter

去通过反射的方式进行实例化并调用loadInto()方法进行初始化操作。


路由跳转流程解析

ARouter.getInstance().build(ARouterConstant.Infrastructure.A_ROUTER_TEST_ACTIVITY).navigation()

(1)首先获取ARouter的一个单例调用build()方法传入路由地址,

(2)build方法会根据路由地址获取所属的组名,并把路由地址和组名等信息封装在

PostCard

类型(继承RouteMeta类型的)的对象中返回,

(3)navigation()方法最后会调用到ARouter的navigation()方法,这个方法中会先做一些前置的工作,主要是通过调用

LogisticsCenter

类的静态方法

completion

()来完成的。

1.这个方法中会先通过路由地址在全局的routes表中查找对应页面信息是否存在,

2.如果存在就继续后面的逻辑,如果不存在也就是第一次遇到该分组的路由时,会继续根据组名在全局的groupIndex表中查找对应的分组实现类的Class对象(一般情况下只要初始化成功了,这个Class对象就一定会存在的,如果不存在就会抛异常),

3.然后通过反射的方式创建该类的实例并调用loadInto()方法将该组下所有的路由地址与对应页面的信息做一个映射保存在全局的routes表中,

4.再次调用自身方法completion()方法, Reload一下,也就是进入了else{}模块的代码。此时路由地址对应的页面信息有了,接下来会把对应的页面信息存到pastcard对象中,

5.然后根据postcard中存储的页面类型进行判断,这里判断了两种类型:IProvider类型和Fragment类型,

6. 如果是Provider类型会获取该类的Class对象通过反射的方式创建一个实例并存储在postcard的provider变量中,同时将该route地址对应的Class对象与该实例做一个映射保存在全局的providers的map表中,并设置其绿色通道标志位(绿色通道标志位:用于判断是否处理拦截器相关逻辑);

7.如果是Fragment类型则设置绿色通道标志位即可。

8.然后返回navigation()继续执行,接下来会根据postcard中存储的类型分别进行处理:

(1)如果是Activity类型的话,会创建一个intent设置设置相关参数及flag位,然后通过startActivity()方式启动目标页面;

(2)如果是Provider类型 就直接返回postcard中provider变量;

(3)如果是broadcastRecevier,service,contentProvider及fragment类型时,则使用该类的Class对象通过反射的方式创建一个实例,如果该实例是v4或是app包中的fragment的话则强转为对应类型,最后返回该实例;其他类型则返回null

第一步到第七步的源码

public synchronized static void completion(Postcard postcard) {
        if (null == postcard) {
            throw new NoRouteFoundException(TAG + "No postcard!");
        }

        RouteMeta routeMeta = Warehouse.routes.get(postcard.getPath());
        if (null == routeMeta) {    // Maybe its does't exist, or didn't load.
            Class<? extends IRouteGroup> groupMeta = Warehouse.groupsIndex.get(postcard.getGroup());  // Load route meta.
            if (null == groupMeta) {
                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()));
                    }

                    IRouteGroup iGroupInstance = groupMeta.getConstructor().newInstance();
                    iGroupInstance.loadInto(Warehouse.routes);
                    Warehouse.groupsIndex.remove(postcard.getGroup());

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

            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) {
                            throw new HandlerException("Init provider failed! " + e.getMessage());
                        }
                    }
                    postcard.setProvider(instance);
                    postcard.greenChannel();    // Provider should skip all of interceptors
                    break;
                case FRAGMENT:
                    postcard.greenChannel();    // Fragment needn't interceptors
                default:
                    break;
            }
        }
    }

第八步的源码

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());
                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;
            case PROVIDER:
                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());
                    }

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



版权声明:本文为wzj_what_why_how原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。