RxJava+Retrofit+MVP的使用已经推出一段时间了,也一直想找个时间学习一下并且应用到接下来的项目中。趁放假这段时间仔细研究了一下,确实相比于其他框架的学习成本要高一些,不过功能实现之后的代码明显层次分明,整洁度较之前分明不少。
本文仅适合入门RxJava+Retrofit的同学用来学习参考,可能对于已经熟练掌握的读者参考价值不大,特此声明。
本章节记录的为RxJava,Retrofit2,MVP设计模式的一些入门级资料,这些资料为笔者近段时间上网搜罗而来。
对于已经提前学习这些或者已经有所掌握的读者,您可以有选择性的查阅您感兴趣的资料查漏补缺,如果您认为已经有一定能力,可以选择跳过本节;
对于想要学习这些,却又不知何处下手的读者,建议您花一点时间学习这些资料;幸运的我们可以通过站在这些先行者前辈的肩膀上,系统进行学习,最高效率入门这门技术。
<1>深入浅出RxJava(一:基础篇) @hi大头鬼hi
全系列共分4章,非常详细介绍了RxJava的使用,虽然是译文,但也可以看出作者十分用心,好评!
唯一一点就是原文作者加入了Lambda表达式,增加了一定的学习成本,但是私以为这样跟能让自己去自己通过思考敲出demo中的代码,反而增深了对rx的理解。
<2> 给 Android 开发者的 RxJava 详解 @扔物线
看完1中的4篇,再看本文就轻松许多,相对于上篇系列中的使用讲解,本文更倾向于阐述“为什么要用RxJava”的思想,获益良多。
<1> 全新的网络加载框架Retrofit2,上位的小三 @戴定康
非常基础实用的Retrofit使用教程
<2>Retrofit2 完全解析 探索与okhttp之间的关系 @ 鸿洋_
鸿洋大神的精品之作,文章从基础的使用引申,通过源码向我们阐述了Retrofit2的实现原理,膜拜。
<1>Android MVP+Retrofit+RxJava实践小结 @吴小龙同學
<2>Android中RxJava+Retrofit2.0+MVP模式的整合 @请叫我小东子
两篇相当精品的入门整合教程,对笔者的学习起着先导性的作用,本文也是整合了文中涉及知识点和笔者在实践中遇到坑及解决方案。
分包:
bean: 主要存放bean的实体类,供Gson解析为bean对象。
model:MVP中的M层,通过presenter初始化并调用处理数据。
presenter:MVP中的P层,像管家一样负责model和view的调用。
view:MVP中的V层,负责界面Ui的搭建。
retrofit:存放的是retrofit的接口以及对应的管理类。
dependencies:
compile ‘io.reactivex:rxjava:1.1.0’
compile ‘io.reactivex:rxandroid:1.1.0’
compile ‘com.squareup.retrofit2:retrofit:2.1.0’
compile ‘com.squareup.retrofit2:adapter-rxjava:2.1.0’//CallAdapterFactory的Rx依赖包
compile ‘com.squareup.retrofit2:converter-gson:2.1.0’
compile ‘com.squareup.retrofit2:converter-scalars:2.1.0’//ConverterFactory的String依赖包
建议放入个人本地的wiki中备忘,以便以后使用。
ps:文中采用的数据接口来源于笔者公司的实际项目,故不在文中暴露接口及bean的实体类代码,有兴趣的可以下载代码或者参照代码使用自己的接口。
service接口
public interface MainService {
/**
* 获取video数据 包括标题title,视频网络地址url等等
* @param type 类型 3
* @param showNum 要展示的数据个数 10
* @return
*/
@POST("getMediate/getMediate")
@FormUrlEncoded
Observable getVideoBean(@Field("type") String type, @Field("showNum") String showNum);
}
管理类,单例模式,通过getVideoBean()方法获得Observable对象在model中调用
public class MainServiceManager {
private static MainServiceManager manager;
private MainService service;
private MainServiceManager() {
init();
}
public synchronized static MainServiceManager getInstance(){
if(manager==null){
manager=new MainServiceManager();
}
return manager;
}
/**
* 初始化资源
*/
private void init() {
Retrofit retrofit=new Retrofit.Builder()
.baseUrl(MainApi.BaseUrlVideo)
.addConverterFactory(GsonConverterFactory.create())
.addCallAdapterFactory(RxJavaCallAdapterFactory.create())
.build();
service = retrofit.create(MainService.class);
}
/**
* 获取video数据列表
* @return
*/
public Observable getVideoBean(String type,String showNum) {
return service.getVideoBean(type,showNum);
}
}
MVP中V的实现类,负责处理的是界面的搭建,获得网络数据等逻辑交给presenter,让其调用model请求数据。
public class MainActivity extends AppCompatActivity implements View.OnClickListener,MainActView {
private Button mBtData;
private ListView mListview;
private MainPresenterImpl presenter;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
//xml文件中仅仅有一个button和一个listview
setContentView(R.layout.activity_main);
initView();
}
private void initView() {
presenter = new MainPresenterImpl(this);
presenter.attachView(this);
mBtData = (Button) findViewById(R.id.bt_data); //button
mListview = (ListView) findViewById(R.id.listview);//listview
mBtData.setOnClickListener(this);
}
@Override
public void onClick(View v) {
switch (v.getId()) {
case R.id.bt_data:
RequestUi();
break;
}
}
private void RequestUi() {
presenter.requestData(String.valueOf(3),String.valueOf(12));
}
private ArrayList datas=new ArrayList<>();
//下面这个方法在model获得网络数据后由presenter调用
@Override
public void loadDatas(VideoBean videoBean) {
if(videoBean==null){
Toast.makeText(this,"加载数据失败,请检查网络!",Toast.LENGTH_SHORT).show();
return;
}
ArrayList list = (ArrayList) videoBean.list;
if(list.size()==0){
Toast.makeText(this,"数据源没有数据!",Toast.LENGTH_SHORT).show();
return;
}
Log.i("test","加载数据成功,已将数据传回mainActivity!!!======"+list.size());
Log.i("test",list.toString());
//下列代码为遍历数据源将,所有视频数据的标题作为item展示在listview上面
for (int i=0;i"test","datas["+i+"]====="+datas.size());
}
ArrayAdapter adapter = new ArrayAdapter(this,android.R.layout.simple_list_item_1,android.R.id.text1,
datas);
mListview.setAdapter(adapter);
//解除注册
presenter.detachView();
}
@Override
protected void onDestroy() {
super.onDestroy();
presenter.detachView(); //解除注册,防止内存泄漏,详见BasePresenter
}
}
通过使presenter继承BasePresenter,当presenter创建时在View层调用attachView方法绑定注册,当View层获得数据后或者销毁时调用detachView解除注册,防止内存泄漏。
public abstract class BasePresenter {
public V mvpView;
private CompositeSubscription mCompositeSubscription;
public void attachView(V mvpView) {
this.mvpView = mvpView;
}
public void detachView() {
this.mvpView = null;
onUnsubscribe();
}
//RXjava取消注册,以避免内存泄露
public void onUnsubscribe() {
if (mCompositeSubscription != null && mCompositeSubscription.hasSubscriptions()) {
mCompositeSubscription.unsubscribe();
Log.i("test","Rx取消注册成功!");
return;
}
Log.i("test","mCompositeSubscription判断为false");
}
public void addSubscription(Subscription subscriber) {
if (mCompositeSubscription == null) {
mCompositeSubscription = new CompositeSubscription();
}
mCompositeSubscription.add(subscriber);
}
}
Presenter接口类
public interface MainPresenter{
/**
* 请求网络数据
*/
void requestData(String type,String showNum);
/**
* 数据请求成功
* @param videoBean
*/
void onSuccess(VideoBean videoBean);
/**
* 数据请求失败
* @param throwable
*/
void onFailure(Throwable throwable);
}
PresenterImpl实现类
public class MainPresenterImpl extends BasePresenter implements MainPresenter {
private MainActView activity;
private MainModel model;
public MainPresenterImpl(MainActView activity) {
this.activity = activity;
model = new MainModelImpl(this);
}
@Override
public void requestData(String type, String showNum) {
//请求网络数据,并且将Subscription作为返回值返回
Subscription subscription = model.requestData(type, showNum);
addSubscription(subscription);//Rx注册
}
@Override //请求数据成功,调用View层的对应方法
public void onSuccess(VideoBean videoBean) {
activity.loadDatas(videoBean);
}
@Override //请求数据失败,调用View层的对应方法并打印错误信息
public void onFailure(Throwable throwable) {
Log.e("test",throwable.toString());
activity.loadDatas(null);
}
}
Model接口类
public interface MainModel {
/**
* 请求数据
* @param type
* @param showNum
*/
Subscription requestData(String type, String showNum);
}
Model实现类
public class MainModelImpl implements MainModel {
private MainPresenterImpl presenter;
private Subscription subscribe;
public MainModelImpl(MainPresenter mainPresenter) {
//获得presenter对象
this.presenter = (MainPresenterImpl) mainPresenter;
}
@Override
public Subscription requestData(String type, String showNum) {
//获得管理类对象
MainServiceManager manager=MainServiceManager.getInstance();
//RxJava代码
Observable observable = manager.getVideoBean(type, showNum);
subscribe = observable
.observeOn(AndroidSchedulers.mainThread())
.subscribeOn(Schedulers.io())
.subscribe(new Subscriber() {
@Override
public void onCompleted() {
}
@Override
public void onError(Throwable e) {
}
@Override
public void onNext(VideoBean videoBean) {
Log.i("test", "model onNext!!!===" + videoBean.list.toString());
presenter.onSuccess(videoBean);//获得数据,presenter调用onSuccess()
}
});
return subscribe;//返回给presenter
}
}
1.一定要保证导入的retrofit2包的版本必须要一致,否则就会报错。
项目完成后效果如下:
Demo源码地址
LifeManager-Android
如果您觉得这个demo对您的Android开发学习和借鉴有所参考作用,望请不吝star,多谢!
应用中采用的框架(持续更新中):
单元测试 Junit+Mockito
ORM数据库 DBFlow
依赖注入 dagger2
联网请求 Rx+Retrofit
图片加载 Glide
其他好用并收录应用的库:
compile ‘org.greenrobot:eventbus:3.0.0’ //eventBus
compile ‘com.ashokvarma.android:bottom-navigation-bar:1.2.0’ //底部导航
compile ‘com.github.GrenderG:Toasty:1.1.5’ //不错的第三方Toast
compile ‘com.liaoinstan.springview:library:1.2.6’ //下拉刷新/上拉加载控件,内含多种流行App的UI效果
compile ‘com.lzy.widget:imagepicker:0.5.4’ //好用的图片选择器,高仿微信图片选择/拍照