阿里巴巴开源路由框架 - ARouter 分析

Arouter 一个用于帮助 Android App 进行组件化改造的框架 —— 支持模块间的路由、通信、解耦

技术准备

  1. SPI
  2. APT

源码分析

我们直接看官方 demo,核心模块包括api,compiler,annotation, 首先先简单讲一下各模块的作用:

  • Annoation: 定义注解和 Route 相关的基本信息
  • Compiler: 主要是用来在编译期间处理注解Router/Interceptor/Autowire三个注解,在编译期间自动注册注解标注的类,成员变量等。
  • Api: 用户调用的核心 api

annoation

Autowired 用于注解 field, 可实现路由件传递参数的自动注入,无需使用者在代码中去获取传递的参数,注意,注解对象的修饰符不能是 private。

Interceptor 用于注解拦截器,拦截器的概念请参考官方文档

Route 路由注解,可以是 Activity,也可以是 Service

RouteMeta 路由相关的数据,包括路由类型,路径,组,权重,参数等信息

compiler

RouteProcessor

路由注解器

关键方法 #parseRoutes#,我们先看这个方法最后生成的文件到底是啥,弄清楚了才能更容易的理清代码。

public class ARouter$$Root$$app implements IRouteRoot {
    public ARouter$$Root$$app() {
    }

    public void loadInto(Map<String, Class<? extends IRouteGroup>> routes) {
        routes.put("service", service.class);
        routes.put("test", test.class);
    }
}
复制代码

注意map 存入的key 分别是 service 和 test,valude 则对应为 ARouter$$Group$$service.class 和 ARouter$$Group$$test.class。一个组件有且只有一个 Root 类,不同的组件以后缀作为区分。

public class ARouter$$Group$$test implements IRouteGroup {
    public ARouter$$Group$$test() {
    }

    public void loadInto(Map<String, RouteMeta> atlas) {
  		atlas.put("/test/*", RouteMeta)
    }
}
复制代码
public class ARouter$$Group$$service implements IRouteGroup {
    public ARouter$$Group$$service() {
    }

    public void loadInto(Map<String, RouteMeta> atlas) {
   		atlas.put("/service/*", RouteMeta)
    }
}
复制代码

注意 Arouter$$Group$$test 中 map 的 key 必然是以 "/test" 为前缀,刚好与其后缀相同

public class ARouter$$Providers$$app implements IProviderGroup {
    public ARouter$$Providers$$app() {
    }

    public void loadInto(Map<String, RouteMeta> providers) {
        providers.put(className, RouteMeta)
    }
}
复制代码
public class ARouter$$Interceptors$$app implements IInterceptorGroup {
    public ARouter$$Interceptors$$app() {
    }

    public void loadInto(Map<Integer, Class<? extends IInterceptor>> interceptors) {
        interceptors.put(privority, Class);
    }
}
复制代码

小结一下:

  1. 在一个module中,自动生成的 IRouteRoot 类有且只有一个,后缀为 module 名
  2. 在一个module中,自动生成的 IRouteGroup 类可以有多个,且都会在 IRouteRoot 类中注册
  3. IProviderGroup 只有一个,后缀为 module 名

准确来说后缀并不是 module 名,实际上你可以在 gradle 中指定,后续在讲解 parseRoutes 方法时会讲到具体原因

接下来将讲解 RouteProcessor#parseRoutes 方法:

  1. 拿到 Route,根据 RouteType 构建 RouteMeta,并通过 #categoryies 方法构建映射关系存储到 groupMap(groupMap 的 key 为 routeName ,value 则是 RouteMeta 集合)
  2. 遍历 groupMap 创建 ARouter$$Group$$groupName 类, 同时将 groupName 与生成的 File 的全类名的映射关系存储到 rootName
  3. 创建 ARouter$$Providers$$moduleName 类
  4. 创建 ARouter$$Rout$$moudleName

其中 groupName 就是你在注解中设置的 group,但我们平时在使用 ARouter 的时候很少回去设置 group, 如果你没有指定 group,Arouter 会默认使用 path 的主路径,比如你指定了 path = "/test/main", 那 groupName 就是 test; moudleName 则由你在 gradle 中指定。

AutoWiredProccesor

首先看一下生成的文件:

public class Test1Activity$$ARouter$$Autowired implements ISyringe {
    private SerializationService serializationService;

    public Test1Activity$$ARouter$$Autowired() {
    }

    public void inject(Object target) {
        this.serializationService = (SerializationService)ARouter.getInstance().navigation(SerializationService.class);
        Test1Activity substitute = (Test1Activity)target;
        substitute.name = substitute.getIntent().getStringExtra("name");
       ...
        substitute.helloService = (HelloService)ARouter.getInstance().navigation(HelloService.class);
    }
}
复制代码

