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

美景总是好的

Android studio工程的结构

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

初始化 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跳转调用开始分析源码

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

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,937评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,503评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,712评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,668评论 1 276
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,677评论 5 366
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,601评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,975评论 3 396
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,637评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,881评论 1 298
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,621评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,710评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,387评论 4 319
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,971评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,947评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,189评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 44,805评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,449评论 2 342

推荐阅读更多精彩内容