本章主要针对RxJava
、Retrofit
和OkHttp
结合使用,现在说明一下三者的职责所在。
OkHttp
:负责网络请求;
Retrofit
:主要负责网络请求接口的封装,结合OkHttp
可以处理网络请求的结果;
RxJava
:负责网络请求的异步;
(1)权限
网络请求别忘了添加网络权限
<uses-permission android:name="android.permission.INTERNET" />
(2)依赖
- RxJava依赖
implementation 'io.reactivex.rxjava2:rxjava:2.1.3'
implementation 'io.reactivex.rxjava2:rxandroid:2.0.1'
或者
implementation 'com.jakewharton.rxbinding3:rxbinding:3.0.0-alpha2'
如果使用rxbinding,就添加rxbinding相关依赖,rxbinding依赖本身支持rxjava和rxandroid,所以如果添加rxbinding依赖就可以去除rxjava和rxandroid依赖了。
- okhttp3依赖
implementation 'com.squareup.okhttp3:okhttp:3.12.2'
//日志拦截器依赖
implementation 'com.squareup.okhttp3:logging-interceptor:3.5.0'
- retrofit依赖
implementation 'com.squareup.retrofit2:retrofit:2.5.0'
//gson转换器依赖
implementation 'com.squareup.retrofit2:converter-gson:2.5.0'
- RxJava和Retrofit混用的适配器
implementation 'com.squareup.retrofit2:adapter-rxjava2:2.5.0'
- autodispose依赖
implementation 'com.uber.autodispose:autodispose-android-archcomponents:1.0.0-RC2'
- 其它辅助依赖
//gson依赖
implementation 'com.google.code.gson:gson:2.8.5'
(3)RxJava介绍
点击某按钮,请求网络接口,要求防抖动(防止不一小心点击两次,导致没必要的网络请求),RxJava可以实现按钮防抖动。
RxView.clicks(findViewById(R.id.bt))
.throttleFirst(2, TimeUnit.SECONDS)
//AutoDispose的关键语句
.as(AutoDispose.<Unit>autoDisposable(AndroidLifecycleScopeProvider.from(this)))
.subscribe(new Consumer<Unit>() {
@Override
public void accept(Unit unit) throws Exception {
//获取新闻
mainPresenter.getWeather();
}
});
throttleFirst
表示2秒内,只处理第一次点击的响应;
AutoDispose
是为了防止内存泄漏;
RxJava还可以和Retrofit共同完成网络请求。
/**
* 获取天气预报
*/
public void getWeather(ResultCallBack callBack){
RetrofitUtils.getInstance().getDataFromAPI(retrofitAPI.getWeather(), callBack);
}
/**
* 通用方法
* @param observable
* @param <T>
*/
public <T> void getDataFromAPI(Observable<T> observable, final ResultCallBack callBack){
//绑定Activity的生命周期
callBack.autoDispose(observable);
observable
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Observer<T>() {
@Override
public void onSubscribe(Disposable d) {
//开始请求
callBack.startRequest();
}
@Override
public void onNext(T t) {
//成功
callBack.success(t);
}
@Override
public void onError(Throwable e) {
e.printStackTrace();
//失败
Log.d("aaa", "onFailure:"+e.getCause() + "\n" + e.getMessage());
if (e instanceof SocketTimeoutException) {
callBack.failed("网络中断,请检查您的网络状态!");
} else if (e instanceof ConnectException) {
callBack.failed("网络中断,请检查您的网络状态!");
}else{
callBack.failed("未知异常!");
}
}
@Override
public void onComplete() {
//结束请求
callBack.requestComplete();
}
});
}
(4)Retrofit介绍
单独使用Retrofit,网络请求的返回值类型是Call
,与RxJava结合使用后返回值变成了observable
,这样做的好处是:
- 可以方便的控制线程
- 可以知道网络请求什么时候开始以及什么时候结束
另外,OkHttp可以设置网络超时以及其他配置。
Retrofit在之前的文章中已经介绍过了,因此这里直接跳过,等下直接贴代码。
(5)MVP介绍
MVP模式请先看一下这篇博客
这篇博客是最简单的MVP模式了,在实际项目中往往更加复杂,但是,复杂并不可怕,可怕的是你不知道怎么使用MVP。
MVP模式一般流向是:View-->Presenter-->Model,这里最好不要逆向,或者禁止逆向。
这个流向大致的意思是View直接访问Presenter,Presenter直接访问Model,那么问题来了:
- Presenter怎么访问View?
Activity(Fragment)就是MVP的View,该Activity(Fragment)会实现一个IView接口,Presenter与IView具有依赖性,Presenter通过IView可以间接执行Activity(Fragment)的IView回调。
- Model怎么访问Presenter?
通过CallBack访问,等下贴下代码就知道了。
- Model怎么访问View?
先通过CallBack访问Presenter,再让Presenter通过IView访问Activity(Fragment)。
(6)完整封装
这里直接贴代码了。
WeatherBean.java(这是新闻类的Bean,网络请求之后数据装载类)
public class WeatherBean {
/**
* status : 201
* message : APP被用户自己禁用,请在控制台解禁
*/
private int status;
private String message;
public int getStatus() {
return status;
}
public void setStatus(int status) {
this.status = status;
}
public String getMessage() {
return message;
}
public void setMessage(String message) {
this.message = message;
}
}
ResultCallBack.java(这是网络请求结果返回的callback)
public interface ResultCallBack<T> {
//开始请求
void startRequest();
//请求成功
void success(T t);
//请求失败
void failed(String error);
//请求完成
void requestComplete();
//自动绑定生命周期
void autoDispose(Observable observable);
}
RetrofitAPI.java(Retrofit接口类,这里会防止所有的网络请求接口)
public interface RetrofitAPI {
@GET("weather?location=jiaxing&output=json&ak=5slgyqGDENN7Sy7pw29IUvrZ")
Observable<WeatherBean> getWeather();
}
RetrofitUtils.java(Retrofit网络请求工具类,这是通用的)
public class RetrofitUtils {
private RetrofitAPI retrofitAPI;
private String BASE_URL = "http://api.map.baidu.com/telematics/v3/";
private RetrofitUtils(){
OkHttpClient client = new OkHttpClient.Builder()
.connectTimeout(10, TimeUnit.SECONDS)//设置连接超时时长
.readTimeout(20, TimeUnit.SECONDS)//设置读超时时长
.writeTimeout(20, TimeUnit.SECONDS)//设置写超时时长
.addInterceptor(getHttpLoggingInterceptor())//添加日志拦截器
.build();
Retrofit retrofit = new Retrofit.Builder().baseUrl(BASE_URL)
.addConverterFactory(GsonConverterFactory.create(new Gson()))
.addCallAdapterFactory(RxJava2CallAdapterFactory.create())
.client(client)
.build();
retrofitAPI = retrofit.create(RetrofitAPI.class);
}
static class SingleHolder{
public static RetrofitUtils instance = new RetrofitUtils();
}
public static RetrofitUtils getInstance(){
return RetrofitUtils.SingleHolder.instance;
}
/**
* 创建日志拦截器
* @return
*/
private HttpLoggingInterceptor getHttpLoggingInterceptor() {
HttpLoggingInterceptor loggingInterceptor = new HttpLoggingInterceptor(
new HttpLoggingInterceptor.Logger() {
@Override
public void log(String message) {
Log.e("RetrofitUtils", "log = " + message);
}
});
loggingInterceptor.setLevel(HttpLoggingInterceptor.Level.BODY);
return loggingInterceptor;
}
public RetrofitAPI getRetrofitAPI(){
return retrofitAPI;
}
/**
* 通用方法
* @param observable
* @param <T>
*/
public <T> void getDataFromAPI(Observable<T> observable, final ResultCallBack callBack){
//绑定Activity的生命周期
callBack.autoDispose(observable);
observable
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Observer<T>() {
@Override
public void onSubscribe(Disposable d) {
//开始请求
callBack.startRequest();
}
@Override
public void onNext(T t) {
//成功
callBack.success(t);
}
@Override
public void onError(Throwable e) {
e.printStackTrace();
//失败
Log.d("aaa", "onFailure:"+e.getCause() + "\n" + e.getMessage());
if (e instanceof SocketTimeoutException) {
callBack.failed("网络中断,请检查您的网络状态!");
} else if (e instanceof ConnectException) {
callBack.failed("网络中断,请检查您的网络状态!");
}else{
callBack.failed("未知异常!");
}
}
@Override
public void onComplete() {
//结束请求
callBack.requestComplete();
}
});
}
}
CarModel.java(MVP模式的Model,用于处理非UI操作,一般用于处理网络请求)
public class CarModel {
private RetrofitAPI retrofitAPI;
public CarModel(){
retrofitAPI = RetrofitUtils.getInstance().getRetrofitAPI();
}
/**
* 获取天气预报
*/
public void getWeather(ResultCallBack callBack){
RetrofitUtils.getInstance().getDataFromAPI(retrofitAPI.getWeather(), callBack);
}
}
BasePresenter.java(MVP模式的Presenter的基类,主要负责处理一些通用的逻辑)
/**
* Presenter基类
* @param <V>
*/
public class BasePresenter<V extends IBaseView> {
/**
* 绑定的view
*/
private V mvpView;
/**
* 绑定view
*/
public void attachView(V mvpView) {
this.mvpView = mvpView;
}
/**
* 销毁view
*/
public void detachView() {
this.mvpView = null;
}
/**
* 是否与View建立连接
*/
public boolean isViewAttached(){
return mvpView != null;
}
/**
* 获取连接的view
*/
public V getView(){
return mvpView;
}
}
MainPresenter.java(MVP模式的Presenter)
public class MainPresenter extends BasePresenter<IMainView> {
private CarModel carModel;
public MainPresenter(){
carModel = new CarModel();
}
/**
* 获取天气预报
* @param
*/
public void getWeather(){
carModel.getWeather(new ResultCallBack<WeatherBean>() {
@Override
public void startRequest() {
getView().loadingDialogShow("请稍等...");
}
@Override
public void success(WeatherBean weatherBean) {
if(weatherBean != null){
getView().setResult(weatherBean.getMessage());
}
}
@Override
public void failed(String error) {
getView().setResult(error);
}
@Override
public void requestComplete() {
getView().loadingDialogDismiss();
}
@Override
public void autoDispose(Observable observable) {
//AutoDispose的关键语句
getView().autoDispose(observable);
}
});
}
}
IBaseView.java(MVP模式的View的基类,可以在Activity的基类中实现)
/**
* 所有UI通用的View操作
*/
public interface IBaseView {
/**
* 显示等待对话框
*/
void loadingDialogShow(String msg);
/**
* 关闭等待对话框
*/
void loadingDialogDismiss();
/**
* 显示提示
* @param msg
*/
void showToast(String msg);
/**
* 获取上下文
* @return 上下文
*/
Context getContext();
/**
* Observable绑定生命周期
* @param observable
* @return
*/
void autoDispose(Observable observable);
}
IMainView.java(MVP模式的View)
public interface IMainView extends IBaseView{
void setResult(String result);
}
BaseActivity.java(所有Activity的基类,包含网络进度条、AutoDispose、Toast、网络请求等待对话框)
public abstract class BaseActivity extends AppCompatActivity implements IBaseView {
private AlertDialog loadingDialog;
protected Toolbar toolbar;
private ViewFlipper mContentView;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
//初始化布局
setContentView(R.layout.layout_base);
mContentView = (ViewFlipper) findViewById(R.id.layout_container);
toolbar = (Toolbar) findViewById(R.id.base_tool_bar);
LinearLayout.LayoutParams lp = new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT, 1);
mContentView.addView(getRootView(), lp);
//初始化基类数据
initBaseData();
//初始化控件
initView();
//初始化数据
initData();
//初始化监听
initListener();
}
/**初始化基类数据*/
private void initBaseData(){}{
}
/**初始化布局*/
protected abstract View getRootView();
/**初始化控件*/
protected abstract void initView();
/**初始化数据*/
protected abstract void initData();
/**初始化点击事件*/
protected abstract void initListener();
/**
* 启动Activity
* @param clazz
*/
protected void startActivity(Class<? extends Activity> clazz){
Intent in = new Intent(this,clazz);
startActivity(in);
}
@Override
public void loadingDialogShow(String msg) {
TextView tipTextView = null;
LayoutInflater inflater = LayoutInflater.from(this);
View view = inflater.inflate(R.layout.loading_dialog, null);// 得到加载view
if (loadingDialog == null) {
ImageView imageView = view.findViewById(R.id.img);
tipTextView = view.findViewById(R.id.tipTextView);// 提示文字
// 加载动画
Animation hyperspaceJumpAnimation = AnimationUtils.loadAnimation(this, R.anim.loading_animation);
// 使用ImageView显示动画
imageView.startAnimation(hyperspaceJumpAnimation);
tipTextView.setText(msg);// 设置加载信息
AlertDialog.Builder builder = new AlertDialog.Builder(this, R.style.loading_dialog);
loadingDialog =builder.create();
builder.setCancelable(false);
}
if (!loadingDialog.isShowing()) {
loadingDialog.show();
loadingDialog.getWindow().setContentView(view);
}
}
@Override
public void loadingDialogDismiss() {
if (loadingDialog != null && loadingDialog.isShowing()) {
try {
loadingDialog.dismiss();
loadingDialog = null;
} catch (Exception e) {
e.printStackTrace();
}
}
}
@Override
public void showToast(String msg) {
Toast.makeText(this, msg, Toast.LENGTH_SHORT).show();
}
@Override
public Context getContext() {
return this;
}
@Override
public void autoDispose(Observable observable) {
//AutoDispose的关键语句
observable.as(AutoDispose.autoDisposable(AndroidLifecycleScopeProvider.from(this)));
}
@Override
protected void onDestroy() {
//关闭进度条
super.onDestroy();
loadingDialogDismiss();
}
}
MainActivity.java
public class MainActivity extends BaseActivity implements IMainView {
private MainPresenter mainPresenter;
private TextView result_text;
@Override
protected View getRootView() {
return LayoutInflater.from(this).inflate(R.layout.activity_main, null);
}
@Override
protected void initView() {
result_text = findViewById(R.id.result_text);
}
@Override
protected void initData() {
mainPresenter = new MainPresenter();
mainPresenter.attachView(this);
}
@Override
protected void initListener() {
RxView.clicks(findViewById(R.id.bt))
.throttleFirst(2, TimeUnit.SECONDS)
//AutoDispose的关键语句
.as(AutoDispose.<Unit>autoDisposable(AndroidLifecycleScopeProvider.from(this)))
.subscribe(new Consumer<Unit>() {
@Override
public void accept(Unit unit) throws Exception {
//获取新闻
mainPresenter.getWeather();
}
});
}
@Override
public void setResult(String result) {
//显示结果
result_text.setText(result);
}
@Override
protected void onDestroy() {
super.onDestroy();
if(mainPresenter.isViewAttached()){
mainPresenter.detachView();
}
}
}
loading_animation.xml
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android"
android:shareInterpolator="false" >
<rotate
android:duration="900"
android:fromDegrees="0"
android:interpolator="@android:anim/linear_interpolator"
android:pivotX="50%"
android:pivotY="50%"
android:repeatCount="-1"
android:repeatMode="restart"
android:startOffset="-1"
android:toDegrees="+360" />
</set>
loading_dialog.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="@android:color/transparent"
android:gravity="center"
android:orientation="horizontal"
android:padding="10dp" >
<ImageView
android:id="@+id/img"
android:layout_width="40dp"
android:layout_height="40dp"
android:scaleType="center"
android:src="@mipmap/aliwx_img_loading" />
<TextView
android:id="@+id/tipTextView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textStyle="bold"
android:layout_marginLeft="5dp"
android:textColor="#FF34A350"
android:text="数据加载中……"
android:textSize="16sp" />
</LinearLayout>
layout_base.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<android.support.v7.widget.Toolbar
android:id="@+id/base_tool_bar"
android:layout_width="match_parent"
android:layout_height="wrap_content">
</android.support.v7.widget.Toolbar>
<ViewFlipper
android:id="@+id/layout_container"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
</LinearLayout>
activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
android:id="@+id/rootview"
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="vertical"
android:layout_centerInParent="true">
<Button
android:id="@+id/bt"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textSize="18sp"
android:text="点击开始网络请求" />
<TextView
android:id="@+id/result_text"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textSize="18sp"/>
</LinearLayout>
</RelativeLayout>
图片:
style:
<!-- 自定义loading dialog -->
<style name="loading_dialog" parent="android:style/Theme.Dialog">
<item name="android:windowFrame">@null</item>
<item name="android:windowNoTitle">true</item>
<item name="android:windowIsFloating">true</item>
<item name="android:backgroundDimEnabled">false</item>
<item name="android:windowBackground">@color/bg_dialog_white</item>
<item name="android:windowContentOverlay">@null</item>
</style>
[本章完...]