生成的 class 继承之 ISyring只有一个方法 inject(Object),该方法的实现主要做了两件事:

  1. AutoWired 注解的 filed 如果是 service (Arouter 中继承自 IProvider), 则初始化对象:
substitute.helloService = (HelloService)ARouter.getInstance().navigation(HelloService.class);
复制代码
  1. AutoWried 注解的 filed 非 service,则从 bundle 中获取相应的 value 赋值给该 field:
	substitute.helloService = (HelloService)ARouter.getInstance().navigation(HelloService.class); // activity
	substitute.name = substitute.getArguments().getString("name"); // fragment
复制代码
  1. 最后生成类名$$Arouter$$AutoWired.class 文件
InterceptorProccessor

拦截注解生成器

public class ARouter$$Interceptors$$app implements IInterceptorGroup {
    public ARouter$$Interceptors$$app() {
    }

    public void loadInto(Map<Integer, Class<? extends IInterceptor>> interceptors) {
        interceptors.put(7, Test1Interceptor.class);
    }
}
复制代码

api

ARouter & _ARouter

launcher 下只有两个类 ARouter, _ARouter, ARouter 采用单例模式,对外提供 ARouter 相关 API,我们在调用ARouter.getInstance().build()时 真正调用的是_ARouter的相关方法,这里采用了代理模式,现在我们就开始以 ARouter 向外提供的方法来逐步分析:

首先看init(): 在 Arouter 库中,有一个 Warehose类,它存储了路由相关的信息,我们在使用 ARouter 之前必须要先调用 init 方法,而 init方法就是需要将信息存储到 Warehose 中 以下为时序图:

通过调用#loadInto(Warehose)方法,成功地将信息存储到 Warehose

现在Warehose 的 groupsIndex, providersIndex, interceptorsIndex 已经被我们存入了路由信息

最后再调用_ARouter#afterInit()实例化 interceptorService

接下来是 navigation()方法:

我们需要先了解Postcard 类,Postcard 继承 RouteMeta,扩展了一些属性,例如:flag(activity 的 flag),uri,provider,anim(动画)。我们平时在是用 Arouter 时 build 方法返回的就是 Postcasrd, postcard#navigation 方法,最终调用的是就是 _Arouter#navaigation

LogisticsCenter.completion(postcard);
if(!postcard.isGreenChannel()) {
    // 拦截器相关,暂时跳过
    interceptorSevice.doInterceptions(post, new InterceptorCallback() {
        @Override
        public void onContinue(Postcard postcard) {
            _navigation(context, postcard, requestCode, callback);
        }
    })
} else {
    return _navigation(context, postcard, requestCode, callback);
}
复制代码

首先我们来看LogisticsCenter#completion(Postcard)方法

...
RouteMeta routeMeta = Warehouse.routes.get(postcard.getPath());
if (null == routeMeta) {
    Class<? extends IRouteGroup> groupMeta = Warehouse.groupsIndex.get(postcard.getGroup());  // Load route meta.
    IRouteGroup iGroupInstance = groupMeta.getConstructor().newInstance(); // ARouter$$Group%%test 对象实例化
    iGroupInstance.loadInto(Warehouse.routes);
    Warehouse.groupsIndex.remove(postcard.getGroup());
    completion(postcard);   // Reload 进入 else 分支
} else {
    ... // postcadrd 设置属性如:destination,type,priority等
    switch(routeMeta.getType) {
        case PROVIDER:
            Class<? extends IProvider> providerMeta = (Class<? extends IProvider>) routeMeta.getDestination();
            IProvider instance = Warehouse.providers.get(providerMeta);
            if(instance == null) {
                IProvider povider = providerMeta.getConstructor().newInstance();
                provider.init(mContext); // init 方法只在创建 provider 对象后调用一次,
                Warehose.providers.put(providerMeta, provider); // 存入 map,除非调用 ARouter#destory, 该 provider 对象会一直存在 map 中
                postcard.setProvider(instance);
                postcard.greenChannel();
            }
            break;
        case FRAGMENT:
            ...
            break;
    }
}
复制代码

到这里面 Warehose 中的 routesproviders也被我们放入了相应信息

_navigation()方法:

switch(postcard.getType()) {
    case ACTIVITY:
        ... // build intent
        startActivity(requestCode, currentCotext, intent, postcard, callback);
        break;
    case POSTCARD:
        return postcard.getProvider();
    case Fragment:
        Fragment fragment = fragmentMeta.getConstructor().newInstance();
        fragment.setArguments(postcard.getExtras());
        return fragment;
    default:
        return null;
}
return null;
复制代码

接下来分析 ARroute#inject(Object)方法:

 AutowiredService autowiredService = ((AutowiredService) ARouter.getInstance().build("/arouter/service/autowired").navigation());
 autowiredService.autowire(objec);
复制代码

