Android之旅 -- ARouter 源码分析(一)

《Android之旅 -- ARouter 源码分析(一)》 美景总是好的

Android studio工程的结构

《Android之旅 -- ARouter 源码分析(一)》 Android Studio 目录结构

  • app — demo application
  • arouter-annotation — 注解相关的声明, 其他工程都要依赖这个
  • arouter-api — ARouter 框架的 API
  • arouter-compiler — 编译期对注解分析的库
  • module-java module-kotlin — demo 的子模块

初始化 ARouter

《Android之旅 -- ARouter 源码分析(一)》 初始化的序列图

// XXXXXActivity(Application)
ARouter.init(getApplication());

来看下 ARouter.init 的实现, 都很简单的直接调用了_ARouter.java

// ARouter.java
public static void init(Application application) {
    ...
    hasInit = _ARouter.init(application);
    ...
}

继续看 _ARouter.java 实现

// _ARouter.java
protected static synchronized boolean init(Application application) {
    mContext = application;

    // 实际初始化的地方
    LogisticsCenter.init(mContext, executor);

    hasInit = true;
    return true;
}

主要实现都在 LogisticsCenter.init 方法 中, 继续查看

// LogisticsCenter.java
public synchronized static void init(Context context, ThreadPoolExecutor tpe) throws HandlerException {
    // ROUTE_ROOT_PAKCAGE is com.alibaba.android.arouter.routes
    // ClassUtils.getFileNameByPackageName 就是根据报名查找对应报名下的类, 就不贴代码了..
    List<String> classFileNames = ClassUtils.getFileNameByPackageName(mContext, ROUTE_ROOT_PAKCAGE);

    // 遍历获取到的 class
    for (String className : classFileNames) {

        // check start with com.alibaba.android.arouter.routes.ARouter$$Root
        if (className.startsWith(ROUTE_ROOT_PAKCAGE + DOT + SDK_NAME + SEPARATOR + SUFFIX_ROOT)) {
            ((IRouteRoot) (Class.forName(className).getConstructor().newInstance())).loadInto(Warehouse.groupsIndex);

        // check start with com.alibaba.android.arouter.routes.ARouter$$Interceptors
        } else if (className.startsWith(ROUTE_ROOT_PAKCAGE + DOT + SDK_NAME + SEPARATOR + SUFFIX_INTERCEPTORS)) {
            ((IInterceptorGroup) (Class.forName(className).getConstructor().newInstance())).loadInto(Warehouse.interceptorsIndex);
        
        // check start with com.alibaba.android.arouter.routes.ARouter$$Providers
        } else if (className.startsWith(ROUTE_ROOT_PAKCAGE + DOT + SDK_NAME + SEPARATOR + SUFFIX_PROVIDERS)) {
            ((IProviderGroup) (Class.forName(className).getConstructor().newInstance())).loadInto(Warehouse.providersIndex);
        }
    }
}

可以看到初始化就是查找com.alibaba.android.arouter.routes包下的类, 获取实例并强制转化成IRouteRoot, IInterceptorGroup, IProviderGroup, 然后调用 loadInto 方法.

通过 demo 的代码查找能看到有com.alibaba.android.arouter.routes.ARouter$$Root$$app 这样的类

// ARouter$$Root$$app.java
/**
 * DO NOT EDIT THIS FILE!!! IT WAS GENERATED BY AROUTER. */
public class ARouter$$Root$$app implements IRouteRoot {
    public void loadInto(Map<String, Class<? extends IRouteGroup>> routes) {
        // 以分组做为 key, 缓存到 routes. 
        // routes 是指向 Warehouse.groupsIndex 的引用
        routes.put("service", ARouter$$Group$$service.class);
        routes.put("test", ARouter$$Group$$test.class);
    }
}

可以看到这是在编译期通过分析注解生成的代码. ARouter$$Group$$service.class 也是生成的.

// ARouter$$Group$$service.java
/**
 * DO NOT EDIT THIS FILE!!! IT WAS GENERATED BY AROUTER. */
public class ARouter$$Group$$service implements IRouteGroup {
    @Override
    public void loadInto(Map<String, RouteMeta> atlas) {
        atlas.put("/service/hello", RouteMeta.build(RouteType.PROVIDER, HelloServiceImpl.class, "/service/hello", "service", null, -1, -2147483648));
        ...
    }
}

到这里可以看到demo代码里定义的HelloServiceImpl.java 等出现了. 其实 ARouter 就是通过分析注解在编译期自动生成了一些关联代码. 另外的 Interceptors, Providers 逻辑上类似.

Interceptors 是注册了声明 Interceptor 注解, 并实现 IInterceptor 接口的类
Providers 是注册了声明 Route 注解, 并实现了 IProvider 接口的类

