一、ARouter概述
ARouter是一个用于帮助Android App进行组件化改造的框架 —— 支持模块间的路由、通信、解耦。ARouter的典型应用场景有:
- 从外部URL映射到内部页面,以及参数传递与解析;
- 跨模块页面跳转,模块间解耦;
- 拦截跳转过程,处理登陆、埋点等逻辑;
-
跨模块API调用,通过控制反转来做组件解耦;
本篇主要介绍ARouter的用法之一:跨模块API调用。在组件化中,为了接耦各个模块,一般做法是各个模块之间不直接依赖,改为依赖模块的接口层。
二、ARouter基础用法
首先,通过一个简单的例子来介绍ARouter-跨模块API调用的基础用法:在MainActivity中有两个文本框,输入数字后点击按钮得到总和:
上面例子中点击”相加”后,需要调用一个计算模块提供的加法功能,不考虑组件耦合的做法一般是app模块直接依赖计算模块,直接调用计算模块的api:
public class MainActivity extends AppCompatActivity implements View.OnClickListener {
private EditText editText1;
private EditText editText2;
private TextView sumTextView;
private Button sumButton;
@Override
public void onClick(View v) {
if (v.getId() == R.id.main_sum_button) {
// 获得两个文本框的值
int a = Integer.parseInt(editText1.getText().toString());
int b = Integer.parseInt(editText2.getText().toString());
// Calculate类在calculate模块中,这里直接调用Calculate的api
Calculate calculate = new Calculate();
int sum = calculate.sum(a, b);
sumTextView.setText(Integer.toString(sum));
}
}
}
calculate模块中的Calculate类如下:
public class Calculate {
public int sum(int a, int b) {
return a + b;
}
}
例子比较简单,calculate模块提供计算的功能,再假设还有另一个log模块表示日志功能,在这种不考虑耦合的做法中,模块间的依赖关系如下:
同样地,与第一章
“组件化基础ARouter(一)”
类似,当项目模块越来越多且依赖关系越来越复杂后,这种方式也会使得项目结构耦合严重或循环依赖等问题。下面介绍ARouter的实现方式。
2.1 添加依赖和配置
apply plugin: 'kotlin-kapt'
android {
defaultConfig {
javaCompileOptions {
annotationProcessorOptions {
// ARouter参数
arguments = [AROUTER_MODULE_NAME: project.getName()]
}
}
}
}
dependencies {
implementation 'com.alibaba:arouter-api:1.5.2'
// 项目中如果使用了kotlin,则需要使用kapt关键字使用Annotation Processor
// java代码使用annotationProcessor关键字即可
kapt 'com.alibaba:arouter-compiler:1.5.2'
}
2.2 抽象接口服务层
在组件化的实现方式下,我们需要将模块的功能抽象成一个接口模块,模块间的依赖只依赖接口层,而不依赖具体实现层,这样就达到了组件接耦的目的。如下所示:calculate模块抽象出了一个lib_calculate_interface模块,实现层在lib_calcualte中:
lib_calculate_interface模块中只有一个继承IProvider的接口,如下所示:
public interface CalculateService extends IProvider {
public int sum(int a, int b);
}
2.3 接口服务实现层
在实现层lib_calcualte中,CalculateServiceImpl实现了具体功能,并且加上了@Route注解:
@Route(path = "/calculate/sum")
public class CalculateServiceImpl implements CalculateService {
@Override
public int sum(int a, int b) {
return a + b;
}
@Override
public void init(Context context) {
}
}
2.4 跨模块API调用
可以通过”ARouter.getInstance().build(“/calculate/sum”).navigation()”来管理&获取服务接口:
public class MainActivity extends AppCompatActivity implements View.OnClickListener {
private EditText editText1;
private EditText editText2;
private TextView sumTextView;
private Button sumButton;
// 模块接口
private CalculateService calculateService;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if (BuildConfig.DEBUG) {
ARouter.openDebug();
ARouter.openLog();
}
ARouter.init(getApplication());
// ARouter方式获取接口实现层实例
calculateService = (CalculateService) ARouter.getInstance().build("/calculate/sum").navigation();
}
@Override
public void onClick(View v) {
=if (v.getId() == R.id.main_sum_button) {
int a = Integer.parseInt(editText1.getText().toString());
int b = Integer.parseInt(editText2.getText().toString());
// 调用实现层的sum方法
int sum = calculateService.sum(a, b);
sumTextView.setText(Integer.toString(sum));
}
}
}
这样,组件化解耦后的实现方式中,由ARouter负责管理和获取服务,模块实现层之间无直接依赖:
三、ARouter源码分析
下面通过源码来分析ARouter是如何实现第二节中的功能的。
3.1 注解处理
在2.3中添加注解@Route(path = “/calculate/sum”)后,ARouter是使用2.1小节中声明的arouter-compiler来处理注解,自动生成代码,在此基础上实现路由跳转的功能。关于Annotation Processor的基知识可参考:
Annotation Processor简单用法
。
ARouter APT自动生成三个class文件(位于/lib_calculate/build/generated/source/kapt/debug/com/alibaba/android/arouter/routes目录下):
这三个class分别实现了IRouteGroup、IRouteRoot、IProviderGroup,且类名都以ARouter$开头,都位于com.alibaba.android.arouter.routes包下:
public class ARouter$$Group$$calculate implements IRouteGroup {
@Override
public void loadInto(Map<String, RouteMeta> atlas) {
atlas.put("/calculate/sum", RouteMeta.build(RouteType.PROVIDER, CalculateServiceImpl.class, "/calculate/sum", "calculate", null, -1, -2147483648));
}
}
public class ARouter$$Root$$lib_calculate implements IRouteRoot {
@Override
public void loadInto(Map<String, Class<? extends IRouteGroup>> routes) {
routes.put("calculate", ARouter$$Group$$calculate.class);
}
}
public class ARouter$$Providers$$lib_calculate implements IProviderGroup {
@Override
public void loadInto(Map<String, RouteMeta> providers) {
providers.put("com.bc.impl.CalculateService", RouteMeta.build(RouteType.PROVIDER, CalculateServiceImpl.class, "/calculate/sum", "calculate", null, -1, -2147483648));
}
}
APT自动生成的代码和
“组件化基础ARouter(一)3.1小节”
的区别在于:
(1)IRouteGroup中添加的RouteType类型是RouteType.PROVIDER;
(2)IProviderGroup中新增了往providers中注册的代码;
3.2 SDK初始化
SDK初始化的源码与
“组件化基础ARouter(一)3.1小节”
大体类似,不再分析。下面只分析不同的地方LogisticsCenter.init()中,对于模块接口IProvider,以<String,IProviderGroup>添加到HashMap(Warehouse.providersIndex)中:
public class LogisticsCenter {
private static Context mContext;
static ThreadPoolExecutor executor;
private static boolean registerByPlugin;
public synchronized static void init(Context context, ThreadPoolExecutor tpe) throws HandlerException {
mContext = context;
executor = tpe;
//load by plugin first
loadRouterMap();
if (registerByPlugin) {
logger.info(TAG, "Load router map by arouter-auto-register plugin.");
} else {
// 1.关键代码routeMap
Set<String> routerMap;
// It will rebuild router map every times when debuggable.
// 2.debug模式或者PackageUtils判断本地路由为空或有新版本
if (ARouter.debuggable() || PackageUtils.isNewVersion(context)) {
// 3.获取ROUTE_ROOT_PAKCAGE(com.alibaba.android.arouter.routes)包名下的所有类
// arouter-compiler根据注解自动生成的类都放在com.alibaba.android.arouter.routes包下
routerMap = ClassUtils.getFileNameByPackageName(mContext, ROUTE_ROOT_PAKCAGE);
// 4.建立routeMap后保存到sp中,下次直接从sp中读取StringSet;逻辑见else分支;
if (!routerMap.isEmpty()) {
context.getSharedPreferences(AROUTER_SP_CACHE_KEY, Context.MODE_PRIVATE).edit().putStringSet(AROUTER_SP_KEY_MAP, routerMap).apply();
}
// 5.更新本地路由的版本号
PackageUtils.updateVersion(context); // Save new version name when router map update finishes.
} else {
routerMap = new HashSet<>(context.getSharedPreferences(AROUTER_SP_CACHE_KEY, Context.MODE_PRIVATE).getStringSet(AROUTER_SP_KEY_MAP, new HashSet<String>()));
}
// 6.获取routeMap后,根据路由类型注册到对应的分组里
for (String className : routerMap) {
if (className.startsWith(ROUTE_ROOT_PAKCAGE + DOT + SDK_NAME + SEPARATOR + SUFFIX_ROOT)) {
// 7.加载root,类名以SUFFIX_ROOT(com.alibaba.android.arouter.routes.ARouter$$Root)开头
// 以<String,Class>添加到HashMap(Warehouse.groupsIndex)中
((IRouteRoot) (Class.forName(className).getConstructor().newInstance())).loadInto(Warehouse.groupsIndex);
} else if (className.startsWith(ROUTE_ROOT_PAKCAGE + DOT + SDK_NAME + SEPARATOR + SUFFIX_INTERCEPTORS)) {
// 8.加载interceptorMeta,类名以SUFFIX_INTERCEPTORS(com.alibaba.android.arouter.routes.ARouter$$Interceptors)开头
// 以<String,IInterceptorGroup>添加到UniqueKeyTreeMap(Warehouse.interceptorsIndex)中;以树形结构实现顺序拦截
((IInterceptorGroup) (Class.forName(className).getConstructor().newInstance())).loadInto(Warehouse.interceptorsIndex);
} else if (className.startsWith(ROUTE_ROOT_PAKCAGE + DOT + SDK_NAME + SEPARATOR + SUFFIX_PROVIDERS)) {
// 9.加载providerIndex,类名以SUFFIX_PROVIDERS(com.alibaba.android.arouter.routes.ARouter$$Providers)开头
// 以<String,IProviderGroup>添加到HashMap(Warehouse.providersIndex)中
((IProviderGroup) (Class.forName(className).getConstructor().newInstance())).loadInto(Warehouse.providersIndex);
}
}
}
}
}
3.3 获取服务
3.3.1 ARouter.build()
继续分析_ARouter.getInstance().build()获取服务时的源码,方法返回Postcard对象,该对象表示一次路由操作所需的全部信息,大体逻辑与
“组件化基础ARouter(一)3.1小节”
类似,唯一不同的地方在于Postcard对象的RouteType是PROVIDER,在LogisticsCenter完善Postcard的信息时,对于PROVIDER的Postcard对象,会把IProvider对应的实现层实例添加到Postcard对象中:
public class LogisticsCenter {
public synchronized static void completion(Postcard postcard) {
// 1.从Warehouse.routes中查找Postcard的path所对应的RouteMeta
RouteMeta routeMeta = Warehouse.routes.get(postcard.getPath());
if (null == routeMeta) {
// routeMet为空,则从groupsIndex查找;没查找到则不存在,查找到则动态添加
} else {
// 2.从Warehouse.routes中查找到Postcard所对应的RouteMeta后,完善Postcard信息
postcard.setType(routeMeta.getType());
switch (routeMeta.getType()) {
case PROVIDER: // if the route is provider, should find its instance
Class<? extends IProvider> providerMeta = (Class<? extends IProvider>) routeMeta.getDestination();
// 3.判断Warehouse.providers中是否已经有对应的服务实现层的实例
IProvider instance = Warehouse.providers.get(providerMeta);
if (null == instance) { // There's no instance of this provider
IProvider provider;
try {
// 4. 如果对应的服务实例不存在,则创建一个
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!");
}
}
// 5. 将服务实现层实例对象赋给postcard
postcard.setProvider(instance);
postcard.greenChannel(); // Provider should skip all of interceptors
break;
case FRAGMENT:
postcard.greenChannel(); // Fragment needn't interceptors
default:
break;
}
}
}
}
如上分析,对于PROVIDER类型的Postcard,LogisticsCenter会返回对应的服务实现层实例对象,特殊提一下,这里的静态方法使用了synchronized标记,来保证多线程操作调用IProvider时取到的对象都是同一个。
3.3.2 Postcard.navigation()
在_ARouter.getInstance().build()返回Postcard对象后,继续分析navigation()方法,大体逻辑仍与
“组件化基础ARouter(一)3.3小节”
类似,唯一不同是Postcard的类型是PROVIDER,这里只分析对应的源码,
final class _ARouter {
/**
* 根据完善的Postcard,执行对应的路由逻辑
*/
private Object _navigation(final Postcard postcard, final int requestCode, final NavigationCallback callback) {
final Context currentContext = postcard.getContext();
// 1.根据不同的routeType执行不同逻辑;我们的例子中routeType是PROVIDER
switch (postcard.getType()) {
case ACTIVITY:
// ...省略
break;
case PROVIDER:
// 对应PROVIDER类型的Postcard处理很简单,直接返回IProvider的实例即可
return postcard.getProvider();
case BOARDCAST:
case CONTENT_PROVIDER:
case FRAGMENT:
// ...省略
case METHOD:
case SERVICE:
default:
return null;
}
return null;
}
}
3.4 总结
- 在组件化思想中,模块间没有直接依赖,各个模块可以将对外提供的方法抽象成一个服务接口模块,各个模块之间只依赖接口层,而不依赖实现层;
- ARouter提供了IProvider接口,模块可以继承IProvider接口来暴露服务;
- ARouter提供了ARouter.getInstance().build(“/calculate/sum”).navigation()来获取path对应的服务;
- ARouter源码中对于IProvider服务的处理方式是直接返回对应的实现层实例对象;
- 各个IProvider的实例对象存放在Warehouse.providers中,ARouter用synchronized保证获取的对象相同(单例模式);
四、控制反转@Autowired
在2.4小节是通过”ARouter.getInstance().build(“/calculate/sum”).navigation()”来获取服务的,此外也可以用ARouter提供的注入功能实现控制反转:
public class MainActivity extends AppCompatActivity implements View.OnClickListener {
@Autowired
private CalculateService calculateService;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if (BuildConfig.DEBUG) {
ARouter.openDebug();
ARouter.openLog();
}
ARouter.init(getApplication());
// 注入@Autowired标记的变量
ARouter.getInstance().inject(this);
}
@Override
public void onClick(View v) {
=if (v.getId() == R.id.main_sum_button) {
int a = Integer.parseInt(editText1.getText().toString());
int b = Integer.parseInt(editText2.getText().toString());
// 调用实现层的sum方法
int sum = calculateService.sum(a, b);
sumTextView.setText(Integer.toString(sum));
}
}
}
注解@Autowired的原理可参考
“组件化基础ARouter(三)”
。
The End
欢迎关注我,一起解锁更多技能:
BC的掘金主页
~💐
BC的CSDN主页
~💐💐
ARouter官方文档:https://github.com/alibaba/ARouter/blob/master/README_CN.md
组件化ARouter系列(一、启动Activity):https://juejin.cn/post/7048527567346728990/
组件化ARouter系列(二、跨模块API调用):https://juejin.cn/post/7053399480191680520/
组件化ARouter系列(三、参数传递与解析):https://juejin.cn/post/7053405921065566244/
组件化ARouter系列(四、拦截器):https://juejin.cn/post/7053749555892289572/