目标
模块之间可以:
1)跳转
2)调用资源
3)调用方法
4)总体架构图梳理
参数处理ParameterManager
1)单例
2)缓存LRUCache
/**
* ParameterManager
*
* 单例,管理apt生成的 ParameterLoad子类XXX$$Parameter对象
* @author zfc
* @date 2020-01-16
*/
public class ParameterManager {
private static ParameterManager instance;
// Lru缓存,key:类名, value:参数Parameter加载接口
private LruCache<String, ParameterLoad> cache;
// APT生成的获取参数源文件,后缀名
private static final String FILE_SUFFIX_NAME = "$$Parameter";
// 单例方式,全局唯一
public static ParameterManager getInstance() {
if (instance == null) {
synchronized (ParameterManager.class) {
if (instance == null) {
instance = new ParameterManager();
}
}
}
return instance;
}
private ParameterManager() {
// 初始化,并赋值缓存中条目的最大值
cache = new LruCache<>(163);
}
/**
* 传入的Activity中所有被@Parameter注解的属性。通过加载APT生成源文件,并给属性赋值
*
* @param activity 需要给属性赋值的类,如:MainActivity中所有被@Parameter注解的属性
*/
public void loadParameter(@NonNull Activity activity) {
String className = activity.getClass().getName();
// 查找缓存集合中是否有对应activity的value
ParameterLoad iParameter = cache.get(className);
try {
// 找不到,加载类后放入缓存集合
if (iParameter == null) {
// 注意:这里的className是全类名:com.xxx.xxx.Activity
Class<?> clazz = Class.forName(className + FILE_SUFFIX_NAME);
iParameter = (ParameterLoad) clazz.newInstance();
cache.put(className, iParameter);
}
// 通过传入参数给生成的源文件中所有属性赋值
iParameter.loadParameter(activity);
} catch (Exception e) {
e.printStackTrace();
}
}
}
那么ParameterManager的使用方式如下:
public class OrderMainActivity extends AppCompatActivity {
@Parameter
String name;
@Parameter(name = "agex")
int age;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.order_main_activity);
//注意 调用方式有new OrderMainActivity$$Parameter() 变为Manager管理调用
//new OrderMainActivity$$Parameter().loadParameter(this);
ParameterManager.getInstance().loadParameter(this);
}
}
组件间跳转封装
由于模块间是相互独立的,即使通过APT生成各自ARouter$$Group$$xxx,但是这些类模块间并不可互相访问。要想获取对应的对象,只能根据路径名"/personal/PersonMainActivity"去自行拼接组名“ARouter$$Group$$personal”,然后通过类加载器获取字节码对象,从而创建group实例。同理“ARouter$$Path$$personal”对象。
/**
* 路由加载管理器
*/
public final class RouterManager {
// 路由组名
private String group;
// 路由详细路径
private String path;
private static RouterManager instance;
// Lru缓存,key:类名, value:路由组Group加载接口
private LruCache<String, ARouterLoadGroup> groupCache;
// Lru缓存,key:类名, value:路由组Group对应的详细Path加载接口
private LruCache<String, ARouterLoadPath> pathCache;
// APT生成的路由组Group源文件前缀名
private static final String GROUP_FILE_PREFIX_NAME = ".ARouter$$Group$$";
// 单例方式,全局唯一
public static RouterManager getInstance() {
if (instance == null) {
synchronized (RouterManager.class) {
if (instance == null) {
instance = new RouterManager();
}
}
}
return instance;
}
private RouterManager() {
// 初始化,并赋值缓存中条目的最大值(最多163组)
groupCache = new LruCache<>(163);
// 每组最多163条路径值
pathCache = new LruCache<>(163);
}
public BundleManager build(String path) {
// @ARouter注解中的path值,必须要以 / 开头(模仿阿里Arouter规范)
if (TextUtils.isEmpty(path) || !path.startsWith("/")) {
throw new IllegalArgumentException("未按规范配置,如:/app/MainActivity");
}
group = subFromPath2Group(path);
// 检查后再赋值
this.path = path;
return new BundleManager();
}
private String subFromPath2Group(String path) {
// 比如开发者代码为:path = "/MainActivity",最后一个 / 符号必然在字符串第1位
if (path.lastIndexOf("/") == 0) {
// 架构师定义规范,让开发者遵循
throw new IllegalArgumentException("@ARouter注解未按规范配置,如:/app/MainActivity");
}
// 从第一个 / 到第二个 / 中间截取,如:/app/MainActivity 截取出 app 作为group
String finalGroup = path.substring(1, path.indexOf("/", 1));
if (TextUtils.isEmpty(finalGroup)) {
// 架构师定义规范,让开发者遵循
throw new IllegalArgumentException("@ARouter注解未按规范配置,如:/app/MainActivity");
}
// 最终组名:app
return finalGroup;
}
/**
* 开始跳转
*
* @param context 上下文
* @param bundleManager Bundle拼接参数管理类
* @param code 这里的code,可能是requestCode,也可能是resultCode。取决于isResult
* @return 普通跳转可以忽略,用于跨模块CALL接口
*/
Object navigation(@NonNull Context context, BundleManager bundleManager, int code) {
// 精华:阿里的路由path随意写,导致无法找到随意拼接APT生成的源文件,如:ARouter$$Group$$abc
// 找不到,就加载私有目录下apk中的所有dex并遍历,获得所有包名为xxx的类。并开启了线程池工作
// 这里的优化是:代码规范写法,准确定位ARouter$$Group$$app
String groupClassName = context.getPackageName() + ".apt" + GROUP_FILE_PREFIX_NAME + group;
Log.e("netease >>> ", "groupClassName -> " + groupClassName);
try {
ARouterLoadGroup groupLoad = groupCache.get(group);
if (groupLoad == null) {
Class<?> clazz = Class.forName(groupClassName);
groupLoad = (ARouterLoadGroup) clazz.newInstance();
groupCache.put(group, groupLoad);
}
// 获取路由路径类ARouter$$Path$$app
if (groupLoad.loadGroup().isEmpty()) {
throw new RuntimeException("路由加载失败");
}
ARouterLoadPath pathLoad = pathCache.get(path);
if (pathLoad == null) {
Class<? extends ARouterLoadPath> clazz = groupLoad.loadGroup().get(group);
if (clazz != null) pathLoad = clazz.newInstance();
if (pathLoad != null) pathCache.put(path, pathLoad);
}
if (pathLoad != null) {
// tempMap赋值
pathLoad.loadPath();
if (pathLoad.loadPath().isEmpty()) {
throw new RuntimeException("路由路径加载失败");
}
RouterBean routerBean = pathLoad.loadPath().get(path);
if (routerBean != null) {
switch (routerBean.getType()) {
case ACTIVITY:
Intent intent = new Intent(context, routerBean.getClazz());
intent.putExtras(bundleManager.getBundle());
// startActivityForResult -> setResult
if (bundleManager.isResult()) {
((Activity) context).setResult(code, intent);
((Activity) context).finish();
}
if (code > 0) { // 跳转时是否回调
((Activity) context).startActivityForResult(intent, code, bundleManager.getBundle());
} else {
context.startActivity(intent, bundleManager.getBundle());
}
break;
}
}
}
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
}
/**
* Bundle拼接参数管理类
*/
public final class BundleManager {
private Bundle bundle = new Bundle();
// 是否回调setResult()
private boolean isResult;
Bundle getBundle() {
return bundle;
}
boolean isResult() {
return isResult;
}
// @NonNull不允许传null,@Nullable可以传null
public BundleManager withString(@NonNull String key, @Nullable String value) {
bundle.putString(key, value);
return this;
}
// 示例代码,需要拓展
public BundleManager withResultString(@NonNull String key, @Nullable String value) {
bundle.putString(key, value);
isResult = true;
return this;
}
public BundleManager withBoolean(@NonNull String key, boolean value) {
bundle.putBoolean(key, value);
return this;
}
public BundleManager withInt(@NonNull String key, int value) {
bundle.putInt(key, value);
return this;
}
public BundleManager withBundle(@NonNull Bundle bundle) {
this.bundle = bundle;
return this;
}
public Object navigation(Context context) {
return RouterManager.getInstance().navigation(context, this, -1);
}
// 这里的code,可能是requestCode,也可能是resultCode。取决于isResult
public Object navigation(Context context, int code) {
return RouterManager.getInstance().navigation(context, this, code);
}
}
页面跳转时使用:
//1. 正向跳转
Bundle bundle = new Bundle();
bundle.putString("name", "simon");
bundle.putInt("age", 35);
bundle.putBoolean("isSuccess", true);
bundle.putString("netease", "net163");
RouterManager.getInstance()
.build("/personal/Personal_MainActivity")
.withString("username", "baby")
.withBundle(bundle)
.navigation(this, 163);
//2. 页面返回
RouterManager.getInstance()
.build("/app/MainActivity")
.withResultString("call", "I'am comeback!")
.navigation(this);
组件间共享资源
以Personal模块使用主模块App中的UserInfo数据为例。
1)在arouter_api模块增加一个新的接口Call
/**
* 跨模块业务回调,空接口可集成拓展/实现
*/
public interface Call {
}
2)在common模块以桥接的方式定义一个要共享的数据结构接口,实现Call接口。
public interface IUser extends Call {
/**
* @return 根据不同子模块的具体实现,调用得到不同的结果
*/
BaseUser getUserInfo();
}
/**
* 例如:用户实体父类
*/
public class BaseUser implements Serializable {
private String name;
private String account;
private String password;
private String phoneNumber;
private int gender;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getAccount() {
return account;
}
public void setAccount(String account) {
this.account = account;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public String getPhoneNumber() {
return phoneNumber;
}
public void setPhoneNumber(String phoneNumber) {
this.phoneNumber = phoneNumber;
}
public int getGender() {
return gender;
}
public void setGender(int gender) {
this.gender = gender;
}
}
3)通过Arouter注解提供 IUser的实现类,以便于提供一种获取具体数据的方式。
@ARouter(path = "/app/getUserInfo")
public class IUserImpl implements IUser {
@Override
public BaseUser getUserInfo() {
UserInfo userInfo = new UserInfo();
userInfo.setName("zfc");
userInfo.setAccount("abc");
userInfo.setPassword("123456");
userInfo.setVipLevel(9);
return userInfo;
}
}
4)ArouterProcessor处理逻辑的扩展
//ArouterPocessor.java
// 解析所有被 @ARouter 注解的 类元素集合
private void parseElements(Set<? extends Element> elements) throws IOException {
// 通过Element工具类,获取Activity、Callback类型
TypeElement activityType = elementUtils.getTypeElement(Constants.ACTIVITY);
TypeElement callType = elementUtils.getTypeElement(Constants.CALL);
// 显示类信息(获取被注解节点,类节点)这里也叫自描述 Mirror
TypeMirror activityMirror = activityType.asType();
TypeMirror callMirror = callType.asType();
// 遍历节点
for (Element element : elements) {
// 获取每个元素类信息,用于比较
TypeMirror elementMirror = element.asType();
messager.printMessage(Diagnostic.Kind.NOTE, "遍历元素信息:" + elementMirror.toString());
// 获取每个类上的@ARouter注解中的注解值
ARouter aRouter = element.getAnnotation(ARouter.class);
// 路由详细信息,最终实体封装类
RouterBean bean = new RouterBean.Builder()
.setGroup(aRouter.group())
.setPath(aRouter.path())
.setElement(element)
.build();
// 高级判断:ARouter注解仅能用在类之上,并且是规定的Activity
// 类型工具类方法isSubtype,相当于instance一样
if (typeUtils.isSubtype(elementMirror, activityMirror)) {
bean.setType(RouterBean.Type.ACTIVITY);
} else if (typeUtils.isSubtype(elementMirror, callMirror)) {
bean.setType(RouterBean.Type.CALL);
} else {
// 不匹配抛出异常,这里谨慎使用!考虑维护问题
throw new RuntimeException("@ARouter注解目前仅限用于Activity类之上");
}
// 赋值临时map存储,用来存放路由组Group对应的详细Path类对象
valueOfPathMap(bean);
}
// routerMap遍历后,用来生成类文件
// 获取ARouterLoadGroup、ARouterLoadPath类型(生成类文件需要实现的接口)
TypeElement groupLoadType = elementUtils.getTypeElement(Constants.AROUTE_GROUP); // 组接口
TypeElement pathLoadType = elementUtils.getTypeElement(Constants.AROUTE_PATH); // 路径接口
// 第一步:生成路由组Group对应详细Path类文件,如:ARouter$$Path$$app
createPathFile(pathLoadType);
// 第二步:生成路由组Group类文件(没有第一步,取不到类文件),如:ARouter$$Group$$app
createGroupFile(groupLoadType, pathLoadType);
}
5)通过Parameter注解获取 IUser的实现类,并实例化对象
@ARouter(path = "/order/Order_MainActivity")
public class Order_MainActivity extends BaseActivity {
@Parameter(name = "/app/getUserInfo")
IUser iUser;
....
}
6)ParamterProccesor处理逻辑的扩展
扫描二维码关注公众号,回复:
10180065 查看本文章
public class ParameterFactory {
// MainActivity t = (MainActivity) target;
private static final String CONTENT = "$T t = ($T)target";
// 方法体构建
private MethodSpec.Builder methodBuidler;
// Messager用来报告错误,警告和其他提示信息
private Messager messager;
// type(类信息)工具类,包含用于操作TypeMirror的工具方法
private Types typeUtils;
// 获取元素接口信息(生成类文件需要的接口实现类)
private TypeMirror callMirror;
// 类名,如:MainActivity
private ClassName className;
private ParameterFactory(Builder builder) {
this.messager = builder.messager;
this.className = builder.className;
typeUtils = builder.typeUtils;
// 通过方法参数体构建方法体:public void loadParameter(Object target) {
methodBuidler = MethodSpec.methodBuilder(Constants.PARAMETER_METHOD_NAME)
.addAnnotation(Override.class)
.addModifiers(Modifier.PUBLIC)
.addParameter(builder.parameterSpec);
this.callMirror = builder.elementUtils
.getTypeElement(Constants.CALL)
.asType();
}
/**
* 添加方法体内容的第一行(MainActivity t = (MainActivity) target;)
*/
public void addFirstStatement() {
// 方法内容:MainActivity t = (MainActivity) target;
methodBuidler.addStatement(CONTENT, className, className);
}
public MethodSpec build() {
return methodBuidler.build();
}
/**
* 构建方体内容,如:t.s = t.getIntent.getStringExtra("s");
*
* @param element 被注解的属性元素
*/
public void buildStatement(Element element) {
// 遍历注解的属性节点 生成函数体
TypeMirror typeMirror = element.asType();
// 获取 TypeKind 枚举类型的序列号
int type = typeMirror.getKind().ordinal();
// 获取属性名
String fieldName = element.getSimpleName().toString();
// 获取注解的值
String annotationValue = element.getAnnotation(Parameter.class).name();
// 判断注解的值为空的情况下的处理(注解中有name值就用注解值)
annotationValue = EmptyUtils.isEmpty(annotationValue) ? fieldName : annotationValue;
// 最终拼接的前缀:
String finalValue = "t." + fieldName;
// t.s = t.getIntent().
String methodContent = finalValue + " = t.getIntent().";
// TypeKind 枚举类型不包含String
if (type == TypeKind.INT.ordinal()) {
// t.s = t.getIntent().getIntExtra("age", t.age);
methodContent += "getIntExtra($S, " + finalValue + ")";
} else if (type == TypeKind.BOOLEAN.ordinal()) {
// t.s = t.getIntent().getBooleanExtra("isSuccess", t.age);
methodContent += "getBooleanExtra($S, " + finalValue + ")";
} else {
// t.s = t.getIntent.getStringExtra("s");
if (typeMirror.toString().equalsIgnoreCase(Constants.STRING)) {
methodContent += "getStringExtra($S)";
// 类型工具类方法isSubtype,相当于instance一样
} else if (typeUtils.isSubtype(typeMirror, callMirror)) {
// t.iUser = (IUserImpl) RouterManager.getInstance().build("/order/getUserInfo").navigation(t);
methodContent = "t." + fieldName + " = ($T) $T.getInstance().build($S).navigation(t)";
methodBuidler.addStatement(methodContent,
TypeName.get(typeMirror),
ClassName.get(Constants.BASE_PACKAGE, Constants.ROUTER_MANAGER),
annotationValue);
return;
}
}
// 健壮代码
if (methodContent.endsWith(")")) {
// 添加最终拼接方法内容语句
methodBuidler.addStatement(methodContent, annotationValue);
} else {
messager.printMessage(Diagnostic.Kind.ERROR, "目前暂支持String、int、boolean传参");
}
}
public static class Builder {
// Messager用来报告错误,警告和其他提示信息
private Messager messager;
// 操作Element工具类 (类、函数、属性都是Element)
private Elements elementUtils;
// type(类信息)工具类,包含用于操作TypeMirror的工具方法
private Types typeUtils;
// 类名,如:MainActivity
private ClassName className;
// 方法参数体
private ParameterSpec parameterSpec;
public Builder(ParameterSpec parameterSpec) {
this.parameterSpec = parameterSpec;
}
public Builder setMessager(Messager messager) {
this.messager = messager;
return this;
}
public Builder setElementUtils(Elements elementUtils) {
this.elementUtils = elementUtils;
return this;
}
public Builder setTypeUtils(Types typeUtils) {
this.typeUtils = typeUtils;
return this;
}
public Builder setClassName(ClassName className) {
this.className = className;
return this;
}
public ParameterFactory build() {
if (parameterSpec == null) {
throw new IllegalArgumentException("parameterSpec方法参数体为空");
}
if (className == null) {
throw new IllegalArgumentException("方法内容中的className为空");
}
if (messager == null) {
throw new IllegalArgumentException("messager为空,Messager用来报告错误、警告和其他提示信息");
}
return new ParameterFactory(this);
}
}
}
4)总体架构图梳理