组件化之实践(四)

目标

模块之间可以:
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处理逻辑的扩展

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)总体架构图梳理

在这里插入图片描述
在这里插入图片描述

代码传动门

发布了98 篇原创文章 · 获赞 6 · 访问量 2万+

猜你喜欢

转载自blog.csdn.net/dirksmaller/article/details/103992308