关于android:XPage系列|这次升级后终于是全自动化注册了

6次阅读

共计 4946 个字符,预计需要花费 13 分钟才能阅读完成。

前言

作为 X-Library 系列框架 的灵魂所在,XPage 开源两年以来,始终致力于升高 Fragment 应用的难度,努力实现一个 Activity 多 Fragment 的 Android 开发模式。

就在前不久,我就整顿了 XPage 开源这几年来的应用状况,写了一篇《史上最不便的 Android 页面框架 XPage 使用指南》, 并且还录了几期视频独自解说了 XPage 的应用,让越来越多地人看到了 XPage 应用的便捷性。

但就在前几天,在交换群里忽然有人问我上面几个问题:

  • 1. 我如果想在多个 module 中应用 XPage,我该怎么办呀?
  • 2. 为什么我应用 XPage 之后,始终找不到 AppPageConfig 这个类啊?

下面的问题让我忽然意识到一点:并不是所有人都对 APT 技术有所理解的。

看来我之前参考 ARouter 实现的主动注册性能可能并没有欠缺,难怪 ARouter 起初会写一个 arouter-register 插件来实现主动注册的性能。

于是乎,为了可能让 XPage 的主动注册性能更加完满,我加班加点开发,于是就有了 XPage 的 3.1.1 版本 – 彻底的自动化注册。

降级后有什么变动

在感触全自动化页面注册带来的便当之前,让咱们先来感受一下之前版本的应用。

3.1.1 之前版本

在 3.1.1 之前版本,在应用主动注册性能的时候,还是须要实现 PageConfiguration 接口,并调用编译时主动生成的页面配置类“moduleName”+PageConfig 的 getPages()办法返回注册的页面。

PageConfig.getInstance()
        .setPageConfiguration(new PageConfiguration() { // 页面注册
            @Override
            public List<PageInfo> registerPages(Context context) {
                // 主动注册页面, 是编译时主动生成的,build 一下就进去了。如果你还没应用 @Page 的话,临时是不会生成的。return AppPageConfig.getInstance().getPages(); // 主动注册页面
            }
        })
        .debug("PageLog")       // 开启调试
        .setContainActivityClazz(XPageActivity.class) // 设置默认的容器 Activity
        .enableWatcher(false)   // 设置是否开启内存泄露监测
        .init(this); 

能够看到,这里的主动注册还是须要一部分手动配合能力实现的。如果说你以后只有一个 module 的话,可能还好说。然而如果你应用了多个 module 之后,你就须要把多个 module 生成的配置类像下面那样一个一个地加进去,这样用起来会让人感觉十分的不不便,这显著违反了我写 XPage 框架的初衷!

不仅如此,这样写死还会带来其余很多问题:

  • 1. 如果 module 名变了,还须要对应地去批改配置类的类名。
  • 2. 如果以后 module 没有应用 @Page 注解润饰 Fragment 的话,配置类也不会主动生成,这样会让很多首次使用者十分纳闷。
  • 3. 我的项目要是没有编译过的话,配置类是不会主动生成的,这样代码就会报错说类找不到,而后很多老手就懵逼了。

3.1.1 之后版本

为了可能解决以上的问题,我实现了一个主动注册的配置类 AutoPageConfiguration。那么上面就看一下最新版本的 XPage 是如何注册的吧:

PageConfig.getInstance()
        .debug("PageLog")       // 开启调试
        .setContainActivityClazz(XPageActivity.class) // 设置默认的容器 Activity,按需设置(非必须).init(this);            // 初始化页面配置

是的,你没有看错,这里没有手动实现 PageConfiguration 接口的局部了,能够说是真正实现了全自动页面注册,是不是十分不便呀?

如何实现注册的自动化

看到下面的变动,你是不是十分想晓得我是如何实现彻底的自动化注册的?

想要答复这个问题,还是让咱们先看一看这个编译时主动生成的配置类是如何实现的。

APT 技术实现页面配置类的主动生成

其实当初实现页面配置类的主动生成的计划,也是我研读了 ARouter 源码之后,受到了 APT 技术的启发后实现的。

因为 XPage 实现路由跳转次要就是靠 PageInfo 和 Fragment 建设起来的映射关系。过后的思路就是采纳 APT 技术,利用 @Page 注解去标识须要注册的 Fragment,而后在编译的时候通过 APT 技术去扫描出所有应用了 @Page 注解标识了的 Fragment,将注解信息转化为 PageInfo, 并按 module 生成对应的页面配置类,在这个配置类外面寄存了该 module 下所有标注了@Page 的页面信息PageInfo

上面是主动生成的一个简略的配置类例子:

能够看到,主动生成的配置类都会寄存在 com.xuexiang.xpage.config 包下,类名都是以 PageConfig 作为结尾。留神这里十分要害,它是我前面实现自动化注册的要害。

具体的实现细节,能够参见 XPage 的页面配置主动生成器 PageConfigProcessor 的源码。

运行时扫描指定包下的配置类反射实现主动注册

下面咱们在理解页面配置类是如何主动生成的时候发现一个法则:

主动生成的配置类都会寄存在 com.xuexiang.xpage.config 包下,类名都是以 PageConfig 作为结尾。

