简介
工作期间有空我就学习rxjava2的使用,现在结合mvp的架构给大家封装出一个开发框架,考虑到代码的重用性,数据接口的加密解密,我这里做出了可以商用的,不单是学习。
框架代码下载
本框架所用到包
compile 'com.android.support:design:25.3.1'
compile 'com.android.support:cardview-v7:25.0.1'
compile 'com.github.bumptech.glide:glide:3.7.0'
//http
compile 'io.reactivex.rxjava2:rxjava:2.1.1'
compile 'io.reactivex.rxjava2:rxandroid:2.0.1'
compile 'com.squareup.retrofit2:retrofit:2.3.0'
compile 'com.squareup.retrofit2:converter-gson:2.3.0'
compile 'com.squareup.retrofit2:converter-scalars:2.3.0'
compile 'com.squareup.retrofit2:adapter-rxjava2:2.3.0'
compile 'com.squareup.okhttp3:logging-interceptor:3.8.1'
compile 'com.tbruyelle.rxpermissions2:rxpermissions:0.9.5@aar'
//rxbind
compile 'com.jakewharton.rxbinding2:rxbinding:2.0.0'
compile 'com.jakewharton.rxbinding2:rxbinding-support-v4:2.0.0'
compile 'com.jakewharton.rxbinding2:rxbinding-appcompat-v7:2.0.0'
//上拉下拉控件
compile 'com.jcodecraeer:xrecyclerview:1.5.9'
//图片压缩库
compile 'id.zelory:compressor:2.1.0'
//动画
compile 'com.nineoldandroids:library:2.4.0'
//轮播图-bug,点击回调不能再内部类中,更新图片list要是新的对象
compile 'com.youth.banner:banner:1.4.10'
都是常用的,结合了网上很多大牛的想法封装出来,由于工作时间忙,一直没有系统的整理处理,可能还有不足请见谅,现在给大家瞅瞅…
先看使用方法,不然怎么知道好不好
里面源码文件demo文件夹放的都是例子,LoginPresenter2,Recycle2Presenter都是封装后使用,LoginPresenter,RecyclePresenter是未封装,
1.来一个登录的界面
由于是mvp架构,我们创建LoginPresenter2,里面是处理view层传来的数据交给model层,这里考虑到每个业务模块一般都有网络访问,那么每次都写这样的接口很烦啊,所以做了封装,创建了基类,直接继承使用就可以。
public class LoginPresenter2 extends HttpPresenter<LoginContract.ILoginView> {
public LoginPresenter2(LoginContract.ILoginView view) {
super(view);
}
@Override //第一种传参方式 map
public void doHttpRequest(Map<String, String> map) {
mModel.httpMap("http://120.24.44.102/fuc/api/web/userapi/login.html", GlobalCode.encryArgs(map))
.subscribe(new Consumer<ResponseBody>() {
@Override
public void accept(@NonNull ResponseBody responseBody) throws Exception {
String result = new String(responseBody.string());
//显示网络返回的数据
mView.showHttpResponse(result);
}
}, new Consumer<Throwable>() {
@Override
public void accept(@NonNull Throwable throwable) throws Exception {
GlobalCode.printLog(Log.getStackTraceString(throwable));
}
});
}
@Override //第二种传参方式 对象
protected void doHttpRequest2(Object object) {
final LoginRequest loginRequest = (LoginRequest) object;
mModel.httpMap("http://120.24.44.102/fuc/api/web/userapi/login.html", setArgField(loginRequest))
.subscribe(new Consumer<ResponseBody>() {
@Override
public void accept(@NonNull ResponseBody responseBody) throws Exception {
String result = new String(responseBody.string());
//显示网络返回的数据
mView.showHttpResponse(result);
//本地测试 数据对象转化
LoginResponse loginResponse = GlobalCode.getHttpResponse(result, LoginResponse.class);
GlobalCode.printLog(loginResponse.getType_id() + "");
}
}, new Consumer<Throwable>() {
@Override
public void accept(@NonNull Throwable throwable) throws Exception {
GlobalCode.printLog(throwable);
}
});
}}
//契约类
public class LoginContract {
interface ILoginView extends BaseView {
void showHttpResponse(String str);
ImageView showImgBg();
}
// interface ILoginPresenter extends BasePresenter {
// void doHttpRequest(Map<String, String> map);
// }
// interface IModel extends BaseModel {
// Observable<ResponseBody> httpMap(Map<String, String> map);
// }
}
看到请求简单吗,用map携带请求参数,这里动态传入URL,不用每次都去写一个observable,注意到就是HttpPresenter类,就是我封装后的,
public class HttpPresenter<T extends BaseView> extends BasePresenterImpl<T> implements GlobalPresenter {
protected GlobalModel mModel;
public HttpPresenter(T view) {
super(view);
this.mModel = new GlobalModel() {
@Override
public Observable<ResponseBody> httpMap(String url, Map<String, String> map) {
return ApiEngine.getInstance().getApiService().doRequestUrl(url, map)
.compose(RxSchedulers.<ResponseBody>io2main())
.doOnSubscribe(new Consumer<Disposable>() { //在subscribe()前调用
@Override
public void accept(@NonNull Disposable disposable) throws Exception {
addDispoable(disposable);
LoadingDialog.showprogress(mView.getCurContext(), "正在加载..");
}
})
.doOnTerminate(new Action() { //action没有返回值,function有返回值
@Override
public void run() throws Exception {
LoadingDialog.dismissprogress();
System.out.println("onTerminate>>>>>");
}
});
}
};
}
@Override
public void doHttpRequest(Map<String, String> map) {
}
protected void doHttpRequest2(Object obj) {
}
}
//类中的BaseView,BasePresenterImpl,GlobalPresenter,都是一次写好的基类,调用者继承使用就好,这里重载HttpPresenter(T view)看到动态创建observable,带等待框,可控制订阅disposable(防止activity退出网络仍然请求,持有View会内存泄漏)
@FormUrlEncoded
@POST()
Observable<ResponseBody> doRequestUrl(@Url String url, @FieldMap Map<String, String> map); //动态创建observable,共用post请求
看到我们返回的网络对象会是ResponeBody,由于商业软件都是带加密的,返回一般就是key="?",data="?",我这里的使用方法是用//本地测试 数据对象转化 LoginResponse loginResponse = GlobalCode.getHttpResponse(result, LoginResponse.class); GlobalCode.printLog(loginResponse.getType_id() + "");
大家一般用的都是对象或者map保存我们从网络接收到json字段,所有要写用Gson解析的成
//解析一个对象 //存在错误收集 httpjson
public static <T> T getHttpResponse(String result, Class<T> cls) {
String httpResponse = result;
JSONObject jsonObject = null;
//返回的数据{code:0,message:0k,data:{}}
//我们要判断返回码200(200成功返回JsonObject对象),其他码要弹出错误提示(return null)
jsonObject = httpJson(httpResponse);
if (null == jsonObject) return null;
String jsonstring = String.valueOf(jsonObject);
T t = null;
try {
JSONObject jsonObject1 = new JSONObject(jsonstring);
JSONObject jsondata = jsonObject1.getJSONObject("data");
// printLog(String.valueOf(jsondata));
Gson gson = new Gson();
t = gson.fromJson(String.valueOf(jsondata), cls);
} catch (JSONException e) {
e.printStackTrace();
}
return t;
}
咋LoginAty类支持toolbar,mvp解耦
public class LoginAty extends BaseActivity<LoginPresenter2> implements LoginContract.ILoginView
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_login_aty);
}
@Override
protected LoginPresenter2 onCreatePresenter() {
return new LoginPresenter2(this);
}
@Override
public void showHttpResponse(String str) {
tv_txt.setText(str);
GlobalCode.printLog("get_log_text" + str);
}
封装以后大大减少开发代码,很多重用的逻辑都在基类中写好。
2.我们再看列表界面的请求有多快,
public class Recycle2presenter extends ReHttpListPresenter<ReContract.IRecycleView, IdCell> {
private ReAdapter mAdapter;
public Recycle2presenter(ReContract.IRecycleView view, String url) {
super(view, url);
}
@Override
protected Map<String, String> setArgsMap() {
mArgsMap.put("page", mCurrentPage + "");
mArgsMap.put("pagesize", 10 + "");
mArgsMap.put("phone", "13232508893");
mArgsMap.put("password", "123456");
mArgsMap.put("type", "PASSWORD");
return super.setArgsMap();
}
@Override
protected void setReAdapter() {
super.setReAdapter();
mAdapter = new ReAdapter(mView.getCurContext(), mDataList, R.layout.list_item, new GlobalReHolder.onItemGlobalClickListener() {
@Override
public void onItemClickListener(int position) {
Toast.makeText(mView.getCurContext(), "pp-" + position, Toast.LENGTH_SHORT).show();
}
});
mView.getReView().setAdapter(mAdapter);
}
@Override
protected void handlerData(String result, boolean ismore) {
HttpListResponse<IdCell> httpListResponse = GlobalCode.getHttpResponseList(str_json2, IdCell.class);
GlobalCode.printLog(httpListResponse+"");
List<IdCell> list = httpListResponse.getSelect();
mCurrentPage++;
mPageCount = httpListResponse.getPagination().getPageCount();
if (ismore) {
mView.getReView().loadMoreComplete();
} else {
mView.getReView().refreshComplete();
}
mDataList.addAll(list); //list=null -->bug?
mDataList.addAll(list); //list=null -->bug?
mDataList.addAll(list); //list=null -->bug?
mDataList.addAll(list); //list=null -->bug?
mAdapter.notifyDataSetChanged();
}
String str_json2 = "{\"code\":\"0\",\"message\":\"获取成功\"," +
"\"data\":{\"select\":[{\"id\":4,\"user_id\":14,\"name\":\"罗泉清\",\"phone\":\"00989778278\",\"gender\":1,\"car_plate\":\"粤C2N111\",\"id_card\":\"441481199010121111\",\"car_type\":\"大货车\"},{\"id\":6,\"user_id\":14,\"name\":\"罗泉清\",\"phone\":\"00989778005\",\"gender\":1,\"car_plate\":\"粤C2N111\",\"id_card\":\"441481199010124009\",\"car_type\":\"大货车\"}]," +
"\"pagination\":{\"totalCount\":2,\"pageCount\":2}}}";}
如果你要创建一个列表,你只要创建行数据bean-Idcell,设配器ReAdapter,网络请求传参在setArgsMap(),适配器创建在setReAdapter(),获取到上拉下拉分页数据在handlerData()处理。
在ListAty我们解耦view层的显示里面只有View的初始化,其他业务逻辑都交给Recycle2Presenter实现。
public class ListAty extends BaseActivity<GlobalPresenter> implements ReContract.IRecycleView
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.list_aty);
this.setBarTitle("通用类集合窗口");
}
//只要重写该方法就可以创建新的listaty.
@Override
protected GlobalPresenter onCreatePresenter() {
return new Recycle2presenter(this,"http://120.24.44.102/fuc/api/web/userapi/login.html");
}
看到列表Activity,只写了这么少代码,是由于继承封装网络请求的RehttpListpresenter类,该类是专门用于Recycleview分页上拉下拉的网络请求,给你看下里面构造方法实现网络请求的代码,依然是动态url
//必须继承
public ReHttpListPresenter(T view, String url) {
super(view);
this.mHttpUrl = url;
this.mModel = new GlobalModel() {
@Override
public Observable<ResponseBody> httpMap(String url, Map<String, String> map) {
return ApiEngine.getInstance().getApiService().doRequestUrl(url + "?page=" + mCurrentPage, map) //注意加密
.compose(RxSchedulers.<ResponseBody>io2main())
.doOnSubscribe(new Consumer<Disposable>() {
@Override
public void accept(@NonNull Disposable disposable) throws Exception {
addDispoable(disposable);
}
});
}
};
//这里是每次上拉下拉都会调用到数据接收方法,返回的依然是一个ResponseBody,
protected void loadData(Map<String, String> map, final boolean ismore) {
mModel.httpMap(mHttpUrl, GlobalCode.encryArgs(map)).subscribe(new Consumer<ResponseBody>() {
@Override
public void accept(@NonNull ResponseBody responseBody) throws Exception {
handlerData(responseBody.string(), ismore);
}
}, new Consumer<Throwable>() {
@Override
public void accept(@NonNull Throwable throwable) throws Exception {
GlobalCode.printLog(Log.getStackTraceString(throwable));
if (ismore) {
mView.getReView().loadMoreComplete();
} else {
mView.getReView().refreshComplete();
}
}
});
}
对于接收到网络jsonobject文本,我们用
//返回一个JsonObject
//我的list数据格式可以看变量str_json2,有分页,你们可以根据自己格式修改函数方法得到//自己想要的,GlobalCode是一个创建一个工具类。
HttpListResponse<IdCell> httpListResponse = GlobalCode.getHttpResponseList(str_json2, IdCell.class);
//public static <T> HttpListResponse<T> getHttpResponseList(String result, Class<T> cls)
我们注意一下加密,post请求是将参数放到body中,加密用的rsa,aes,我们发到服务器接口的加密数据返回格式会是{code:0,message:"",key:"?",data:"?"},我的请求接口很多带共用的参数,如token,每次请求接口都写这些参数也是浪费,在post请求中用GlobalCode.encryArgs()处理公共参数,已经考虑到加密和不加密,你不要管太多,只传参数就可以,当然传对象也可以我做了封装。。。
对于rxjava使用
大学那会就看rxjava1.0,觉得看不懂,很难用,不想去学,后来在工作中很多参考的项目都用了rxjava2,我就认真学习了,发现你明白以后,码代码是真的快,不用管handler,不用去多次判断某些逻辑(rxbinding),复杂的业务就也好用,例如多层网络嵌套,联合判断,多个网络请求后不同数据更新UI。
它自带的线程调度实在太厉害,精髓是subscribeOn()指定未指定线程的observable(被观察者),且第一次有效,observeOn()每次有效指定线程切换。我开始看他的线程调度方法位置也懵逼的,我靠什么意思,后来看了很多文章感悟出来,有些总结话你每看一次都会有新的感悟,_。如果你要复杂的使用flatmap,scan,filter,map这些操作符,一定要把线程调度理解好,单纯使用没什么切换的就写个ObservableTransformer.
RxTextUtil里面有rxjava2的学习代码。Rxbinding,RxPermission,这些使用难度都不大。
框架代码