Android高级-组件化-路由管理器完成版实践

前面我们使用APT+JavaPoet完成了组件化路由框架搭建

即:

1:通过添加自定义注解ARouter,通过对应的注解处理器动态扫描含有ARouter注解的Activity

2:在注解处理器中处理获取的含有ARouter注解的Activity,通过JavaPoet生成对应的Group路由和Path路由

3:通过动态添加注解Parameter,通过对应的注解处理器动态臊面含有Parameter注解的属性以及对应的Activity,完成传参

这节主要解决,模块一,模块二之间没有任何依赖,但是可以通信。

开始之前我们先带着几个疑问:

1:之前我们已经生成好了带有ARouter的Activity到 “com/netease/modular/apt” 下,那我们要跳转的话直接从这个包下面去找对应的Class类就可以了。那么如何去找?

2:之前章节中的跳转是build后,都是类似于通过下面的方式,我们知道,这样在编译的时候也会出现问题,所以我们该怎么优化呢?

 public void jump(View view) {
        Intent intent = new Intent(this, OrderActivity$$ARouter.findTargetClass("/app/OrderActivity"));
        startActivity(intent);
    }

构思:

因为我们在Activity创建的时候,在类上添加了自定义的注解:@ARouter(path = "/order/Order_MainActivity") 类似于这样,这个也是ARouterLoadPath路由中的key,我们可以通过这个key找到我们需要的class文件,最终完成跳转。

也就是通过 RouterManager将"/order/Order_MainActivity"传到RouterManager里面,在这个里面解析并查找具体的class文件

开始撸码

我们创建一个路由管理类(单例模式的路由管理类)

路由加载管理器  RouterManager

第一步:

新建一个路由管理器,我们做Activity跳转的时候,就要通过这个类去传递

/**
 * 路由加载管理器
 */
public final class RouterManager {

    // 路由组名
    private String group;
    // 路由详细路径
    private String path;

    private static RouterManager instance;

    // 单例方式,全局唯一
    public static RouterManager getInstance() {
        if (instance == null) {
            synchronized (RouterManager.class) {
                if (instance == null) {
                    instance = new RouterManager();
                }
            }
        }
        return instance;
    }
}

第二步:分析

因为是管理类,那么就要去“com/netease/modular/apt” 包下,将所有生成的Gruop组路由和Path路由都查出来,放到集合中,然后再从集合中取。

所以为了性能考虑 我们先建两个有缓存功能的Map集合,因 ARouter$$Path$$app 和 ARouter$$Group$$app 都是固定写法,只有最后一个$$符号后有变化,所以取最后一个$$符号后的数据为key,他们的父类ARouterLoadGroup 和ARouterLoadPath 为值

    // 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$$";


    private RouterManager() {
        // 初始化,并赋值缓存中条目的最大值(最多163组)
        groupCache = new LruCache<>(163);
        // 每组最多163条路径值
        pathCache = new LruCache<>(163);
    }

第三步:

为了遵循设计模式的单一原则,我们尽量一个类只负责一个功能,当前的这个RouterManager负责从集合中找到对应了路径的Activity跳转,我们需要新建一个传参的管理器。因传参管理器也是跳转的一部分,所以我们采用建造者模式(builder模式)将复杂对象使用此模式分解。

即BundleManager

/**
 * Bundle拼接参数管理类
 */
public final class BundleManager {

    private Bundle bundle = new Bundle();
    // 底层业务接口
    private Call call;
    // 是否回调setResult()
    private boolean isResult;

    Bundle getBundle() {
        return bundle;
    }

    Call getCall() {
        return call;
    }

    void setCall(Call call) {
        this.call = call;
    }

    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);
    }
}

上面的重点是:我们将具体的跳转操作(也就是路由)交回给RouterManager去处理。

第四步 开始处理跳转

    /**
     * 开始跳转
     *
     * @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

        //前面为Group路由的固定写法,group变量为初始化RouterManager动态截取的
        String groupClassName = context.getPackageName() + ".apt" + GROUP_FILE_PREFIX_NAME + group;
        Log.e("netease >>> ", "groupClassName -> " + groupClassName);

        try {
            //返回group组的Map集合( Map<String, Class<? extends ARouterLoadPath>> )
            ARouterLoadGroup groupLoad = groupCache.get(group);
            //取出来的数据类似于:ARouter$$Group$$app
            //如果集合不为空,说明获取过这个路径的信息
            if (groupLoad == null) {
                Class<?> clazz = Class.forName(groupClassName);
                groupLoad = (ARouterLoadGroup) clazz.newInstance();
                //groupLoad ==ARouter$$Group$$app.Class
                //将所有
                //集合里面为空 说明之前没有获取过这个信息  那么就把信息加入到集合中。  方便二次获取
                groupCache.put(group, groupLoad);
            }


            // 获取路由路径类ARouter$$Path$$app
            if (groupLoad.loadGroup().isEmpty()) {
                throw new RuntimeException("路由加载失败");
            }

            ARouterLoadPath pathLoad = pathCache.get(path);
            //返回的信息    Map<String, RouterBean>
            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 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;

                        case CALL:
                            Class<?> clazz = routerBean.getClazz();
                            Call call = (Call) clazz.newInstance();
                            bundleManager.setCall(call);
                            return bundleManager.getCall();
                    }
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

总结一下:

以person为例:

1:查找Arouter$$Group$$personal ( 通过 包名+ 拼接 + 类名) 类加载

2:Arouter$$Group$$personal执行loadGroup

3:执行loadGroup return groupMap

4:执行groupMap.get(path)

5:根据上面的执行,即拿到了Arouter$$Paht$$personal.class

6:执行Arouter$$Paht$$personal的loadPath

7:pathMap.get(path)

8:通过上面的执行,拿到了RouterBean

9:拿到RouterBean里面的Personal_MainActivity.class

10:完成跳转

下面我们开始完成跳转

   public void jumpApp(View view) {
        RouterManager.getInstance()
                .build("/app/MainActivity")
                .withResultString("call", "I'am comeback!")
                .navigation(this);
    }

    public void jumpPersonal(View view) {
        RouterManager.getInstance()
                .build("/personal/Personal_MainActivity")
                .withString("name", "simon")
                .navigation(this);
    }

 

 

 

 

 

;

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值