到此初始化工作都做完了, 总结一下 ARouter 会在编译期根据注解声明分析自动生成一些注入代码, 初始化工作主要是把这些注解的对象和对注解的配置缓存到 Warehouse 的静态对象中.

从Activity跳转调用开始分析源码

《Android之旅 -- ARouter 源码分析(一)》 navigation序列图

// 跳转activity的调用
ARouter.getInstance().build("/test/activity2").navigation();
// ARouter.java 
public Postcard build(String path) {
    return _ARouter.getInstance().build(path);
}
// _ARouter.java
// group 默认是传进来的 path 第一部分内容. 例如 path = /test/activity1, group会默认为 test
// 如果手动声明的,一定要手动传递, 不然会找不到

protected Postcard build(String path, String group) {
    return new Postcard(path, group);
}

这里就是直接返回了一个 Postcard 对象, 并保存了path, group. Postcard 是继承了 RouteMeta
navigation方法最后都要调用的 _ARouter.java 中, 中间过程省略.我们直接看核心代码

// _ARouter.java
// postcard 就是前面用build 方法构造的对象实例
// requestCode 是区分 startAcitivity 的方式.如果不为-1, 就用startActivityForResult的方式启动
// NavigationCallback 是对各种状态的回调. 
protected Object navigation(final Context context, final Postcard postcard, final int requestCode, final NavigationCallback callback) {
    try {
        // 验证是否能找到对应的 postcard.path
        LogisticsCenter.completion(postcard);
    } catch (NoRouteFoundException ex) {
        // 如果没找到postcard的配置, 调用onLost回调方法, 或者系统配置的"降级服务"(DegradeService)回调
        if (null != callback) {
            callback.onLost(postcard);
        } else {    
            DegradeService degradeService = ARouter.getInstance().navigation(DegradeService.class);
            if (null != degradeService) {
                degradeService.onLost(context, postcard);
            }
        }
        return null;
    }

    ... 
 }

navigation调用了LogisticsCenter.completion方法做验证, 我们看下 LogisticsCenter.java 这个方法如何验证 postcard, 然后再继续看navigation方法下面的逻辑

// LogisticsCenter.java
public synchronized static void completion(Postcard postcard) {
    // 通过postcard 的 path 查找对应的 RouteMeta
    RouteMeta routeMeta = Warehouse.routes.get(postcard.getPath());

    if (null == routeMeta) {
        // 如果没找到, 可能是还没装载过, 需要根据 group 查找对应的 groups
        Class<? extends IRouteGroup> groupMeta = Warehouse.groupsIndex.get(postcard.getGroup()); 
        if (null == groupMeta) {
            // 如果没找到, 抛出 NoRouteFoundException 错误方法结束
            throw new NoRouteFoundException(TAG + "There is no route match the path [" + postcard.getPath() + "], in group [" + postcard.getGroup() + "]");

        } else {
            // 返回 IRouteGroup 对象, 并调用 loadInto方法.
            IRouteGroup iGroupInstance = groupMeta.getConstructor().newInstance();
            iGroupInstance.loadInto(Warehouse.routes);

            // 删除 group 缓存
            Warehouse.groupsIndex.remove(postcard.getGroup());

            // 重新调用 completion 方法,此时对应的 group 已经缓存完成
            completion(postcard);   // Reload
        }

    } else {
        // 可以查找到 routeMeta, copy routeMeta 的原始数据到 postcard 中.
        postcard.setDestination(routeMeta.getDestination());
        postcard.setType(routeMeta.getType());
        postcard.setPriority(routeMeta.getPriority());
        postcard.setExtra(routeMeta.getExtra());

        switch (routeMeta.getType()) {
        case PROVIDER: 
            Class<? extends IProvider> providerMeta = (Class<? extends IProvider>) routeMeta.getDestination();
            IProvider instance = Warehouse.providers.get(providerMeta);

            // 初始化 provider 对象, 并调用初始化方法, 缓存到Warehouse.providers中.
            if (null == instance) {
                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());
                }
            }
            
            // 设置一个provider 引用
            postcard.setProvider(instance);

            // provider 默认设置跳过拦截器
            postcard.greenChannel(); 
            break;
        case FRAGMENT:
            // fragment 默认设置跳过拦截器
            postcard.greenChannel(); 
        default:
            break;
        }
    }
}

completion方法主要是对 group 做一次懒式加载, 我们继续查看 navigation 方法下面的内容.

