前言:之前就学习了相关的知识,一直未结合使用,现在打算结合起来使用,这个例子就是包含mvp、rxjava以及retrofit2的基本用法。下一篇文章会基于这篇文章的基础介绍dagger2的用法。
首先,在gralde文件中引入后续要用到的库:
//rxjava 以及rxandroid
compile 'io.reactivex:rxjava:1.0.14'
compile 'io.reactivex:rxandroid:1.0.1'
//retrofit
compile 'com.squareup.retrofit2:retrofit:2.0.2'
compile 'com.squareup.retrofit2:converter-gson:2.0.2'
//okhttp 以及在retrofit中使用rxjava要导入的库
compile 'com.squareup.okhttp3:okhttp:3.1.2'
compile 'com.squareup.retrofit2:adapter-rxjava:2.0.2'
//图片加载经典库universalimageloader
compile 'com.nostra13.universalimageloader:universal-image-loader:1.9.5'
MVP这里就简单介绍下:
View :负责绘制UI元素、与用户进行交互(在Android中体现为Activity);
View interface :需要View实现的接口,View通过View interface与 Presenter进行交互,降低耦合,方便进行单元测试;
Model :负责存储、检索、操纵数据(有时也实现一个Model interface用来降低耦合)
Presenter :作为View与Model交互的中间纽带,处理与用户交互的负责逻辑。
注意 presenter和view的交互一般是通过接口来实现的。view和model是不能直接交互的,以上设计主要是为了降低代码间的耦合。
我们这里的例子是使用上面的这些框架来实现listview中数据的填充,数据来源于网络。使用retrofit 实现数据的下载和解析,rxjava来保持代码简洁以及线程间的快速切换。
项目的结构如下图:
MyServiceBase 代码如下:
public class MyServiceBase {
public static IMyService getService(boolean isGetImg, int readTimeOut, int connectTimeOut){
OkHttpClient client = new OkHttpClient.Builder()
.readTimeout(readTimeOut <= 0 ? 30 : readTimeOut, TimeUnit.SECONDS)
.connectTimeout(connectTimeOut <= 0 ? 30 : connectTimeOut, TimeUnit.SECONDS)
.build();
Retrofit retrofit = new Retrofit.Builder()
.baseUrl(isGetImg ? "http://img.mukewang.com/":"http://www.imooc.com/")
.addConverterFactory(GsonConverterFactory.create())//gson 解析
.addCallAdapterFactory(RxJavaCallAdapterFactory.create())// rxjava 方法
.client(client)
.build();
IMyService myService = retrofit.create(IMyService.class);
return myService;
}
}
该类主要就是初始化retrofit,以及通过参数isGetImg来选择是加载图片还是加载数据,很简单,都有注释。
IMyService代码如下:
public interface IMyService {
@GET("api/teacher")
Observable<DataBean> getData(@Query("type") int type, @Query("num") int num);
@GET
Observable<ResponseBody> getBitmap(@Url String url);
}
getData 是获取数据,getBitmap是用于listview每个item获取图片。
@GET(“api/teacher”) 以及@Query(“type”)都是retrofit的基本用法,用来填充获取数据的URL。
MyImageLoaderUtils 是用来获取图片的,其中增加了用LruCache做图片缓存,代码如下:
public class MyImageLoaderUtils {
private LruCache<String , Bitmap> bitmapLruCache ;
private ImageView imageView;
private String mImgUrl;
public MyImageLoaderUtils(){
int maxMemorySize = (int) Runtime.getRuntime().maxMemory();
int cacheSize = maxMemorySize/4;
bitmapLruCache = new LruCache<String , Bitmap>(cacheSize){
@Override
protected int sizeOf(String key, Bitmap value) {
return value.getByteCount();
}
};
}
private void setBitmapToCache(String url, Bitmap bm){
if(getBitmapFromCache(url) == null){
bitmapLruCache.put(url, bm);
}
}
private Bitmap getBitmapFromCache(String url){
return bitmapLruCache.get(url);
}
public void setImgByUrl(ImageView imgView, String url) {
mImgUrl = url;
Bitmap bm = getBitmapFromCache(url);
if(bm != null){
imgView.setImageBitmap(bm);
}else{
getBitmap(imgView, url);
}
}
public interface ImgService {
@GET
Observable<ResponseBody> getImgBody(@Url String url);
}
private void getBitmap(final ImageView imgView, final String url) {
IMyService imgService = MyServiceBase.getService(true, 0, 0);
imgService.getBitmap(url).subscribeOn(Schedulers.newThread())
.observeOn(Schedulers.io())//io线程获取图片
.map(new Func1<ResponseBody, Bitmap>() {//map方法是用来将ResponseBody转换成Bitmap输出
@Override
public Bitmap call(ResponseBody responseBody) {
InputStream is = null;
try {
is = responseBody.byteStream();
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
if (is != null) {
is.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
if (is != null) {
return BitmapFactory.decodeStream(is);
}
return null;
}
}).observeOn(AndroidSchedulers.mainThread())//跳转到主线程
.subscribe(new Action1<Bitmap>() {
@Override
public void call(Bitmap bitmap) {
Log.e(TestListViewActivity.TAG, "bitmap = " + bitmap);
bitmapLruCache.put(url, bitmap);
String imgUrl = (String) imgView.getTag();
if(imgUrl.equals(url)){//防止滑动listview的时候图片加载错乱
imgView.setImageBitmap(bitmap);
}
}
});
}
}
上面用到了rxjava的主要方法都有注释。
下面主要是mvp的内容了,我们的界面很简单,就是ListView和progressbar,当加载数据的时候显示progressbar,加载数据完成后 隐藏progressbar。
所以我们先定义一个IBaseView接口,Presenter就是通过该接口和View交互。
我们这个接口需要哪几个方法呢?
1. 请求数据是要显示 progressbar
2. 请求数据完成是要隐藏 progressbar
3. 请求数据成功后将数据set到View中。
4. 请求数据失败要toast “获取数据失败”
public interface IBaseView {
void showProgressView();
void hideProgressView();
void setData(List<DataBean.ItemData> datas);
void getDataFailed();
}
在MainActivity里面实现IBaseView 接口:
public class MainActivity extends Activity implements IBaseView {
private ProgressBar mProgressBar;
private ListView mListView;
private List<DataBean.ItemData> mdDatas;
private LvAdapter mLvAdapter;
private MainPresenter mMainPresenter;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.mvp_rxjava_dagger2_retrofit2_main_layout);
initView();
mMainPresenter.getLessonData();
}
private void initView() {
mProgressBar = (ProgressBar) findViewById(R.id.mvp_rxjava_progressbar);
mListView = (ListView) findViewById(R.id.mvp_rxjava_listview);
mdDatas = new ArrayList<>();
mLvAdapter = new LvAdapter(this);
mMainPresenter = new MainPresenter(this);
}
@Override
public void showProgressView() {
mProgressBar.setVisibility(View.VISIBLE);
}
@Override
public void hideProgressView() {
mProgressBar.setVisibility(View.INVISIBLE);
}
@Override
public void setData(List<DataBean.ItemData> datas) {
mLvAdapter.setItemDataList(datas);
mListView.setAdapter(mLvAdapter);
}
@Override
public void getDataFailed() {
Toast.makeText(this, "获取数据失败", Toast.LENGTH_SHORT).show();
}
}
MainActivity 持有Presenter,通过Presenter来请求数据,MainPresenter如下:
public class MainPresenter {
private final static String TAG = "MainPresenter";
private MainModel mainModel;
private IBaseView baseView;
public MainPresenter(IBaseView baseView){
this.baseView = baseView;
mainModel = new MainModel();
}
public void getLessonData(){
baseView.showProgressView();
mainModel.getData(new onDataListener() {
@Override
public void onSuccess(List<DataBean.ItemData> datas) {
Log.e(TAG, "onSuccess thread name = "+Thread.currentThread().getName());
baseView.setData(datas);
baseView.hideProgressView();
}
@Override
public void onFailed() {
Log.e(TAG, "onFailed thread name = "+Thread.currentThread().getName());
baseView.getDataFailed();
baseView.hideProgressView();
}
});
}
public interface onDataListener {
void onSuccess(List<DataBean.ItemData> datas);
void onFailed();
}
MainPresenter 中持有 IBaseView,以及请求数据操作的Model。由于rxjava在请求数据完成后将现场转到主线程,所以我们可以在回调onSuccess以及onFailed 中操作UI。
MainModel 如下:
public class MainModel {
private static final String TAG = "MainModel";
public void getData(final MainPresenter.onDataListener listener) {
IMyService myService = MyServiceBase.getService(false, 0, 0);
myService.getData(4, 30).subscribeOn(Schedulers.newThread())//在子线程中下载
.observeOn(AndroidSchedulers.mainThread())//数据下载好后转到main线程
.subscribe(new Subscriber<DataBean>() {
@Override
public void onCompleted() {
}
@Override
public void onError(Throwable e) {
Log.e(TAG, e.getMessage());
if (listener != null) {
listener.onFailed();
}
}
@Override
public void onNext(DataBean dataBean) {
Log.e(TAG, "SIZE = " + dataBean.getDatas().size());
List<DataBean.ItemData> datas = dataBean.getDatas();
if (listener != null) {
listener.onSuccess(datas);
}
}
});
}
下一篇在本文的基础上讲 dagger2的应用。
学习rxjava
可以访问:https://gank.io/post/560e15be2dca930e00da1083