做爲 X-Library系列框架 的靈魂所在,XPage 開源兩年以來,一直致力於下降Fragment使用的難度,努力實現一個Activity多Fragment的Android開發模式。java
就在前不久,我就整理了XPage開源這幾年來的使用狀況,寫了一篇《史上最方便的Android頁面框架XPage使用指南》 ,而且還錄了幾期視頻單獨講解了XPage的使用 ,讓愈來愈多地人看到了XPage使用的便捷性。git
但就在前幾天,在交流羣裏忽然有人問我下面幾個問題:github
AppPageConfig
這個類啊?上面的問題讓我忽然認識到一點:並非全部人都對APT技術有所瞭解的。segmentfault
看來我以前參考ARouter
實現的自動註冊功能可能並無完善,難怪ARouter
後來會寫一個arouter-register
插件來實現自動註冊的功能。微信
因而乎,爲了可以讓XPage的自動註冊功能更加完美,我加班加點開發,因而就有了XPage的3.1.1版本--完全的自動化註冊 。框架
在感覺全自動化頁面註冊帶來的便利以前,讓咱們先來感覺一下以前版本的使用。
在3.1.1以前版本,在使用自動註冊功能的時候,仍是須要實現PageConfiguration
接口,並調用編譯時自動生成的頁面配置類「moduleName」+PageConfig 的getPages()方法返回註冊的頁面。ide
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
框架的初衷!優化
不只如此,這樣寫死還會帶來其餘不少問題:ui
@Page
註解修飾Fragment的話,配置類也不會自動生成,這樣會讓不少初次使用者很是疑惑。爲了可以解決以上的問題,我實現了一個自動註冊的配置類AutoPageConfiguration 。那麼下面就看一下最新版本的XPage是如何註冊的吧:this
PageConfig.getInstance() .debug("PageLog") //開啓調試 .setContainActivityClazz(XPageActivity.class) //設置默認的容器Activity,按需設置(非必須) .init(this); //初始化頁面配置
是的,你沒有看錯,這裏沒有手動實現PageConfiguration
接口的部分了,能夠說是真正實現了全自動頁面註冊,是否是很是方便呀?
看到上面的變化,你是否是很是想知道我是如何實現完全的自動化註冊的?
想要回答這個問題,仍是讓咱們先看一看這個編譯時自動生成的配置類是如何實現的。
其實當初實現頁面配置類的自動生成的方案,也是我研讀了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); } }
從源碼中咱們能夠看到,我是這樣作的:
ClassUtils.getClassNames
獲取到com.xuexiang.xpage.config
包下的全部類的類名。這裏的ClassUtils
也是我借鑑了ARouter裏面的源碼。PageConfig
篩選出全部的配置類。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的新版本還有那些地方更新了!
在此以前,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開源之旅」