個人博客:haichenyi.com。感謝關注
??上一篇,我們把mvp+dagger加進去了,這一篇,我們把網(wǎng)絡請求加上
??我這里的網(wǎng)絡請求是用的裝飾者模式去寫的,什么是裝飾者模式呢?在不必改變原類文件和使用繼承的情況下,動態(tài)地擴展一個對象的功能。它是通過創(chuàng)建一個包裝對象,也就是裝飾來包裹真實的對象。我的理解就是一個接口,兩個實現(xiàn)類,一個實現(xiàn)類負責調用接口的方法,另一個類負責功能的具體實現(xiàn)。本文中所提到的代碼都是偽代碼,最后會給出完整的,最初版本的項目框架。不包含任何業(yè)務邏輯

??容我一個一個來說,首先,我們一般請求網(wǎng)絡的時候,會有統(tǒng)一的返回數(shù)據(jù)格式,一個是需要判斷返回code碼的,就比方說登錄功能,那登錄成功,還是失敗,我們只用判斷code碼即可,這種類型,我們統(tǒng)一是HttpNoResult。還有一個是返回數(shù)據(jù)的,就比方說查一個列表數(shù)據(jù)。這里我們統(tǒng)一的是HttpResult。我先給出這兩個類的代碼:
package com.haichenyi.myproject.model.http;
/**
* Author: 海晨憶
* Date: 2018/2/23
* Desc:沒有解析數(shù)據(jù)的返回
*/
public class HttpNoResult {
private int code;
private String msg;
public int getCode() {
return code;
}
public HttpNoResult setCode(int code) {
this.code = code;
return this;
}
public String getMsg() {
return msg;
}
public HttpNoResult setMsg(String msg) {
this.msg = msg;
return this;
}
@Override
public String toString() {
return "HttpNoResult{" + "code=" + code + ", msg='" + msg + '\'' + '}';
}
}
package com.haichenyi.myproject.model.http;
import com.google.gson.annotations.SerializedName;
/**
* Author: 海晨憶
* Date: 2018/2/23
* Desc:有解析數(shù)據(jù)的返回
*/
public class HttpResult<T> {
private int code;
private String msg;
@SerializedName(value = "result")
private T data;
public int getCode() {
return code;
}
public HttpResult setCode(int code) {
this.code = code;
return this;
}
public String getMsg() {
return msg;
}
public HttpResult setMsg(String msg) {
this.msg = msg;
return this;
}
public T getData() {
return data;
}
public HttpResult setData(T data) {
this.data = data;
return this;
}
@Override
public String toString() {
return "HttpResult{" + "code=" + code + ", msg='" + msg + '\'' + ", data=" + data + '}';
}
}
??這里我就需要說一點,有數(shù)據(jù)返回的時候,每個數(shù)據(jù)類型都是不一樣的,所以,這里我用的泛型傳遞,不同的數(shù)據(jù)類型,傳不同的bean對象
??言歸正傳,我們來說說網(wǎng)絡請求的一個接口,兩個實現(xiàn)類。
一個接口—HttpHelper
package com.haichenyi.myproject.model.http;
import io.reactivex.Flowable;
/**
* Author: 海晨憶
* Date: 2018/2/23
* Desc:網(wǎng)絡接口,接口參數(shù)Token統(tǒng)一處理,方法中不傳Token
*/
public interface HttpHelper {
/**
* 登錄時獲取驗證碼.
*
* @param phone 手機號
* @return {"code":0}
*/
Flowable<HttpNoResult> loginCode(String phone);
/*Flowable<HttpResult<Login>> login(String phone, String code);
Flowable<HttpResult<List<DiyBean>>> diyKeys(String allId);*/
}
??Flowable是RxJava2.0新增的,所以說RxJava完美兼容Retrofit,泛型就是我們需要解析的數(shù)據(jù)
loginCode方法是說返回數(shù)據(jù),我們只用判斷是否是成功還是失敗,
login方法是說返回數(shù)據(jù)是一個Login對象,至于對象是什么內容,那就是和你們后臺確認了
diyKeys方法就是說,返回數(shù)據(jù)是一個list對象,每個list的item是DiyBean對象
package com.haichenyi.myproject.model;
import com.haichenyi.myproject.model.http.HttpHelper;
import com.haichenyi.myproject.model.http.HttpNoResult;
import io.reactivex.Flowable;
/**
* Author: 海晨憶
* Date: 2018/2/23
* Desc:網(wǎng)絡請求的實現(xiàn)類
*/
public class DataHelper implements HttpHelper {
private HttpHelper http;
public DataHelper(HttpHelper http) {
this.http = http;
}
@Override
public Flowable<HttpNoResult> loginCode(String phone) {
return http.loginCode(phone);
}
}
??DataHelper是HttpHelper的實現(xiàn)類,他的唯一作用就是調用接口的方法即可,具體的功能實現(xiàn)是后面一個類,這里需要說明的是這個類的構造方法要public表示,因為他要dagger生成,用private或者protected表示無法生成。
package com.haichenyi.myproject.model.http;
import com.haichenyi.myproject.model.http.api.HttpApi;
import io.reactivex.Flowable;
/**
* Author: 海晨憶
* Date: 2018/2/23
* Desc: 網(wǎng)絡接口Retrofit實現(xiàn)
*/
public class RetrofitHelper implements HttpHelper{
private HttpApi httpApi;
@Inject
RetrofitHelper(HttpApi httpApi) {
this.httpApi = httpApi;
}
@Override
public Flowable<HttpNoResult> loginCode(String phone) {
return httpApi.loginCode(phone);
}
}
??RetrofitHelper類作為HttpHelper接口的實現(xiàn)類,他是具體功能的實現(xiàn)類,為什么說他是具體功能的實現(xiàn)類呢?因為,他是調用HttpApi接口的方法。HttpApi接口是干什么用的呢?
package com.haichenyi.myproject.model.http.api;
import com.haichenyi.myproject.model.http.HttpNoResult;
import com.haichenyi.myproject.model.http.ProtocolHttp;
import io.reactivex.Flowable;
import retrofit2.http.Field;
import retrofit2.http.FormUrlEncoded;
import retrofit2.http.POST;
/**
* Author: 海晨憶
* Date: 2018/2/23
* Desc:網(wǎng)絡請求接口api
*/
public interface HttpApi {
/**
* 登錄時獲取驗證碼.
*
* @param phone 手機號
* @return {"code":0}
*/
@FormUrlEncoded
@POST(ProtocolHttp.METHOD_LOGIN_CODE)
Flowable<HttpNoResult> loginCode(@Field("phone") String phone);
}
這個就是Retrofit的網(wǎng)絡請求的方式,看不懂?這個就是Retrofit的東西了
方法注解,包含@GET、@POST、@PUT、@DELETE、@PATH、@HEAD、@OPTIONS、@HTTP。
標記注解,包含@FormUrlEncoded、@Multipart、@Streaming。
參數(shù)注解,包含@Query、@QueryMap、@Body、@Field,@FieldMap、@Part,@PartMap。
其他注解,包含@Path、@Header、@Headers、@Url。
這里我們還差一個接口
package com.haichenyi.myproject.model.http;
/**
* Author: 海晨憶
* Date: 2018/2/23
* Desc:
*/
public interface ProtocolHttp {
String HTTP_HOST = "http://xxx.xx.xxx.xxx:8080/app/con/";
String HTTP_COMMON = "common/";
String METHOD_LOGIN_CODE = HTTP_COMMON + "code";//登錄發(fā)送驗證碼
}
??如上,這里需要注意的是不能以""結尾,然后就是,跟你們后臺商量,格式不要錯了,盡量就只有接口名字不同,接口名字前面部分都是一樣的。
??到此,這里基本上就說完了,那么有同鞋就會問了,接口定義方法的時候,我們知道該如何寫返回數(shù)據(jù)類型呢?這個我就不知道了,你得問你們后臺,根據(jù)后臺返回的數(shù)據(jù)類型去寫對應的bean類。推薦一個功能PostMan。
??到目前為止,我們都還沒有初始化網(wǎng)絡請求的參數(shù),這些網(wǎng)絡請求的參數(shù)在哪里初始化呢?這些參數(shù),我們就只用初始化一次,我們就想到了dagger的全局單例模式,沒錯,就是這個,我們上一篇寫了很多沒有用的東西,里面有一個HttpModule
package com.haichenyi.myproject.di.module;
import com.haichenyi.myproject.di.qualifier.ApiUrl;
import com.haichenyi.myproject.model.http.ProtocolHttp;
import com.haichenyi.myproject.model.http.api.HttpApi;
import java.util.concurrent.TimeUnit;
import javax.inject.Singleton;
import dagger.Module;
import dagger.Provides;
import okhttp3.OkHttpClient;
import retrofit2.Retrofit;
import retrofit2.adapter.rxjava2.RxJava2CallAdapterFactory;
import retrofit2.converter.gson.GsonConverterFactory;
/**
* Author: 海晨憶
* Date: 2018/2/23
* Desc:網(wǎng)絡請求的參數(shù)初始化
*/
@Module
public class HttpModule {
@Provides
@Singleton
OkHttpClient.Builder providesOkHttpHelper() {
//請求讀寫超時時間
return new OkHttpClient.Builder()
.connectTimeout(10, TimeUnit.SECONDS)
.readTimeout(10, TimeUnit.SECONDS)
.writeTimeout(10, TimeUnit.SECONDS);
}
@Provides
@Singleton
OkHttpClient provideClient(OkHttpClient.Builder builder) {
return builder
// .addInterceptor(new MyHttpInterceptor())
.build();
}
@Provides
@Singleton
Retrofit.Builder providesRetrofitBuilder() {
return new Retrofit.Builder();
}
@Provides
@Singleton
HttpApi provideApi(@ApiUrl Retrofit retrofit) {
return retrofit.create(HttpApi.class);
}
@Provides
@Singleton
@ApiUrl
Retrofit providesApiRetrofit(Retrofit.Builder builder, OkHttpClient client) {
return createRetrofit(builder, client, ProtocolHttp.HTTP_HOST);//這里就是你的網(wǎng)絡請求的url
}
private Retrofit createRetrofit(Retrofit.Builder builder, OkHttpClient client, String host) {
return builder.client(client)
.baseUrl(host)
.addConverterFactory(GsonConverterFactory.create())//添加gson自動解析,我們不用關
.addCallAdapterFactory(RxJava2CallAdapterFactory.create())
.build();
}
}
如上代碼,注釋寫的都有,考過去用就行了
在AppModule里面添加如下代碼
package com.haichenyi.myproject.di.module;
import com.haichenyi.myproject.base.MyApplication;
import com.haichenyi.myproject.model.DataHelper;
import com.haichenyi.myproject.model.http.HttpHelper;
import com.haichenyi.myproject.model.http.RetrofitHelper;
import javax.inject.Singleton;
import dagger.Module;
import dagger.Provides;
/**
* Author: 海晨憶
* Date: 2018/2/23
* Desc:
*/
@Module
public class AppModule {
private MyApplication application;
public AppModule(MyApplication application) {
this.application = application;
}
@Provides
@Singleton
DataHelper provideDataHelper(HttpHelper httpHelper) {
return new DataHelper(httpHelper);
}
@Provides
@Singleton
HttpHelper provideHttpHelper(RetrofitHelper retrofitHelper) {
return retrofitHelper;
}
}
這里都是dagger了生成全局單例對象需要的東西
在AppComponent里面添加如下代碼
package com.haichenyi.myproject.di.component;
import com.haichenyi.myproject.di.module.AppModule;
import com.haichenyi.myproject.di.module.HttpModule;
import com.haichenyi.myproject.model.DataHelper;
import javax.inject.Singleton;
import dagger.Component;
/**
* Author: 海晨憶
* Date: 2018/2/23
* Desc:
*/
@Singleton
@Component(modules = {AppModule.class, HttpModule.class})
public interface AppComponent {
DataHelper getDataHelper();
}
在BaseMvpPresenter里面添加如下代碼
package com.haichenyi.myproject.base;
import io.reactivex.disposables.CompositeDisposable;
import io.reactivex.disposables.Disposable;
/**
* Author: 海晨憶
* Date: 2018/2/23
* Desc:
*/
public class BaseMvpPresenter<T extends BaseView> implements BasePresenter<T> {
protected T baseView;
private CompositeDisposable disposables;
@Override
public void attachView(T baseView) {
this.baseView = baseView;
}
protected void addSubscribe(Disposable disposable) {
if (null == disposables) {
disposables = new CompositeDisposable();
}
disposables.add(disposable);
}
@Override
public void detachView() {
this.baseView = null;
unSubscribe();
}
private void unSubscribe() {
if (null != disposables) {
disposables.clear();
disposables = null;
}
}
}
至此,就全部寫完了,關于網(wǎng)絡請求的內容。調用方式如下:
package com.haichenyi.myproject.presenter;
import com.haichenyi.myproject.base.BaseMvpPresenter;
import com.haichenyi.myproject.base.MyApplication;
import com.haichenyi.myproject.contract.MainContract;
import com.haichenyi.myproject.model.DataHelper;
import javax.inject.Inject;
import io.reactivex.android.schedulers.AndroidSchedulers;
import io.reactivex.schedulers.Schedulers;
/**
* Author: 海晨憶
* Date: 2018/2/23
* Desc:
*/
public class MainPresenter extends BaseMvpPresenter<MainContract.IView>
implements MainContract.Presenter {
private DataHelper dataHelper;
@Inject
MainPresenter() {
dataHelper = MyApplication.getAppComponent().getDataHelper();
}
@Override
public void loadData() {
addSubscribe(dataHelper.loginCode("134xxxxxxxx")
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe());
// baseView.showTipMsg("加載數(shù)據(jù)");
}
}
記得在清單文件里面,加上網(wǎng)絡權限
<uses-permission android:name="android.permission.INTERNET"/>
網(wǎng)絡請求,這樣調用之后在哪處理呢?我給出我的幾個處理的工具類。首先,按如下圖設置1.8支持lambda表達式

