前言
作為 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呼叫指定的方法進行處理。
相關連結
最後
非常感謝大家對XPage 的支援,喜歡的小夥伴可以到專案的Github主頁:https://github.com/xuexiangjys/XPage 點選star支援一下哦!
更多資訊內容,歡迎微信搜尋公眾號:「我的Android開源之旅」