那么咱们可不可以在运行的时候,间接扫描 com.xuexiang.xpage.config 包下的所有类,而后找到以 PageConfig 作为结尾的配置类,而后反射它的 getPages 办法间接获取到所有的配置信息,而后注册进去?

上面是我依据下面的猜测,实现的 AutoPageConfiguration 类源码:

public class AutoPageConfiguration implements PageConfiguration {
    /**
     * 页面配置所在的包名
     */
    private static final String PAGE_CONFIG_PACKAGE_NAME = "com.xuexiang.xpage.config";
    /**
     * 页面配置生成类的类后缀名
     */
    private static final String PAGE_CONFIG_CLASS_NAME_SUFFIX = "PageConfig";

    @Override
    public List<PageInfo> registerPages(Context context) {List<PageInfo> pageInfos = new ArrayList<>();
        Set<String> classSet = null;
        try {classSet = ClassUtils.getClassNames(context, PAGE_CONFIG_PACKAGE_NAME);
        } catch (Exception e) {e.printStackTrace();
        }
        if (classSet != null) {for (String className : classSet) {if (className != null && className.endsWith(PAGE_CONFIG_CLASS_NAME_SUFFIX)) {
                    try {pageInfos.addAll(getPagesByClass(Class.forName(className)));
                    } catch (Exception e) {PageLog.e(e);
                    }
                }
            }
        }
        return pageInfos;
    }

    private List<PageInfo> getPagesByClass(Class<?> clazz) throws Exception {
        // 获取单例对象
        Method getInstanceMethod = clazz.getDeclaredMethod("getInstance");
        getInstanceMethod.setAccessible(true);
        Object instance = getInstanceMethod.invoke(null);
        // 获取页面信息
        Method getPagesMethod = clazz.getDeclaredMethod("getPages");
        getPagesMethod.setAccessible(true);
        return (List<PageInfo>) getPagesMethod.invoke(instance);
    }
}

从源码中咱们能够看到,我是这样做的:

  • 1. 应用 ClassUtils.getClassNames 获取到 com.xuexiang.xpage.config 包下的所有类的类名。这里的 ClassUtils 也是我借鉴了 ARouter 外面的源码。
  • 2. 遍历这个类名汇合,并依据类名结尾是否是 PageConfig 筛选出所有的配置类。
  • 3. 调用 getPagesByClass 办法,反射获取到配置类的所有页面信息,而后退出到页面汇合中,最终返回所有 module 配置页面的信息。

有了 AutoPageConfiguration 之后,上面就非常简单啦,咱们只须要将 mPageConfiguration 默认设置成AutoPageConfiguration,这样就能够实现自动化注册啦!

/**
 * 初始化页面信息
 *
 * @param context 上下文
 */
private void initPages(Context context) {if (mPageConfiguration == null) {
        // 没有设置的话,就应用主动注册配置
        mPageConfiguration = new AutoPageConfiguration();}
    registerPageInfos(mPageConfiguration.registerPages(context));
    CoreConfig.init(context, getPages());
}

减少混同配置

你认为到这儿就完结了?没那么简略!能够发现,下面的实现计划次要是依赖于扫描类并进行反射注册的。所以如果代码做了混同了之后,该计划就会生效了,所以咱们还须要在混同配置清单中减少如下的配置来防止混同:

-keep class com.xuexiang.xpage.config.** {*;}

咱们要保障 com.xuexiang.xpage.config 包下的类不能被混同。

到这儿,主动注册的实现算是真正的讲完了,上面让咱们来瞧瞧 XPage 的新版本还有那些中央更新了!

其余更新

去除 LeakCanary 依赖

在此之前,XPage 始终依赖了 LeakCanary,次要起因还是LeakCanary 在 2.0 版本之前的应用还是相当不不便的,于是我就做了一下默认集成以方便使用。

然而当 LeakCanary 降级到 2.0 以上版本的时候,这个问题仿佛就没了。因为进行了从新的设计,LeakCanary的应用变得没那么具备侵入性,因而我就思考去除了 LeakCanary 的依赖。

优化了页面点击的键盘解决

之前在 XPageActivity 外面做了简略的页面点击解决:当用户点击到非输入框区域就暗藏键盘。

然而这样做了之后发现成果并不是很好,因为有些页面可能并不需要这个性能,如果把这个写到 Activity 外面的话,那么在这个 Activity 下的所有 Fragment 都将领有这个性能,这样十分不灵便。

除此之外,使用者可能也想自定义屏幕的触摸事件,因而我对此做了从新设计,将触摸事件的解决下放至每个 Fragment 之中,由 Activity 调用指定的办法进行解决。

相干链接

  • 史上最不便的 Android 页面框架 XPage 使用指南
  • Navigation 和 XPage 框架相比谁更香
  • XPage 我的项目地址:https://github.com/xuexiangjys/XPage

最初

非常感谢大家对 XPage 的反对,喜爱的小伙伴能够到我的项目的 Github 主页:https://github.com/xuexiangjys/XPage 点击 star 反对一下哦!

更多资讯内容,欢送微信搜寻公众号:「我的 Android 开源之旅」

正文完
 0