继续接着上一次https://www.cnblogs.com/webor2006/p/12455238.html的框架进行进一步的完善。
网络层加入RxJava:
商用项目中对于网络层精典的搭配当属于“OkHttp+Retrofit+RxJava”了,所以这里也不例外,准备加入RxJava,先添加依赖:
然后再建一个新的包,用来存放RxJava相关的包:
然后这里基于Retrofit的框架进行改造,不过这里还是保留单纯Retrofit的使用方式,其实要变成RxJava的方式也不是很麻烦,之前https://www.cnblogs.com/webor2006/p/10545699.html对于怎么变成RxJava的风格也已经学习过了,所以一些细节就不详细来阐述了,为了能够转换成RxJava,首先得在Retrofit配置中增加一个Adapter:
然后再来定义请求接口,比如简单:
package com.android.core.net.rx; import java.util.Map; import io.reactivex.Observable; import okhttp3.MultipartBody; import okhttp3.RequestBody; import okhttp3.ResponseBody; import retrofit2.http.Body; import retrofit2.http.DELETE; import retrofit2.http.FieldMap; import retrofit2.http.FormUrlEncoded; import retrofit2.http.GET; import retrofit2.http.Multipart; import retrofit2.http.POST; import retrofit2.http.PUT; import retrofit2.http.Part; import retrofit2.http.QueryMap; import retrofit2.http.Streaming; import retrofit2.http.Url; public interface RxRestService { @GET Observable<String> get(@Url String url, @QueryMap Map<String, Object> params); @FormUrlEncoded @POST Observable<String> post(@Url String url, @FieldMap Map<String, Object> params); @FormUrlEncoded @PUT Observable<String> put(@Url String url, @FieldMap Map<String, Object> params); @DELETE Observable<String> delete(@Url String url, @QueryMap Map<String, Object> params); //下载是直接到内存,所以需要 @Streaming @Streaming @GET Observable<ResponseBody> download(@Url String url, @QueryMap Map<String, Object> params); //上传 @Multipart @POST Observable<String> upload(@Url String url, @Part MultipartBody.Part file); //原始数据 @POST Observable<String> postRaw(@Url String url, @Body RequestBody body); @PUT Observable<String> putRaw(@Url String url, @Body RequestBody body); }
这就没啥可解释的,就是将之前的Call换成了Observable了,然后再来建立相关的Client,还是利用构建者模式:
package com.android.core.net.rx; import com.android.core.net.HttpMethod; import com.android.core.net.RestCreator; import java.io.File; import java.util.HashMap; import io.reactivex.Observable; import okhttp3.MultipartBody; import okhttp3.RequestBody; import okhttp3.ResponseBody; public class RxRestClient { private final HashMap<String, Object> PARAMS; private final String URL; private final RequestBody BODY; //上传下载 private final File FILE; public RxRestClient(HashMap<String, Object> params, String url, RequestBody body, File file) { this.PARAMS = params; this.URL = url; this.BODY = body; this.FILE = file; } public static RxRestClientBuilder create() { return new RxRestClientBuilder(); } //开始实现真实的网络操作 private Observable<String> request(HttpMethod method) { final RxRestService service = RestCreator.getRxRestService(); Observable<String> observable = null; switch (method) { case GET: observable = service.get(URL, PARAMS); break; case POST: observable = service.post(URL, PARAMS); break; case PUT: observable = service.put(URL, PARAMS); break; case DELETE: observable = service.delete(URL, PARAMS); break; case UPLOAD: final RequestBody requestBody = RequestBody.create(MultipartBody.FORM, FILE); final MultipartBody.Part body = MultipartBody.Part.createFormData( "file", FILE.getName(), requestBody); observable = service.upload(URL, body); break; default: break; } return observable; } //各种请求 public final Observable<String> get() { return request(HttpMethod.GET); } public final Observable<String> post() { return request(HttpMethod.POST); } public final Observable<String> put() { return request(HttpMethod.PUT); } public final Observable<String> delete() { return request(HttpMethod.DELETE); } public final Observable<String> upload() { return request(HttpMethod.UPLOAD); } public final Observable<ResponseBody> download() { return RestCreator.getRxRestService().download(URL, PARAMS); } }
package com.android.core.net.rx; import java.io.File; import java.util.HashMap; import okhttp3.MediaType; import okhttp3.RequestBody; public class RxRestClientBuilder { private HashMap<String, Object> mParams; private String mUrl; private RequestBody mBody; //上传下载 private File mFile; RxRestClientBuilder() { } public final RxRestClientBuilder url(String url) { this.mUrl = url; return this; } public final RxRestClientBuilder params(HashMap<String, Object> params) { this.mParams = params; return this; } public final RxRestClientBuilder raw(String raw) { this.mBody = RequestBody.create( MediaType.parse("application/json;charset=UTF-8"), raw); return this; } //上传 public final RxRestClientBuilder file(File file) { this.mFile = file; return this; } public final RxRestClientBuilder file(String file) { this.mFile = new File(file); return this; } public final RxRestClient build() { return new RxRestClient(mParams, mUrl, mBody, mFile); } }
其中这块得增加一个获取RxRestService的方法:
这里就不去调用了,比较简单。
事件总线封装:
接下来做写个类似于RxBus的东东来改善MVP中的代码,哪一块代码呢?
对于MVP中的数据请求,往往是Model层进行数据的加载,然后加载完的数据通过回调接口返给P层,然后P层再来调用V进行数据展现,如下:
嗯,那有啥问题么?当然没啥问题,只是需要定义多余的回调接口嘛:
那用RxBus来解决不就行了嘛,这里自己来实现一个,比较简单,先来定义一个注解:
package com.android.core.net.rx.databus; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; @Target(ElementType.METHOD) @Retention(RetentionPolicy.RUNTIME) public @interface RegisterRxBus { String value(); }
package com.android.core.net.rx.databus; import java.util.Set; import java.util.concurrent.CopyOnWriteArraySet; public class RxBus { //订阅者集合 private Set<Object> subscribers; //volatile 自带线程安全(禁止指令重排) private static volatile RxBus instance; private RxBus() { //读写分离的集合 subscribers = new CopyOnWriteArraySet<>(); } public static synchronized RxBus getInstance() { if (instance == null) { synchronized (RxBus.class) { if (instance == null) { instance = new RxBus(); } } } return instance; } /** * 注册 */ public synchronized void register(Object subscriber) { subscribers.add(subscriber); } /** * 取消注册 */ public synchronized void unRegister(Object subscriber) { subscribers.remove(subscriber); } }
其中由于这条总线会存大多线程经常访问的情况,所以对于线程的同步比较讲究,订阅集合也采用线程安全比较高的CopyOnWriteArraySet了,接下来则需要来处理数据,最终来回调订阅集合中标有RegisterRxBus注解的方法了,这里采用RxJava的方式,具体如下:
package com.android.core.net.rx.databus; import java.lang.reflect.Method; import java.util.Set; import java.util.concurrent.CopyOnWriteArraySet; import io.reactivex.Observable; import io.reactivex.android.schedulers.AndroidSchedulers; import io.reactivex.functions.Consumer; import io.reactivex.functions.Function; import io.reactivex.schedulers.Schedulers; public class RxBus { //订阅者集合 private Set<Object> subscribers; //volatile 自带线程安全(禁止指令重排) private static volatile RxBus instance; private RxBus() { //读写分离的集合 subscribers = new CopyOnWriteArraySet<>(); } public static synchronized RxBus getInstance() { if (instance == null) { synchronized (RxBus.class) { if (instance == null) { instance = new RxBus(); } } } return instance; } /** * 注册 */ public synchronized void register(Object subscriber) { subscribers.add(subscriber); } /** * 取消注册 */ public synchronized void unRegister(Object subscriber) { subscribers.remove(subscriber); } /** * 把处理过程包装起来 * function:就是用户的操作 */ public void chainProcess(Function function) { Observable.just("") .subscribeOn(Schedulers.io()) .map(function)//在这里进行网络操作 .observeOn(AndroidSchedulers.mainThread()) .subscribe(new Consumer<Object>() { @Override public void accept(Object data) throws Exception { //上面的function的处理结果就会到data上 if (data == null) { return; } //把数据发送到表示层 send(data); } }); } public void send(Object data) { for (Object subscriber : subscribers) { //扫描注解,将数据发送到注册的对象标记的位置(一个方法) //subscriber表示层 callMethodByAnnotation(subscriber, data); } } private void callMethodByAnnotation(Object target, Object data) { //1.得到presenter中写的所有的方法 Method[] methodArray = target.getClass().getDeclaredMethods(); for (int i = 0; i < methodArray.length; i++) { try { //2.如果哪个方法上用了我们写的注解,就把数据输入 if (methodArray[i].getAnnotation(RegisterRxBus.class) != null) { Class paramType = methodArray[i].getParameterTypes()[0]; if (data.getClass().getName().equals(paramType.getName())) { //执行 methodArray[i].invoke(target, new Object[]{data}); } } } catch (Exception e) { e.printStackTrace(); } } } }
好,此时咱们就可以利用这个事件总线来优化我们的MVP的代码了,具体修改如下:
然后在MainActivity中来具体实现一下:
此时则需要在P中定义一个订阅方法:
接下来Model的接口就可以去掉回调方法了:
然后实现的Model就变为:
此时就用了RxBus改造完了。
Dagger2集成:
接下来则加入Dagger2的功能,让一些强new的对象变为注入的方式,来加大框架的灵活度,关于它的用法在之前https://www.cnblogs.com/webor2006/p/12392271.html已经详细学习过了, 那怎么将其加入到咱们目前的框架当中呢?这里简单演示一下,在Activity中我们使用了Http请求数据了,如下:
所以此时需要用我们封装的Http的框架来进行数据的请求,这里就最合适用Dagger的方式了,具体下面简单演示一下:
先引入依赖包:
先定义Module,里面提供对像的创建:
package com.android.mvparcstudy.di; import com.android.isolation_processor.httpprocessor.HttpHelper; import dagger.Module; import dagger.Provides; @Module public class HttpModule { @Provides public HttpHelper providerHttpHelper() { return HttpHelper.obtain(); } }
然后再到组件中进行Module的注册:
package com.android.mvparcstudy.di; import com.android.mvparcstudy.MainActivity; import dagger.Component; @Component(modules = {HttpModule.class}) public interface HttpComponent { void injectMainActivity(MainActivity activity); }
此时编译一下,然后在Activity中进行注入:
这里就简单用一下既可,在实际项目中如果有对像不想硬new的话,就都可以采用Dagger2框架来注入,具体根据实际需要再来用它既可。
关于MVP框架的封装就到这了,里面有很多是跟MVP木有关系的,但是是实际项目中都可能遇到的一些通用封装,适合自己的项目的框架才是最好的,这里只是做个操练,另外代码实现的细节倒不是太重要,重要的是对框架的思想的理解。