protected Object navigation(final Context context, final Postcard postcard, final int requestCode, final NavigationCallback callback) {
    ...

    // 执行到这里就是找到了 postcard. 执行对应回调
    if (null != callback) {
        callback.onFound(postcard);
    }

    // 如果是"绿色通道", 则直接执行_navigation方法, 目前只有provider, fragment 默认是走绿色通道
    if (!postcard.isGreenChannel()) { 
        // interceptorService 是 ARouter 配置的默认的拦截服务
        interceptorService.doInterceptions(postcard, new InterceptorCallback() {

            public void onContinue(Postcard postcard) {
                _navigation(context, postcard, requestCode, callback);
            }

            public void onInterrupt(Throwable exception) {
                if (null != callback) {
                    callback.onInterrupt(postcard);
                }
            }
        });
    } else {
        // 绿色通道
        return _navigation(context, postcard, requestCode, callback);
    }
    return null;
 }

interceptorService 是 ARouter 配置的默认的拦截器com.alibaba.android.arouter.core.InterceptorServiceImpl.java

public void doInterceptions(final Postcard postcard, final InterceptorCallback callback) {
    // 用线程池异步执行
    LogisticsCenter.executor.execute(new Runnable() {
        public void run() {
            // 初始化一个同步计数类, 用拦截器的 size
            CancelableCountDownLatch interceptorCounter = new CancelableCountDownLatch(Warehouse.interceptors.size());
            try {
                // 执行拦截操作, 看到这猜是递归调用.直到 index 满足条件, 退出递归.
                _excute(0, interceptorCounter, postcard);
            
                // 线程等待计数完成, 等待300秒...
                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);
            }
        }
    });
}

我们继续看看_excute方法

private static void _excute(final int index, final CancelableCountDownLatch counter, final Postcard postcard) {
    // 递归退出条件
    if (index < Warehouse.interceptors.size()) {
        // 获取要执行的拦截器
        IInterceptor iInterceptor = Warehouse.interceptors.get(index);

        // 执行拦截
        iInterceptor.process(postcard, new InterceptorCallback() {

            public void onContinue(Postcard postcard) {
                // 计数器减1
                counter.countDown();
                
                // 继续执行下一个拦截
                _excute(index + 1, counter, postcard);  
            }

            public void onInterrupt(Throwable exception) {
                // 当被拦截后退出递归
                postcard.setTag(null == exception ? new HandlerException("No message.") : exception.getMessage()); 
                counter.cancel();
            }
        });
    }
}

和我们猜测的一样, 一个标准的递归调用, 当所有拦截器执行后(假设都不做拦截), 最后还是要回到_navigation方法

private Object _navigation(final Context context, final Postcard postcard, final int requestCode, final NavigationCallback callback) {
    final Context currentContext = null == context ? mContext : context;

    // 我们就先只分析 activity 的逻辑
    switch (postcard.getType()) {
    case ACTIVITY:
        // 初始化 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)) { 
            intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
        }

         // 在 UI 线程进行 start activity
        new Handler(Looper.getMainLooper()).post(new Runnable() {
            public void run() {
                if (requestCode > 0) {  // Need start for result
                    ActivityCompat.startActivityForResult((Activity) currentContext, intent, requestCode, postcard.getOptionsBundle());
                } else {
                    ActivityCompat.startActivity(currentContext, intent, postcard.getOptionsBundle());
                }

                // 动画设置
                if ((0 != postcard.getEnterAnim() || 0 != postcard.getExitAnim()) && currentContext instanceof Activity) {    // Old version.
                    ((Activity) currentContext).overridePendingTransition(postcard.getEnterAnim(), postcard.getExitAnim());
                }

                if (null != callback) { // Navigation over.
                    callback.onArrival(postcard);
                }
            }
        });
        break;
    }
    return null;
}

对 IProvider 进行 navigation

主要实现是在LogisticsCenter.completion方法中对IProvider进行了一些分支处理

        switch (routeMeta.getType()) {
        case PROVIDER: 
            // 返回实现IProvider接口的类
            Class<? extends IProvider> providerMeta = (Class<? extends IProvider>) routeMeta.getDestination();

            // 在缓存中查找
            IProvider instance = Warehouse.providers.get(providerMeta);

            // 初始化 provider 对象, 并调用初始化方法, 缓存到Warehouse.providers中.
            if (null == instance) {
                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());
                }
            }
            
            // 设置一个provider 引用
            postcard.setProvider(instance);

            // provider 默认设置跳过拦截器
            postcard.greenChannel(); 
            break;
    // 可以看 _navigation 方法就是直接返回在 completion 方法中是设置的引用
    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;
    }

好了.到这里我们已经知道了 ARouter 大概的已经工作流程了, 总结一下.
初始化的时候把注解标示的内容注入到缓存中, 然后启动跳转的时候根据缓存查找对应的类的实现. 看上去也是挺简单的, 主要的黑科技其实也就是在编译期生成代码. 下一步我们继续会看下编译期到底做了哪些内容.

    原文作者:程序猿老麦
    原文地址: https://www.jianshu.com/p/9c452e01ca50
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