相關閱讀:
[乾貨]2017已來,最全面試總結——這些Android面試題你一定需要
Android常用開源框架的源碼解讀套路--教你怎麼讀開源框架源碼
關注回覆:Android,iOS,PHP,js,HTML5,Python,Hadoop,c++,J2EE等關鍵字就能免費獲取學習資料視頻
前言
今年的Android技術圈中MVP,Dagger2,Rxjava,Retrofit這些詞彙非常火,隨便打開一個技術論壇都充斥着大量的關於這些技術的文章,Github也充斥着各種以基於MVP+Retrofit+RxJava+Dagger2+MaterialDesign開發的xxxx爲標題的開源項目或Demo.
但是大家這麼熱心的開源此類項目,一直重複的做着同樣的事教授大家使用的方式和技巧有沒有想過依賴一個第三方庫,就可以快速的搭建此類框架?
特性
-
自動生成MVP,Dagger2相關類
-
版本更新
-
更新日誌
-
通用框架,適合所有類型的項目,支持大型項目的開發,Demo的包結構直接可以拿來用
-
全部使用Dagger2管理(將所有模塊使用Dagger連接起來,絕不是簡單的使用)
-
大量使用Rxjava
-
修改包名(common包不要修改)後就可以直接使用,快速接入(老項目接入請按下面的步驟)
-
全部UI自適應
-
圖片加載類ImageLoader使用策略模式和建造者模式,輕鬆切換圖片加載框架和功能擴展
-
Model層提供Retrofit API和RxCache,是否使用緩存自行選擇
-
全局http Request(請求參數,headers) Response(服務器返回的結果,headers,耗時)信息監聽,可解析json後根據狀態碼做相應的全局操作
-
全局Rxjava錯誤處理,錯誤後自動重試,捕捉整個應用的所有錯誤
框架結構
包結構
開發須知
-
開發者需要具有一定的Android開發能力
-
開發者必須有使用Dagger2,Rxjava,Retrofit的經驗,沒使用過也必須瞭解,不然很難使用
Libraries簡介
-
MvpGoogle官方出品的Mvp架構項目,含有多個不同的架構分支(此爲Dagger分支).
-
Dagger2Google根據Square的Dagger1出品的依賴注入框架,通過apt動態生成代碼,性能優於用反射技術依賴注入的框架.
-
Rxjava提供優雅的響應式Api解決異步請求.
-
RxAndroid爲Android提供響應式Api.
-
Rxlifecycle在Android上使用rxjava都知道的一個坑,就是生命週期的解除訂閱,這個框架通過綁定activity和fragment的生命週期完美解決.
-
RxbindingJakeWharton大神的View綁定框架,優雅的處理View的響應事件.
-
RxCache是使用註解爲Retrofit加入二級緩存(內存,磁盤)的緩存庫
-
RetrofitSquare出品的網絡請求庫,極大的減少了http請求的代碼和步驟.
-
Okhttp同樣Square出品,不多介紹,做Android都應該知道.
-
AutolayoutAndroid全尺寸適配框架.
-
GsonGoogle官方的Json Convert框架.
-
ButterknifeJakeWharton大神出品的view注入框架.
-
Androideventbus一個輕量級使用註解的Eventbus.
-
TimberJakeWharton大神出品Log框架,內部代碼極少,但是思想非常不錯.
-
Glide此庫爲本框架默認封裝圖片加載庫,可參照着例子更改爲其他的庫,Api和Picasso差不多,緩存機制比Picasso複雜,速度快,適合處理大型圖片流,支持gfit,Fresco太大了!,在5.0一下優勢很大,5.0以上系統默認使用的內存管理和Fresco類似.
-
Realm速度和跨平臺性使它成爲如今最火的數據庫,美中不足的就是so庫太大
-
LeakCanarySquare出品的專門用來檢測Android和Java的內存泄漏,通過通知欄提示內存泄漏信息
-
RxErroHandlerRxjava錯誤處理庫,可在出現錯誤後重試
1 開發準備
此框架適合自己做定製修改,所有暫時不上傳至Jcenter或Maven,請自行下載或clone
1.1 導入框架
compile project(':arms')
1.2 引用config.build
本框架提供一個引用大量第三方庫的config.gradle文件,用於第三方庫版本管理,將config.gradle複製進根目錄,並在項目的頂級build.gradle中引用它
1.2.1 使用config.build
因爲在頂級build.gradle中引用了它,所以在整個項目的所有build.gradle中都可以使用rootProject.xxx來使用它裏面的內容
也可以使用它來管理一些項目的信息,這樣有多個module也可以直接使用一個信息
1.3 依賴Dagger2
本框架全部使用Dagger2管理,所以必須依賴Dagger2,找到app的build.gradle,加入如下代碼
1.4 配置AndroidManifest
1.4.1 添加權限
1.4.2 配置Autolayout Meta
使用Autolayout 自適應框架必須配置Meta屬性及設計圖的寬高,詳情參考Autolayout
1.4.3 引用Glide自定義屬性
本框架默認使用Glide加載圖片,但提供一個管理器ImageLoader提供統一接口,使用策略者模式可輕鬆替換圖片加載框架,本框架默認提供Glide的自定義緩存配置信息,使用它之前先引用它的自定義配置信息
<!--glide配置-->
<meta-data
android:name="com.jess.arms.widget.imageloader.glide.GlideConfiguration"
android:value="GlideModule"/>
1.5 混淆
由於本框架依賴大量三方庫,所以已經在arms Module下的proguard-rules.pro中提供了所有規則,如果想使用它,請複製它替換app Module中的proguard-rules.pro,混淆時可以根據自己的需求修改或添加規則,混淆前務必注意將Java Bean,自定義組件添加進規則
1.6 版本更新
! 一定不能修改common包的包名,舊版本需要找到和common包下同名的類並刪除,然後重
新引用common包裏的類
如果你獲得本框架的方式是通過clone或者下載:
-
直接可以通過命令行git pull origin master拉取最新的版本並自動合併
-
如果你修改了包名還得執行命令git rm --cache -r app/src/main/java/me/jessyan/mvparms,下次拉取時就不會拉取Demo的內容
如果你獲得本框架的方式是通過fork到自己倉庫後,clone或下載:
-
git remote add arms https://github.com/JessYanCoding/MVPArms.git 添加遠程倉庫,arms是遠程倉庫的代號,可自定義,以後都通過這個代號對遠程倉庫作操作
-
git fetch arms拉取遠程倉庫最新的版本
-
git merge arms/master --allow-unrelated-histories合併遠程倉庫到當前分支
-
後面如果本框架有更新就只用重複2,3步,--allow-unrelated-histories只用在第一次合併時添加
-
如果你修改了包名還得執行命令git rm --cache -r app/src/main/java/me/jessyan/mvparms,下次拉取時就不會拉取Demo的內容
2 快速開始
2.1 繼承BaseApplication
新建項目的Application繼承自BaseApplication,並在AndroidManifest中聲明
2.1.1 AppComponent
Application生命週期是和App是一樣的,所以是適合提供一些單例對象,本框架使用Dagger2管理,所以使用AppComponent來提供全局所有的單例對象
創建AppComponent接口
-
構造AppComponent對象
-
ServiceModule(提供RetrofitApi)和CacheModule(提供緩存)都需自行創建,詳情ServiceModule(2.1.2) ,CacheModule(2.1.3)
2.1.2 ServiceModule
ServiceModule提供RetrofitApi對應的Service,這些Service對象在AppComponent中注入ServiceManager(需繼承BaseServiceManager)中統一管理
-
自行定義Retrofit Service如下,熟練Retrofit請忽略
public interface CommonService {
String HEADER_API_VERSION = "Accept: application/vnd.github.v3+json";
@Headers({HEADER_API_VERSION})
@GET("/users")
Observable<List<User>> getUsers(@Query("since") int lastIdQueried, @Query("per_page") int perPage);
}
-
定義ServiceModule,這裏使用Retrofit對象(ClientModule提供)實例化Service接口,提供所有Service對象(可以根據不同的邏輯劃分多個Service接口)
@Module
public class ServiceModule {
@Singleton
@Provides
CommonService provideCommonService(Retrofit retrofit) {
return retrofit.create(CommonService.class);
}
}
-
AppComponent將所有的Service注入到ServiceManager中,所有Model層都可以拿到此對象,意味着每個Model都可以請求任意Api
2.1.3 CacheModule
Cache層默認使用RxCache,CacheModule提供RetrofitApi對應的Cache對象,這些Cache對象在AppComponent中注入CacheManager(需繼承BaseCacheManager)中統一管理
-
自行定義RxCache Provider如下,熟練RxCache請忽略
public interface CommonCache {
@LifeCache(duration = 2, timeUnit = TimeUnit.MINUTES)
Observable<Reply<List<User>>> getUsers(Observable<List<User>> oUsers, DynamicKey idLastUserQueried, EvictProvider evictProvider);
}
-
定義CacheModule,這裏使用RxCache對象(ClientModule提供)實例化所有Cache接口,提供所有Cache對象
@Module
public class CacheModule {
@Singleton
@Provides
CommonCache provideCommonService(RxCache rxCache) {
return rxCache.using(CommonCache.class);
}
}
-
AppComponent將所有的Cache注入到CacheManager中,所有Model層都可以拿到所有的Cache對象
2.2 繼承BaseActivity
讓項目的基類Activity繼承BaseActivity,BaseActivity默認注入Presenter,所以如果要使用Presenter必須指定對應的範型,並且提供注入Presenter所需要的Component
2.3 繼承BaseFragment
讓項目的基類Fragment繼承BaseFragment,BaseFragment默認注入Presenter,所以如果要使用Presenter必須指定對應的範型,並且提供注入Presenter所需要的Component
2.4 MVP實戰
定義業務邏輯MVP,繼承MVP各自的基類即可,這裏可以稍微粗力度的定義MVP類,即無需每個Fragment和Activity(每個頁面)都定義不同的MVP類,可以按照相同的業務邏輯使用一組MVP類
2.4.1 Contract
這裏根據Google官方的MVP項目,可以在Contract中定義MVP的接口,便於管理,此框架無需定義Presenter接口,所以Contract只定義Model和View的接口
2.4.2 View
一般讓Activity或Fragment實現Contract中定義的View接口,供Presenter調用對應方法操作UI,BaseActivity默認注入Presenter,如想使用Presenter,必須指定Presenter的範型,和實現setupActivityComponent來提供Presenter需要的Component和Module
2.4.3 Model
Model實現Contract的Model接口,並且繼承BaseModel,指定範型爲,上面定義的ServiceManager和CacheManager,然後通過兩個Manager拿到需要的Service和Cache爲Presenter提供需要的數據(是否使用緩存請自行選擇)
2.4.4 Presenter
Presenter在MVP中的大部分的作用爲通過從Model層接口獲取數據,在調用View層接口顯示數據,首先實現BasePresenter,指定Model和View的範型,注意一定要指定Contract中定義的接口,Presenter需要的Model和View,都使用Dagger2注入,這樣即解藕又方便測試,怎麼注入?
2.4.5 MVP Module
這裏的Module提供當前業務邏輯對應的View和Model接口(Contract中定義的接口)的實現類,Model需要AppComponent中提供的ServiceManager和CacheManager來實現網絡請求和緩存,所以需要通過Component依賴AppComponent拿到這兩個Manager
2.4.6 MVP Component
這裏需要注意的是此Component必須依賴AppComponent,這樣才能提供Model需要的ServiceManager和CacheManager,提供inject()方法就能將Module及AppComponent中提供的對象注入到對應的類中,inject()中的參數不能是接口,怎麼注入?
@ActivityScope
@Component(modules = UserModule.class,dependencies = AppComponent.class)
public interface UserComponent {
void inject(UserActivity activity);
}
2.4.7 Dagger Scope
在上面的代碼中ActivityScope大量出現在Module和Component中,Dagger2使用Scope限制每個Module中提供的對象的生命,Dagger2默認只提供一個@SingletonScope即單例,本框架提供@ActvityScope和@FragmentScope,如有其他需求請自行實現,Module和Component定義相同的Scope後Module中提供的對象的生命週期會和Component中一樣(即在Component生命週期內,如需使用到Moudle中提供的對象,只會調用一次@Provide註解的方法得到此對象)
2.4.8 MVP總結
-
以後每個業務邏輯都重複構造這些類,只是換個名字而已,值得注意的是MVP剛開始用時確實會覺得平白無故多了很多類,非常繁瑣麻煩,但是等頁面代碼邏輯越來多時,你會發現其中的好處,邏輯清晰,解耦,便於團隊協作,測試容易,錯誤好定位,所以現在本框架提供Template自動生成代碼解決這個痛點,讓開發者更加愉快的使用本框架
3 功能使用
3.1 App全局配置信息(使用Dagger注入)
GlobeConfigModule使用建造者模式將App的全局配置信息封裝進Module(使用Dagger注入到需要配置信息的地方),可以配置CacheFile,InterCeptor等,因爲使用的是建造者模式所以如你有其他配置信息需要使用Dagger注入,直接就可以添加進Builder並且不會影響到其他地方
//如需添加個Boolean字段提供給Log工具類,來判斷是否打印Log
@Module
public class GlobeConfigModule {
private Boolean isLog;
private GlobeConfigModule(Buidler buidler) {
this.isLog = builder.isLog
}
public static Buidler buidler() {
return new Buidler();
}
public static final class Buidler {
private Boolean isLog;
private Buidler() {}
//1.給Builder中添加個方法接受isLog字段
public Buidler isLog(Boolean isLog) {
this.isLog = isLog;
return this;
}
public GlobeConfigModule build() {
return new GlobeConfigModule(this);
}
}
//2.使用@Provides,將isLog返回出去,供Dagger注入到Log工具類
@Singleton
@Provides
Boolean provideIsLog() {
return isLog;
}
}
3.2 全局捕捉Http請求和響應
通過GlobeConfigModule.globeHttpHandler()方法傳入GlobeHttpHandler
@Override
protected GlobeConfigModule getGlobeConfigModule() {
return GlobeConfigModule
.buidler()
.baseurl(Api.APP_DOMAIN)
.globeHttpHandler(new GlobeHttpHandler() {// 這裏可以提供一個全局處理http響應結果的處理類,
// 這裏可以比客戶端提前一步拿到服務器返回的結果,可以做一些操作,比如token超時,重新獲取
@Override
public Response onHttpResultResponse(String httpResult, Interceptor.Chain chain, Response response) {
//這裏可以先客戶端一步拿到每一次http請求的結果,可以解析成json,做一些操作,如檢測到token過期後
//重新請求token,並重新執行請求
try {
if (!TextUtils.isEmpty(httpResult)) {
JSONArray array = new JSONArray(httpResult);
JSONObject object = (JSONObject) array.get(0);
String login = object.getString("login");
String avatar_url = object.getString("avatar_url");
Timber.tag(TAG).w("result ------>" + login + " || avatar_url------>" + avatar_url);
}
} catch (JSONException e) {
e.printStackTrace();
return response;
}
//這裏如果發現token過期,可以先請求最新的token,然後在拿新的token放入request裏去重新請求
//注意在這個回調之前已經調用過proceed,所以這裏必須自己去建立網絡請求,如使用okhttp使用新的request去請求
// create a new request and modify it accordingly using the new token
// Request newRequest = chain.request().newBuilder().header("token", newToken)
// .build();
// // retry the request
//
// response.body().close();
//如果使用okhttp將新的請求,請求成功後,將返回的response return出去即可
//如果不需要返回新的結果,則直接把response參數返回出去
return response;
}
// 這裏可以在請求服務器之前可以拿到request,做一些操作比如給request統一添加token或者header
@Override
public Request onHttpRequestBefore(Interceptor.Chain chain, Request request) {
//如果需要再請求服務器之前做一些操作,則重新返回一個做過操作的的requeat如增加header,不做操作則返回request
//return chain.request().newBuilder().header("token", tokenId)
// .build();
return request;
}
})
.build();
}
3.3 全局錯誤處理及發生錯誤時重新執行
如果需要使用Rxjava的全局錯誤處理,需通過GlobeConfigModule.responseErroListener()方法傳入ResponseErroListener,並在每次使用Rxjava調用subscribe時,使用ErrorHandleSubscriber,並傳入AppComponent中提供的RxErrorHandler,此Subscribe,默認已經實現OnError方法,如想自定義可以重寫OnError方法
-
在Rxjava中使用
3.4 切換圖片請求框架
本框架默認使用Glide實現圖片加載功能,使用ImagerLoader提供統一的接口,ImagerLoader使用策略模式和建造者模式,可以動態切換圖片框架(比如說切換成Picasso),並且加載圖片時傳入的參數也可以隨意擴展(loadImage方法在需要擴展參數時,也不需要改動,全部通過Builder擴展,比如你想讓內部的圖片加載框架,清除緩存你只需要定義個boolean字段,內部根據這個字段if|else,其他操作同理)
-
使用ImageLoader必須傳入一個實現了BaseImageLoaderStrategy接口的圖片加載實現類從而實現動態切換,所以首先要實現BaseImageLoaderStrategy,實現時必須指定一個繼承自ImageConfig的實現類,使用建造者模式,可以儲存一些信息,比如URL, ImageView,Placeholder等,可以不斷的擴展,供圖片加載框架使用
-
實現ImageCofig使用建造者模式
public class PicassoImageConfig extends ImageConfig{
private PicassoImageConfig(Buidler builder) {
this.url = builder.url;
this.imageView = builder.imageView;
this.placeholder = builder.placeholder;
this.errorPic = builder.errorPic;
}
public static Buidler builder() {
return new Buidler();
}
public static final class Buidler {
private String url;
private ImageView imageView;
private int placeholder;
protected int errorPic;
private Buidler() {
}
public Buidler url(String url) {
this.url = url;
return this;
}
public Buidler placeholder(int placeholder) {
this.placeholder = placeholder;
return this;
}
public Buidler errorPic(int errorPic){
this.errorPic = errorPic;
return this;
}
public Buidler imagerView(ImageView imageView) {
this.imageView = imageView;
return this;
}
public PicassoImageConfig build() {
if (url == null) throw new IllegalStateException("url is required");
if (imageView == null) throw new IllegalStateException("imageview is required");
return new PicassoImageConfig(this);
}
}
}
-
在ImageLoader構造時可以傳入PicassoImageLoaderStrategy(),也可以通過AppComponent拿到ImageLoader對象後,setLoadImgStrategy(new PicassoImageLoaderStrategy)替換之前的實現(默認使用Glide)
3.5 AndroidEventBus Tag
本框架使用AndroidEventBus實現事件總線,此框架使用註解標記目標方法,統一將Tag的常量寫到EventBusTag接口中,便於管理,如果要在當前對象中使用AndroidEventBus請在需要使用的Activity,Fragment,Presenter中重寫useEventBus(),返回true代表使用,默認返回true
3.6 AutoLayout組件
本框架使用AutoLayout框架,實現控件自適應,此框架要讓組件自適應,必須讓它的父控件,重新測量,和重寫LayoutParams,而官方只默認提供了三個ViewGroup,AutoRelativeLayout,AutoLinearLayout,AutoFrameLayout實現了這些操作,爲了方便開發者使用,本框架提供了一些常用的AutoLayout組件,在框架的widget包下的autolayout包中,在xml中引用即可使子控件自適應,並且還提供一個 Template(在最後面)用於生成自適應所需要的的Auto系列View,如需要使ScrollView的子控件自適應,使用此Template輸入ScrollView,即可生成AutoScrollView,在xml中引用即可
3.7 自定義PopupWindow
框架提供一個建造者模式的自定義PopupWindow組件CustomPopupWindow,自己實現佈局後就可以直接使用這個實現PopupWindow,使用建造者模式,隨意擴展自定義參數
3.8 快速實現RecycleView
本框架提供DefaultAdapter和BaseHolder基類快速實現Recycleview.
-
BaseHolder默認初始化了ButterKnife和AutoLayout,繼承後不僅可以直接注入View,佈局還可以自適應屏幕
-
RecycleView默認是不提供Item的點擊事件的,使用DefaultAdapter調用setOnItemClickListener可以實現Item的點擊事件
3.9 權限管理(適配Android6.0權限管理)
本框架使用RxPermissions用於權限管理(適配android6.0),並提供PermissionUtil工具類一行代碼實現權限請求.適配Android6.0權限管理詳解
PermissionUtil.launchCamera(new RequestPermission() {
@Override
public void onRequestPermissionSuccess() {
launchCapture();//請求權限成功後做一些操作
}
}, mRxPermissions, mRootView, mErrorHandler);
3.10 Gradle配置啓動DeBug模式
在主項目(app)的build.gradle中配置是否開啓打印Log或則是否使用LeakCanary,等調試工具
-
在build.gradle中配置
-
在代碼中使用(比如在application中做一些初始化設置)
3.11 AppManager(管理所有的Activity)
AppManager用於管理所有的Activity,內部持有一個含有所有存活的Activity(未調用onDestroy)的List,和一個當前在最前端的Activity(未調用onPause),AppManager封裝有多種方法,可以很方便的對它們進行操作,也可以在未持有AppManager的情況下,通過EventBus遠程遙控它的所有方法,這樣我們可以在整個app的任何地方對任何Activity進行全局操作,比如在app請求網絡超時時讓最前端的Activity顯示連接超時的交互頁面(這個邏輯不用寫到當前請求的Activity裏,可以在一個單例類裏做全局的統一操作,因爲可以隨時通過AppManager拿到當前的Activity)
-
遠程遙控通過EventBuspost Message實現,通過不同的what區分不同的方法和Handler同理,可以根據自己的需求適當的在AppManager中添加對應的方法
總結
如果你是構建一個全新的項目,直接將整個項目clone(或者下載)下來,直接將Demo當成主Module,再將項目包名改成自己的包名,Demo包含可以直接使用的包結構,一個主流的MVP+Dagger2+Retrofit+Rxjava框架就這樣輕鬆的構建成功了,現在你參考Demo Mvp包下的UserActivity的格式,使用Template在對應包下自動生成MVP,Dagger2相關類,配合查閱Wiki文檔慢慢掌握本框架,看再多文章不如早點在項目中使用它,在實踐中學習總是最快的.
關於Java和Android大牛頻道
Java和Android大牛頻道是一個數萬人關注的探討Java和Android開發的公衆號,分享和原創最有價值的乾貨文章,讓你成爲這方面的大牛!
我們探討android和Java開發最前沿的技術:android性能優化 ,插件化,跨平臺,動態化,加固和反破解等,也討論設計模式/軟件架構等。由一羣來自BAT的工程師組成的團隊。
關注即送紅包,回覆:“百度” 、“阿里”、“騰訊” 有驚喜!!!關注後可用入微信羣。羣裏都是來自百度阿里騰訊的大牛。
歡迎關注我們,一起討論技術,掃描和長按下方的二維碼可快速關注我們。或搜索微信公衆號:JANiubility。
公衆號:JANiubility