去年就一直想搭建一個框架边器,可惜一直拖到年初才有時間撼短。熟話說基礎不牢地洞山搖靶擦。當然我們要先看項目的組織結構腮考,既然是一個通用的庫,就只能包括通用的模塊玄捕。
項目模塊劃分
|- base
|-BaseApplication
|-BaseActivity
|-BaseFragment
|-BaseActivityLifeCircleCallbak
|-db
|-message
|-network
|-okhttp
|-retrofit
|-HttpManager
|-utils
|-ARouter
|-monitor
這里我們對于模塊的組合采用了Router方式踩蔚,對于ios或者前端的人士應該比較熟悉,對模塊復用解耦比較簡單枚粘,大體項目結構如上馅闽,有些小的類就沒寫了,以及一些模塊持續(xù)更新中馍迄。下面的講解就以我的添加順序來了
Network
網絡模塊在一個App中屬于大模塊福也,而且相對獨立,所以我選擇首先建立攀圈。這里選取了Retrofit+Okhttp3的插件暴凑,本身都是square出品的Retrofit與其說是為了OkHttp而生的也不過分,關于OkHttp的分析請參考我的另一篇OkHttp核心理解,關于Retrofit是基于主流請求都是restful風格赘来,使用請參考Retrofit使用沒什么大的東西搬设,主要是對一些注解的理解。
這里可操作性比較大的就在與我們的OkHttp里面撕捍,他的精髓大概就是Interceptor了吧拿穴。
因為網絡模塊可以分為日志,緩存忧风,Https默色,授權(廢棄)。
- 日志模塊我們往OkHttpClient里加了一個ApplicationInterceptor狮腿,獲取request以及Response的信息腿宰,這里日志我用了自己上傳Jcenter的項目,比Logger難看點,附上github地址BullDog
- 緩存這邊情況比較復雜缘厢,大概講下遇到的困難吃度,后來就放棄了第一種方案,本來如果設計合理的話贴硫,我們其實可以使用Interceptor直接實現(xiàn)無網絡使用緩存椿每,有網絡更新的策略伊者。但是由于OkHttp的默認緩存是只支持Get的與我們的實際情況有所出入,反正我也講下合理情況下的方案间护。
首先我們需要了解下DiskLruCache,這個本來是一個開源項目亦渗,使用文件緩存,被google官方推薦汁尺,也被OkHttp內置了法精,區(qū)別就是OkHttp是用了Okio的流處理(Sink,SourceOkio使用痴突,基本操作不難)搂蜓,需要了解DiskLruCache的請自行Google,由于本身的Api不太友好我進行了二次封裝DiskLruCacheHelper
先講下設計合理的處理辽装,本身我們的請求可以帶Cache-Control這個頭帮碰,在無網條件下只要我們在request內加入CacheControl.FORCE_CACHE這個參數(shù),OkHttp內部會自行查詢有無該緩存進行返回
為什么說Get才需要緩存呢如迟,其實我們緩存需要一個Key,如果是get的話我們直接可以把url作為key攻走,但是如果是post其實我們區(qū)分key的是post body里面的信息殷勘,這樣就比較難處理,比如一個人的信息昔搂,get的話是帶uid這類標識符的玲销,post的話我們就無法區(qū)分是哪個人的信息了。然后是解決方法摘符,我這邊的網絡請求是加了一層殼的贤斜,我們知道Retrofit結合Okhttp返回的結果是Observable,所以我們在Subscriber里面做文章逛裤,在返回接過錢我們既可以捕捉異常瘩绒,又可以加緩存,這邊的請求我多了一個cacheKey的參數(shù)带族,可以自由配置我們需要緩存的信息锁荔。根據(jù)我們的策略我們先判斷有無網,有網則更新緩存蝙砌,無網則取出緩存返回阳堕。
- Retrofit模塊,這個模塊沒什么好講择克,主要是用于寫請求的恬总,以及一些基本配置
- 最后加了個BaseNetWorkCallbackBean用來規(guī)范我們的網絡返回
總結,這邊模塊分的比較多肚邢,主要遵循組合大于繼承壹堰,有利于以后的拆解閱讀。
DB
Db模塊以前我們用的大多是core data比如說greenDao,我用的這個realm在ios平臺用的比較多缀旁,網上有關于這塊區(qū)別的講解记劈,主要還是從效率上來講,realm明顯要高出core data這塊很多并巍,有點缺陷就是對bean的污染目木,每個bean都要繼承RealmObject,照某網友的說法就是強奸代碼懊渡。Realm官方網站刽射。
我這邊主要是對Realm進行了簡單的封裝,對一些常用操作簡單化剃执,比方說insert誓禁,query,delete由于Realm不支持sql語句查詢肾档,所以自由度相對也就差點摹恰,不過移動端不像后端需要復雜查詢各種外鍵條件的,我封裝的幾個查詢應該已經滿足日常使用了怒见,此外Realm還支持異步俗慈,但有一點千萬要注意,Realm的操作類并非單例遣耍,你獲取一個闺阱,他就線索樹里面多一個引用,所以每次使用完記得釋放舵变,這里我是在BaseActivity的生命周期內控制的酣溃,只不過異步的情況要復雜一點,在api封裝上也是講realm對象作為參數(shù)傳入纪隙,在最后進行釋放赊豌。
Base
這個是比較基礎的部分,為什么不選擇先建是因為有些東西是隨著模塊的加入逐步完善的绵咱,比方說前面提到的Realm亿絮,還有后面的RxBus。
- BaseApplication麸拄,這個類的核心在于一些插件的初始化派昧。涉及到這個如果我們的插件比較多,勢必影響到APP的啟動速度拢切,所以我們最簡單的處理就是啟用線程池蒂萎,引入異步初始化,但是需要注意有些操作是一開始就需要的只能放在同步操作里淮椰,比方說Log框架的加載五慈。這里我封裝了一個ApplicationInitializer纳寂,主要的就是使用cacheThreadPool來控制初始化,分離的目的也是為了單一職責泻拦。以下為一些類的初始化
BLog.init();
/* 其他初始化任務無時序要求可多線程處理 */
cachedThreadScheduler.execute(new Runnable() {
@Override
public void run() {
Realm.init(application);
RealmConfiguration realmConfiguration = new RealmConfiguration.Builder()
.name(DB_NAME)
.deleteRealmIfMigrationNeeded()
.build();
Realm.setDefaultConfiguration(realmConfiguration);
}
});
cachedThreadScheduler.execute(new Runnable() {
@Override
public void run() {
Fresco.initialize(application);
}
});
cachedThreadScheduler.execute(new Runnable() {
@Override
public void run() {
// LeakCanary.install(application);
}
});
cachedThreadScheduler.execute(new Runnable() {
@Override
public void run() {
// BlockCanary.install(application,new AppBlockCanaryContext()).start();
}
});
cachedThreadScheduler.execute(new Runnable() {
@Override
public void run() {
ARouter.init(application);
}
});
除了初始化這塊毙芜,ICE_CREAM_SANDWICH以后我們可以通過在Application內注冊ActivityLifeCircleCallbak監(jiān)聽activity的生命周期,后面可以了解到Arouter也可以檢測到争拐,或者第三種方案就是在Application內維持一個List<Activity> activities腋粥,BaseActivity生命周期內去更新這個變量。
- BaseActivity 這個作為跨頁面控制比較重要的結構架曹,可以封裝一些比較常用的方法隘冲,這里我主要維護了一個List<Fragment> fragments,用來管理activity下面的fragment绑雄,因為當前app主流還是單activity多fragment的模式展辞,還有就是保存一些經常使用的變量,比如Realm万牺,Rxbus還有ioc框架的初始化罗珍,這里的ioc我也是自己寫的 比較簡單,只有click脚粟,longclick兩個事件Potato
Utils
這個模塊就大概講解下覆旱,主要都是一些基本的工具包,各個項目通用珊楼,github有提供下載通殃。
Rxbus
類似與EventBus度液,因為本身我們引入了RxJava厕宗,所以用rxjava來實現(xiàn)事件總線是更可取的方法。參考rxbus實現(xiàn)堕担,在post中采用了tag已慢,更符合eventbus的使用習慣,具體就是封裝Event包裹了一個tag對用戶透明霹购,在返回的時候先filter篩選然后采用flatMap進行轉化
MVP
關于mvp模式的實現(xiàn)佑惠,中間想了蠻久,主要是糾結于怎樣使用范型齐疙,在BaseActivity進行P跟V的綁定膜楷,以及P跟M的關聯(lián),參照了google的mvp實現(xiàn)贞奋,使用了contracts使接口或者抽象類更加關系明確赌厅,集中。
這里我們實現(xiàn)了BasePresenter<T>這里的T是用來引入M的轿塔,而V的引入在BaseActivity<V extends BasePresenter>這樣就形成了一個V初試化P特愿,然后P初始化M的鏈仲墨,P就成為了關鍵,當V destroy的時候就P就解綁V揍障,釋放M目养,把業(yè)務邏輯放到P上,插句題外話毒嫡,中間了解了下mvvm也就是m-vm-v的模式癌蚁,直接在布局層就將數(shù)據(jù)跟v通過viewmodel緊密結合了,省掉了findViewById的工作审胚,但個人不太喜歡這種模式匈勋,感覺可讀性較差,分層不是那么明確
monitor
監(jiān)視框架膳叨,已經引入了blockcanary洽洁,leakcanary,接下來要引入aop設計進行關鍵點檢測菲嘴,然后接入友萌饿自,這個就是中國版的firebase當初去外企面試被問倒了,哈哈哈
test
測試框架還沒開始龄坪,主要分單元測試昭雌,espresso ui測試,mock數(shù)據(jù)...測試框架蠻多的健田,最近再看烛卧,按順序來吧
ARouter
路由機制,github上有兩個下載量多的妓局,之所一選擇ali的完全是信仰吧总放,基本就是一些配置,使用說明官網有ARouter好爬,采用這種機制也是偶然在android weekly上看到人家一個老司機用這種框架搭建中小型項目局雄,也算是一種新的嘗試吧。
2017/3/1:更新Mvp
文章未完待續(xù)存炮,盡量月底前完成接下來的工程炬搭,歡迎喜歡這文章的朋友下載我的代碼,幫我發(fā)現(xiàn)問題穆桂,完善框架SpaghettiFrame