Android 从零开始搭建一个主流项目框架—RxJava2.0+Retrofit2.0+OkHttp
我这里的网络请求是用的装饰者模式去写的,什么是装饰者模式呢?在不必改变原类文件和使用继承的情况下,动态地扩展一个对象的功能。它是通过创建一个包装对象,也就是装饰来包裹真实的对象。我的理解就是一个接口,两个实现类,一个实现类负责调用接口的方法,另一个类负责功能的具体实现。本文中所提到的代码都是伪代码,最后会给出完整的,最初版本的项目框架。不包含任何业务逻辑
容我一个一个来说,首先,我们一般请求网络的时候,会有统一的返回数据格式,一个是需要判断返回code码的,就比方说登录功能,那登录成功,还是失败,我们只用判断code码即可,这种类型,我们统一是HttpNoResult。还有一个是返回数据的,就比方说查一个列表数据。这里我们统一的是HttpResult。我先给出这两个类的代码:
package com.haichenyi.myproject.model.http; /** * Author: 海晨忆 * Date: 2018/2/23 * Desc:没有解析数据的返回 */ 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:有解析数据的返回 */ 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 + '}'; } }
这里我就需要说一点,有数据返回的时候,每个数据类型都是不一样的,所以,这里我用的泛型传递,不同的数据类型,传不同的bean对象
言归正传,我们来说说网络请求的一个接口,两个实现类。
一个接口—HttpHelper
package com.haichenyi.myproject.model.http; import io.reactivex.Flowable; /** * Author: 海晨忆 * Date: 2018/2/23 * Desc:网络接口,接口参数Token统一处理,方法中不传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,泛型就是我们需要解析的数据
-
loginCode方法是说返回数据,我们只用判断是否是成功还是失败,
-
login方法是说返回数据是一个Login对象,至于对象是什么内容,那就是和你们后台确认了
-
diyKeys方法就是说,返回数据是一个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:网络请求的实现类 */ 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的实现类,他的唯一作用就是调用接口的方法即可,具体的功能实现是后面一个类,这里需要说明的是这个类的构造方法要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: 网络接口Retrofit实现 */ 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接口的实现类,他是具体功能的实现类,为什么说他是具体功能的实现类呢?因为,他是调用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:网络请求接口api */ public interface HttpApi { /** * 登录时获取验证码. * * @param phone 手机号 * @return {"code":0} */ @FormUrlEncoded @POST(ProtocolHttp.METHOD_LOGIN_CODE) Flowable<HttpNoResult> loginCode(@Field("phone") String phone); }
这个就是Retrofit的网络请求的方式,看不懂?这个就是Retrofit的东西了
方法注解,包含@GET、@POST、@PUT、@DELETE、@PATH、@HEAD、@OPTIONS、@HTTP。
标记注解,包含@FormUrlEncoded、@Multipart、@Streaming。
参数注解,包含@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";//登录发送验证码 }
如上,这里需要注意的是不能以""结尾,然后就是,跟你们后台商量,格式不要错了,尽量就只有接口名字不同,接口名字前面部分都是一样的。
到此,这里基本上就说完了,那么有同鞋就会问了,接口定义方法的时候,我们知道该如何写返回数据类型呢?这个我就不知道了,你得问你们后台,根据后台返回的数据类型去写对应的bean类。推荐一个功能PostMan。
到目前为止,我们都还没有初始化网络请求的参数,这些网络请求的参数在哪里初始化呢?这些参数,我们就只用初始化一次,我们就想到了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:网络请求的参数初始化 */ @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);//这里就是你的网络请求的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; } } }
至此,就全部写完了,关于网络请求的内容。调用方式如下:
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("加载数据"); } }
记得在清单文件里面,加上网络权限
<uses-permission android:name="android.permission.INTERNET"/>
网络请求,这样调用之后在哪处理呢?我给出我的几个处理的工具类。首先,按如下图设置1.8支持lambda表达式
然后添加如下几个类
HttpCode
package com.haichenyi.myproject.model.http; /** * Author: 海晨忆. * Date: 2017/12/21 * Desc: 网络请求状态码 */ public interface HttpCode { /** * 成功. */ int SUCCESS = 0; /** * 参数为空. */ 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("参数为空"); 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("加载数据"); } }