然后添加如下幾個類
HttpCode
package com.haichenyi.myproject.model.http;
/**
* Author: 海晨憶.
* Date: 2017/12/21
* Desc: 網(wǎng)絡請求狀態(tài)碼
*/
public interface HttpCode {
/**
* 成功.
*/
int SUCCESS = 0;
/**
* 參數(shù)為空.
*/
int NO_PARAMETER = 1;
/**
* 服務器錯誤.
*/
int SERVER_ERR = 3;
}
ApiException
package com.haichenyi.myproject.model.http;
/**
* Author: 海晨憶.
* Date: 2017/12/21
* Desc: 接口異常判斷處理
*/
public class ApiException extends Exception {
private int code;
@SuppressWarnings("unused")
public ApiException(int code) {
this.code = code;
}
public ApiException(int code, String message) {
super(message);
this.code = code;
}
public int getCode() {
return code;
}
public ApiException setCode(int code) {
this.code = code;
return this;
}
}
MyRxUtils
package com.haichenyi.myproject.model.http;
import io.reactivex.BackpressureStrategy;
import io.reactivex.Flowable;
import io.reactivex.FlowableTransformer;
import io.reactivex.Scheduler;
import io.reactivex.android.schedulers.AndroidSchedulers;
import io.reactivex.schedulers.Schedulers;
/**
* Author: 海晨憶.
* Date: 2017/12/27
* Desc:切換線程的工具類
*/
public class MyRxUtils {
/**
* 從其他線程轉到主線程.
*
* @param scheduler Schedulers.io()等等
* @param <T> t
* @return FlowableTransformer
*/
public static <T> FlowableTransformer<T, T> toMain(Scheduler scheduler) {
return upstream -> upstream.subscribeOn(scheduler).observeOn(AndroidSchedulers.mainThread());
}
public static <T> FlowableTransformer<HttpResult<T>, T> handResult() {
return upstream -> upstream.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.flatMap(tHttpResult -> {
if (tHttpResult.getCode() == HttpCode.SUCCESS) {
return /*createData(tHttpResult.data)*/Flowable.just(tHttpResult.getData());
} else {
return Flowable.error(new ApiException(tHttpResult.getCode(), tHttpResult.getMsg()));
}
});
}
private static <T> Flowable<T> createData(final T data) {
return Flowable.create(e -> {
e.onNext(data);
e.onComplete();
}, BackpressureStrategy.ERROR);
}
}
MySubscriber
package com.haichenyi.myproject.model.http;
import com.haichenyi.myproject.base.BaseView;
import io.reactivex.subscribers.ResourceSubscriber;
/**
* Author: 海晨憶.
* Date: 2017/12/21
* Desc:
*/
public abstract class MySubscriber<T> extends ResourceSubscriber<T> {
private BaseView baseView;
private boolean showLoading;
public MySubscriber(BaseView baseView) {
this.baseView = baseView;
}
public MySubscriber(BaseView baseView, boolean showLoading) {
this.baseView = baseView;
this.showLoading = showLoading;
}
@Override
protected void onStart() {
super.onStart();
if (null != baseView && showLoading) {
baseView.showLoading();
}
}
@Override
public void onError(Throwable t) {
if (null == baseView) {
return;
}
baseView.hideLoading();
if (t instanceof ApiException) {
ApiException apiException = (ApiException) t;
switch (apiException.getCode()) {
case HttpCode.NO_PARAMETER:
baseView.showTipMsg("參數(shù)為空");
break;
case HttpCode.SERVER_ERR:
baseView.showTipMsg("服務器錯誤");
break;
default:
break;
}
}
}
@Override
public void onComplete() {
if (null != baseView) {
baseView.hideLoading();
}
}
}
這幾個類不想多做解釋,結合注釋,仔細看幾遍,就知道是干嘛用的了
加上這幾個之后調用方式就變成了以下的方式:
package com.haichenyi.myproject.presenter;
import com.haichenyi.myproject.base.BaseMvpPresenter;
import com.haichenyi.myproject.base.MyApplication;
import com.haichenyi.myproject.contract.MainContract;
import com.haichenyi.myproject.model.DataHelper;
import com.haichenyi.myproject.model.http.HttpNoResult;
import com.haichenyi.myproject.model.http.MyRxUtils;
import com.haichenyi.myproject.model.http.MySubscriber;
import javax.inject.Inject;
import io.reactivex.schedulers.Schedulers;
/**
* Author: 海晨憶
* Date: 2018/2/23
* Desc:
*/
public class MainPresenter extends BaseMvpPresenter<MainContract.IView>
implements MainContract.Presenter {
private DataHelper dataHelper;
@Inject
MainPresenter() {
dataHelper = MyApplication.getAppComponent().getDataHelper();
}
@Override
public void loadData() {
addSubscribe(dataHelper.loginCode("134xxxxxxxx")
.compose(MyRxUtils.toMain(Schedulers.io()))
.subscribeWith(new MySubscriber<HttpNoResult>(baseView, true) {
@Override
public void onNext(HttpNoResult httpNoResult) {
}
}));
// baseView.showTipMsg("加載數(shù)據(jù)");
}
}
完了,完了,終于寫完了。