代码很简单,关键是 AutowiredService 的实现

    @Override
    public void autowire(Object instance) {
        String className = instance.getClass().getName();
        try {
            if (!blackList.contains(className)) {
                ISyringe autowiredHelper = classCache.get(className);
                if (null == autowiredHelper) {  // No cache.
                    autowiredHelper = (ISyringe) Class.forName(instance.getClass().getName() + SUFFIX_AUTOWIRED).getConstructor().newInstance(); // $$ARouter$$Autowired = $$ARouter$$Autowired
                }
                autowiredHelper.inject(instance); // Text1Activity$$ARouter$$Autowired.inject(this) 取出 bundle 中传递的参数
                classCache.put(className, autowiredHelper);
            }
        } catch (Exception ex) {
            blackList.add(className);    // This instance need not autowired.
        }
    }
复制代码

到这里,所有的注解生成的 class 的作用及其实现方法的调用时机都已经介绍到了,相信读者应该对 ARouter 有了新的了解了吧。接下来讲一下我们平时比较少用到的 interceptor

ARouter官方为我们提供了一个拦截器的使用场景:

比较经典的应用就是在跳转过程中处理登陆事件,这样就不需要在目标页重复做登陆检查
复制代码

还记得在讲解 ARroute#navigation时我们跳过了拦截器相关的内容吗

if(!postcard.isGreenChannel()) {
    // 拦截器相关,暂时跳过
    interceptorSevice.doInterceptions(post, new InterceptorCallback() {
        @Override
        public void onContinue(Postcard postcard) {
            _navigation(context, postcard, requestCode, callback);
        }
    })
}
复制代码

只要是拦截器,一般都会使用责任链设计模式,在 ARouter中也不例外,我们先看 interceptorService的创建过程,它的 type 是 Provider,在创建后会首先调用 init方法

@Override
public void init(final Context context) {
    LogisticsCenter.executor.execute(new Runnable() {
            @Override
            public void run() {
                for(entry : Warehose.interceptorsIndex) {
                    Class<? extends IInterceptor> interceptorClass = entry.getValue();
                    IInterceptor iInterceptor = interceptorClass.getConstructor().newInstance();
                    iInterceptor.init(context);
                    Warehose.interceptors.add(iInterceptor);
                }
            }
    }
}
复制代码

interceptorService#doInterceptions 调用 _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() {
                @Override
                public void onContinue(Postcard postcard) {
                    // Last interceptor excute over with no exception.
                    counter.countDown();
                    _excute(index + 1, counter, postcard);  // When counter is down, it will be execute continue ,but index bigger than interceptors size, then U know.
                }

                @Override
                public void onInterrupt(Throwable exception) {
                    // Last interceptor excute over with fatal exception.
                    postcard.setTag(null == exception ? new HandlerException("No message.") : exception.getMessage());    // save the exception message for backup.
                    counter.cancel();
                }
            });
        }
复制代码

常见错误分析

  1. service 内存泄漏, 考虑以下代码:

    public class HelloServiceImpl implements HelloService {
        private Context context;
        
        @Override
        public void init(Context context) {
            this.context = context
        }
        
        @Override
        public void say() {
            Toast.makeText(context, R.string.hello, Toast.LENGTH_LONG).show();
        }
    }
    复制代码

    之前我们分析源码的时候就已经讲过了,service 的实现类在初始化后会被放入Warehose.providers,后续通过调用ARouter.inject都是从Warehose.providers中取出。在上面的代码中如果 context 是一个 activity 对象,将该 activity 赋值给了成员变量 context,则该 activity 对象不会被回收,造成了内存泄漏

  2. 混淆导致 service 为 null

    public class HelloHolder {
         @Autowired(name = "/service/hello")
        HelloService helloService;
        
        public HelloHolder() {
            ARouter.getInstance().inject(this);
        }
        
        public void sayHello() {
            helloSerice.say;
        }
    }
    复制代码

    该代码本身并没有问题,问题出在在 android 开发中我们需要混淆代码,大家还记得之前讲的 AutowiredService 的实现吧,关键代码在这里,

                    if (null == autowiredHelper) {  // No cache.
                        autowiredHelper = (ISyringe) Class.forName(instance.getClass().getName() + SUFFIX_AUTOWIRED).getConstructor().newInstance(); // $$ARouter$$Autowired = $$ARouter$$Autowired
                    }
    复制代码

    本来 ARouter 构建的类名应该是 HelloHolder$$ARouter$$Autowired, 然后实例化该对象,调用该对象的 #inject方法,可是你现在将 HelloHolder混淆成了H,导致创建对象失败,这就是你为什么在代码中调用了 ARouter.getInstance().inject(this),但是 service 还是为 null 的原因

    在 android 开发中,Activity,Fragment 我们在混淆规则中是配置了 keep 等规则的,所以不会有问题。但是像上文中 HelloHolder 这种情况常常被我们所忽略,在开发中一定要注意,建议采用注解方式来保持类名不被混淆。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值