概述
原來一直在用Android最原生的框架進行開發(fā)夫嗓,最多也就使用了butterknife,減少了很多的findviewById删窒。前段時間看google的IO大會宏多,偶爾聽到了新型的Android開發(fā)框架dagger2等等萧求,然后對此框架產(chǎn)生了濃厚的興趣腕巡。
通過一段時間的深入學(xué)習(xí),把我的學(xué)習(xí)分享出來嫩挤,希望大家能夠喜歡害幅。
mvp+dagger2+retrofit2+rxjava 一套開發(fā)模式自我感覺將是以后Android開發(fā)的趨勢,盡早的用起來吧岂昭。
使用新型框架能給我們帶來什么好處以现?
- 解耦,降低模塊耦合度约啊。
- 可以更方便的寫單元測試邑遏。
- 減少Activity編碼
- 提高團隊協(xié)作的效率
- 提高編碼的效率
- 提高代碼的可讀性
示例:
本文示例功能:
- retrofit2+Rxjava進行Http和Https網(wǎng)絡(luò)請求封裝
- MVP工程結(jié)構(gòu)
- Rxjava的使用示例
- dagger2的使用示例
說明
閱讀此文首先你要對以下技術(shù)有一定的了解。對以上技術(shù)還不熟悉的朋友可以先去了解一下恰矩。
在我閱讀過無數(shù)相關(guān)技術(shù)文章之后无宿,我給大家推薦這些技術(shù)學(xué)習(xí)的文章:
dagger2
理論:http://android.jobbole.com/82694/
實例(網(wǎng)絡(luò)上沒有找到合適的,我自己寫的一篇博文):http://www.reibang.com/p/269c3f70ec1e
官方:http://google.github.io/dagger/
mvp:
這個理論很簡單枢里,自己百度或者google吧
示例:https://github.com/googlesamples/android-architecture
retrofit2:
官方:http://square.github.io/retrofit/
rxjava:
偏理論:http://gank.io/post/560e15be2dca930e00da1083
偏實踐:http://blog.chinaunix.net/uid-20771867-id-5187376.html
對上面的技術(shù)有一定的了解后,我們開始一個示例:
架構(gòu)搭建
首先我們要一個示例的方式來詳細說明整體項目的架構(gòu)與思想
示例功能蹂午;
- 登錄功能
- 檢查用戶名和密碼是否合法
- 登錄按鈕如果不合法則不可點擊栏豺,合法后登錄按鈕可以點擊
- 調(diào)用登錄接口進行登錄
- 將用戶名和密碼保存本地
- 文章列表
- 從網(wǎng)絡(luò)獲取文章列表并展示
- 將文章列表保存到數(shù)據(jù)庫
- 點擊列表進入文章詳情
- 網(wǎng)絡(luò)獲取圖片
- 單元測試
- 集成測試
- 單元測試
整體架構(gòu)圖
運行webserver json
//安裝json-server
$ npm install -g json-server
//進入工程目錄
$ cd AndroidArchitecture/
//運行服務(wù)
json-server --watch login.json
運行后可以通過此地址訪問接口
http://localhost:3000/users
http://localhost:3000/topics
關(guān)于工程
由于工程代碼較多,在這里就不一一將代碼貼出了豆胸,詳細的demo地址見我的github
https://github.com/wlj32011/AndroidArchitecture
下圖為demo目錄接口奥洼,查看demo源碼可以參考此結(jié)構(gòu)
關(guān)鍵代碼
-
網(wǎng)絡(luò)請求返回消息體統(tǒng)一錯誤處理
消息體結(jié)構(gòu)
//登錄
{
"status_msg" : "登錄成功",
"status_code" : 200,
"data" : {
"username" : "admin",
"id" : 1,
"password" : "123456",
"gender" : "男"
}
}
模型結(jié)構(gòu)
public class BaseResponse<T> {
private int status_code;
private String status_msg;
private T data;
public int getStatus_code() {
return status_code;
}
public void setStatus_code(int status_code) {
this.status_code = status_code;
}
public String getStatus_msg() {
return status_msg;
}
public void setStatus_msg(String status_msg) {
this.status_msg = status_msg;
}
public T getData() {
return data;
}
public void setData(T data) {
this.data = data;
}
}
使用Rxjava對象變換
public class BaseResponseFunc<T> implements Func1<BaseResponse<T>, Observable<T>> {
@Override
public Observable<T> call(BaseResponse<T> tBaseResponse) {
//遇到非200錯誤統(tǒng)一處理,將BaseResponse轉(zhuǎn)換成您想要的對象
if (tBaseResponse.getStatus_code() != 200) {
return Observable.error(new Throwable(tBaseResponse.getStatus_msg()));
}else{
return Observable.just(tBaseResponse.getData());
}
}
}
自定義訂閱者
/**
* 錯誤統(tǒng)一處理
*
* Created by wanglj on 16/7/4.
*/
public class ExceptionSubscriber<T> extends Subscriber<T> {
private SimpleCallback<T> simpleCallback;
private Application application;
public ExceptionSubscriber(SimpleCallback simpleCallback, Application application){
this.simpleCallback = simpleCallback;
this.application = application;
}
@Override
public void onStart() {
super.onStart();
if(simpleCallback != null)
simpleCallback.onStart();
}
@Override
public void onCompleted() {
if(simpleCallback != null)
simpleCallback.onComplete();
}
@Override
public void onError(Throwable e) {
e.printStackTrace();
if (e instanceof SocketTimeoutException) {
Toast.makeText(application, "網(wǎng)絡(luò)中斷,請檢查您的網(wǎng)絡(luò)狀態(tài)", Toast.LENGTH_SHORT).show();
} else if (e instanceof ConnectException) {
Toast.makeText(application, "網(wǎng)絡(luò)中斷晚胡,請檢查您的網(wǎng)絡(luò)狀態(tài)", Toast.LENGTH_SHORT).show();
} else {
Toast.makeText(application, "error:" + e.getMessage(), Toast.LENGTH_SHORT).show();
}
if(simpleCallback != null)
simpleCallback.onComplete();
}
@Override
public void onNext(T t) {
if(simpleCallback != null)
simpleCallback.onNext(t);
}
}
簡單的回調(diào)模型
public interface SimpleCallback<T> {
void onStart();
void onNext(T t);
void onComplete();
}
presenter層調(diào)用
public void login(String username,String password){
apiManager.login(username, password, new SimpleCallback<User>() {
@Override
public void onStart() {
loginView.showLoading();
}
@Override
public void onNext(User user) {
loginView.showUser(user);
}
@Override
public void onComplete() {
loginView.hideLoading();
}
});
}
- 對外提供ApiManager以及retrofit的封裝
@Module
public class ApiModule {
@Provides
@Singleton
public OkHttpClient provideOkHttpClient() {
final OkHttpClient.Builder builder = new OkHttpClient.Builder();
//添加logo日志打印網(wǎng)絡(luò)請求的攔截器
if (BuildConfig.DEBUG) {
HttpLoggingInterceptor logging = new HttpLoggingInterceptor();
logging.setLevel(HttpLoggingInterceptor.Level.BODY);
builder.addInterceptor(logging);
}
builder.connectTimeout(60 * 1000, TimeUnit.MILLISECONDS)
.readTimeout(60 * 1000, TimeUnit.MILLISECONDS);
return builder.build();
}
@Provides
@Singleton
public Retrofit provideRestAdapter(OkHttpClient okHttpClient) {
Retrofit.Builder builder = new Retrofit.Builder();
builder.client(okHttpClient)
.baseUrl(ApiService.SERVER_URL)
.addCallAdapterFactory(RxJavaCallAdapterFactory.create())
.addConverterFactory(GsonConverterFactory.create());
return builder.build();
}
@Provides
@Singleton
public ApiService provideApiService(Retrofit restAdapter) {
return restAdapter.create(ApiService.class);
}
@Provides
@Singleton
public ApiManager provideApiManager(Application application,ApiService githubApiService) {
return new ApiManager(githubApiService,application);
}
}
- 所有的全局共用對象都可以在AppModule里對外提供灵奖,比如PreferencesManager DatabaseManager等等
更高級的用法--dagger2 劃分更細的scope
目前demo示例是將功能模塊直接依賴于整個APP,其實我們可以劃分更細的作用域估盘。使一個對象的生命周期存在于多個功能模塊中瓷患。
比如:項目中登錄成功后,獲取文章列表需要用戶信息遣妥,獲取文章詳情以及文章下的評論列表擅编,又需要當前文章和用戶的信息。那么我們就可以這樣設(shè)計我們的工程架構(gòu)如圖:
后記
由于寫的比較倉促,架構(gòu)圖中的紅色字體部分還未實現(xiàn)爱态,更細的scope還沒有在demo中體現(xiàn)出來谭贪,如果您對此感興趣,請關(guān)注锦担,后續(xù)會陸續(xù)更新俭识。
由于表達能力有限,可能有些地方解釋的不是很清楚洞渔,歡迎在下方評論套媚,一起討論一起進步~