MVP + Retrofit + RxJava 搭建一个基础的Android开发框架
前言
- 本项目旨在搭建一个简易的Android开发结构,避免新手开发时出现代码混乱的问题,如有不同意见欢迎评论区提出
- 包含一个基础的框架搭建,讲解都在注释里
一、主要功能
- 提供基础的MVP结构,并集成相关的初始化操作
- 提供基础的网络管理框架,更方便的进行网络请求和管理
- 提供日志打印管理,可以在logcat自动打印指定级别的http日志,方便调试
- 提供简单的页面示例 SampleActivity
二、部分关键代码
1. 引入相关依赖
// blankj 工具包,包含了相当多的功能,强推
implementation 'com.blankj:utilcodex:1.30.6'
// retrofit
implementation 'com.squareup.retrofit2:retrofit:2.9.0'
implementation 'com.jakewharton.retrofit:retrofit2-rxjava2-adapter:1.0.0'
implementation 'com.squareup.retrofit2:converter-gson:2.9.0'
// rx java
implementation 'io.reactivex.rxjava2:rxandroid:2.1.1'
implementation "me.jessyan:rxerrorhandler:2.1.1"
// butterKnife 控件绑定
implementation 'com.jakewharton:butterknife:10.2.3'
annotationProcessor 'com.jakewharton:butterknife-compiler:10.2.3'
2. 代码结构图
3. MVP 相关代码
- MVP 接口定义
public interface IModel {
void onDestroy();
}
public interface IView {
/**
* 显示进度条
*/
default void showLoading(){
}
/**
* 隐藏进度条
*/
default void hideLoading() {
}
/**
* 显示报错的信息,默认使用ToastUtils提示,子类可重写
* @param msg 需要显示的信息
*/
default void showMessage(String msg) {
ToastUtils.showShort(msg);
}
}
public interface IPresenter {
/**
* 向 presenter 生命周期中增加发射器
* View 和 Model 均可添加向其中添加需要注册的事件,不用担心生命周期
*/
void addDisposable(Disposable... disposables);
/**
* 首次进入页面时的网络请求
*/
default void initDataReq() {
}
void onDestroy();
}
- BaseModel 实现
import com.blankj.utilcode.util.ToastUtils;
import io.reactivex.Observable;
import io.reactivex.android.schedulers.AndroidSchedulers;
import java.lang.reflect.ParameterizedType;
import java.util.Objects;
public abstract class BaseModel<P extends IPresenter, S> implements IModel {
protected P mPresenter;
Class<S> serviceClass;
@SuppressWarnings("unchecked")
public BaseModel(P mPresenter) {
this.mPresenter = mPresenter;
try {
// 这一段代码,就是反射,通过泛型,获取指定的类
ParameterizedType type = (ParameterizedType) getClass().getGenericSuperclass();
if (type == null) {
throw new RuntimeException("运行异常,初始化失败: " + getClass().getSimpleName());
}
serviceClass = (Class<S>) type.getActualTypeArguments()[1];
} catch (Exception e) {
e.printStackTrace();
}
}
//使用Retrofit获取了指定接口的实例
//通过Service执行某一个请求时,拼接Url并执行网络访问,并将返回结果按指定格式解析并返回
protected S obtainService() {
return NetManager.getInstance().bindService(serviceClass);//谁拿到 ,谁指定Service
}
/**
* 检查 code,如果返回值中 code 不是 {@link NetCode#Success} 则通知相应错误信息
* @param bean 需要检查的code
* @return 如果成功则为 true
*/
protected boolean checkCode(CommonResBean<?> bean) {
String needDisplay = bean.checkCode();
if (needDisplay == null) {
return true;
}
// 向 presenter 中 增加通知错误信息线程
mPresenter.addDisposable(Observable.just(needDisplay)
.observeOn(AndroidSchedulers.mainThread())
.filter(Objects::nonNull)
.subscribe(ToastUtils::showShort));
return false;
}
@Override
public void onDestroy() {
mPresenter = null;
}
}
- BasePresenter 实现
import io.reactivex.disposables.CompositeDisposable;
import io.reactivex.disposables.Disposable;
import java.lang.reflect.ParameterizedType;
//BasePresenter 两个泛型,前者为视图的,后者为数据源的
public abstract class BasePresenter<V extends IView, M extends IModel> implements IPresenter {
protected V mView;//V 泛型变量
protected M mModel;//M 泛型变量
// 这是一个集合,用来控制生命周期
private CompositeDisposable compositeDisposable;
@SuppressWarnings("unchecked")
public BasePresenter(V mView) {
this.mView = mView;
// 保存了上级 View
try {
// 一个泛型Model 实例
ParameterizedType type = (ParameterizedType) getClass().getGenericSuperclass();
if (type == null) {
throw new RuntimeException("运行异常,初始化失败: " + getClass().getSimpleName());
}
Class<M> classM = (Class<M>) type.getActualTypeArguments()[1];
this.mModel = (M) classM.getConstructors()[0].newInstance(this);
} catch (Exception e) {
e.printStackTrace();
}
compositeDisposable = new CompositeDisposable();
}
//从子管理传参到这里,再for循环出来数据 给compositeDisposable
public void addDisposable(Disposable... disposables) {
if (compositeDisposable == null) {
return;
}
for (Disposable disposable : disposables) {
compositeDisposable.add(disposable);
}
}
@Override
public void onDestroy() {
mView = null;
if (mModel != null) {
mModel.onDestroy();//不等于空,就销毁
mModel = null;
}
// 在合适的时机取消未结束的网络请求
// 取消所有未完成的网络请求
// 因为视图已经销毁了,如果继续下去,会崩溃
if (compositeDisposable != null) {
compositeDisposable.dispose();
compositeDisposable = null;
}
}
}
- BaseActivity/BaseFragment 实现
import android.annotation.SuppressLint;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import androidx.annotation.CallSuper;
import androidx.appcompat.app.AppCompatActivity;
import butterknife.ButterKnife;
/**
* FileName: BaseActivity
* Founder:
* Create Date:
* Email:
* Profile: Base Activity 基类
*/
public abstract class BaseActivity<P extends IPresenter> extends AppCompatActivity implements IView, InitialPro<P> {
protected P mPresenter;
protected View mContentView;
@SuppressLint("InflateParams")
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
//Event后,在后台能接到消息; 要用Manager里面的eventMManager
mPresenter = initPresenter();//被 泛型的某一个Presenter来初始化IPresenter 在Activity中
mContentView = LayoutInflater.from(this).inflate(getContentViewId(), null, false);
setContentView(mContentView);
ButterKnife.bind(this, mContentView);
initView();
initData();
}
@CallSuper
protected void initData() {
mPresenter.initDataReq();
}
//解绑
@Override
protected void onDestroy() {
super.onDestroy();
if (mPresenter != null) {
mPresenter.onDestroy();
mPresenter = null;
}
}
}
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import androidx.annotation.CallSuper;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.fragment.app.Fragment;
import butterknife.ButterKnife;
/**
* FileName: BaseFragment
* Founder:
* Create Date:
* Email:
* Profile: Base Fragment
*
* 泛型未知,但是,主动继承IPresenter
*/
public abstract class BaseFragment<P extends IPresenter> extends Fragment implements IView, InitialPro<P> {
protected P mPresenter;
protected View mContentView;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mPresenter = initPresenter();//被泛型的某一个Presenter来初始化(引入)IPresenter 在Fragment中
}
@Nullable
@Override
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
mContentView = inflater.inflate(getContentViewId(), container, false);
// Base View 主要做的,绑定 Presenter,初始化结构,绑定ButterKnife
// ButterKnife 就是做了一个视图绑定,不用再findviewbyid
ButterKnife.bind(this, mContentView);
initView();
initData();
return mContentView;
}
@CallSuper
protected void initData() {
// 可以看到,在View 基类中,视图创建好了之后
// 会告知Presenter可以准备请求网络数据了
// 因为如果过早,视图还没创建,如果网络请求好了,那就会崩溃
// 所以这个时机让Presenter请求网络 哦。明白了。是的。。
mPresenter.initDataReq();
}
@Override
public void onDestroy() {
super.onDestroy();
// 在视图销毁时,释放资源
if (mPresenter != null) {
// 告知Presenter释放
mPresenter.onDestroy();
mPresenter = null;
}
}
}
4. Retrofit 部分关键代码
import com.blankj.utilcode.util.GsonUtils;
import com.jakewharton.retrofit2.adapter.rxjava2.RxJava2CallAdapterFactory;
import okhttp3.OkHttpClient;
import retrofit2.Retrofit;
import retrofit2.converter.gson.GsonConverterFactory;
import java.util.concurrent.TimeUnit;
public class NetManager {
private static NetManager netManager;
Retrofit retrofit;
private NetManager() {
// 我们使用的是Retrofit代管网络,这是他的初始化方法
retrofit = new Retrofit.Builder()
.client(obtainOKHttpClient())
// 这是基础url,会跟 Service 中的 拼接
.baseUrl("https://www.7timer.info")
.addConverterFactory(GsonConverterFactory.create(GsonUtils.getGson()))
.addCallAdapterFactory(RxJava2CallAdapterFactory.create())
.build();
}
private OkHttpClient obtainOKHttpClient() {
return new OkHttpClient.Builder()
.callTimeout(10, TimeUnit.SECONDS)
.connectTimeout(10, TimeUnit.SECONDS)
.readTimeout(10, TimeUnit.SECONDS)
.writeTimeout(10, TimeUnit.SECONDS)
.addInterceptor(new RequestInterceptor(RequestInterceptor.Level.ALL))
.build();
}
public static NetManager getInstance() {
if (netManager == null) {
throw new RuntimeException("你还没有初始化 NetManager");
}
return netManager;
}
// 这两个操作,保证唯一,放到Application中
// 初始化,单例模式,保证唯一
public static void init(Context context) {
netManager = new NetManager();
}
// 释放资源
public static void release() {
if (netManager != null) {
netManager.retrofit = null;
netManager = null;
}
}
public <T> T bindService(Class<T> targetInterface) {
return retrofit.create(targetInterface);
}
}
三、使用示例
1. 声明要访问的网络接口
import io.reactivex.Observable;
import retrofit2.http.GET;
import retrofit2.http.Query;
public interface SampleServices {
/**
* <p>1. 可以请求一个地址为 https://www.7timer.info/bin/api.pl?lon=113.17&lat=23.09&product=astro&output=json 的接口,获取天气</p>
* <p>2. 可以看到 四个参数刚好对应的就是链接中对应的四个参数,此外也可以传输文件,上传下载,post请求都可以极方便的实现。</p>
* <p>关于 retrofit 的更多用法可以参考:<a href="https://www.jianshu.com/p/021a2c6e128b">Retrofit + RxJava使用详解</a></p>
*/
@GET("/bin/api.pl")
Observable<SampleBeans.WeatherData> getWeather(
@Query("lon") double lon,
@Query("lat") double lat,
@Query("product") String pro,
@Query("output") String output
);
}
2. 创建SampleModel调用请求
import cn.lzd.mvp.base.struct.BaseModel;
import cn.lzd.mvp.base.struct.IPresenter;
import io.reactivex.Observable;
public class SampleModel extends BaseModel<IPresenter, SampleServices> {
public SampleModel(IPresenter mPresenter) {
super(mPresenter);
}
public Observable<SampleBeans.WeatherData> getWeather() {
return obtainService().getWeather(
113.17,
23.09,
"astro",
"json"
);
}
}
3. 创建Presenter管理请求,并分发数据
import cn.lzd.mvp.base.net.RxUtils;
import cn.lzd.mvp.base.struct.BasePresenter;
public class SamplePresenter extends BasePresenter<SampleActivity, SampleModel> {
public SamplePresenter(SampleActivity mView) {
super(mView);
}
void reqWeather() {
addDisposable(mModel.getWeather()
.compose(RxUtils.applySchedulers(mView))
.subscribe(weatherData -> mView.onReceiveWeatherData(weatherData.getDatas())));
}
}
4. 创建View显示视图
@SuppressLint("NonConstantResourceId")
public class SampleActivity extends BaseActivity<SamplePresenter> {
@BindView(R.id.reqResult)
TextView tvReqResult;
@BindView(R.id.loadingBG)
View loadingBG;
@BindView(R.id.loadingProgress)
ProgressBar loadingProgress;
@BindView(R.id.request)
View request;
@Override
public int getContentViewId() {
return R.layout.activity_sample;
}
@Override
public void initView() {
super.initView();
tvReqResult.setMovementMethod(ScrollingMovementMethod.getInstance());
}
@OnClick({R.id.request, R.id.loadingBG})
public void onClick(View view) {
// 对不同的控件做点击事件处理
switch (view.getId()) {
case R.id.request: {
tvReqResult.setText("");
view.setEnabled(false);
mPresenter.reqWeather();
break;
}
case R.id.loadingBG:{
ToastUtils.showShort("请等待……");
break;
}
}
}
void onReceiveWeatherData(List<SampleBeans.DayData> weatherData) {
StringBuilder stringBuilder = new StringBuilder();
for (SampleBeans.DayData dayData : weatherData) {
stringBuilder.append(GsonUtils.toJson(dayData)).append("\n\n");
}
tvReqResult.setText(stringBuilder);
request.setEnabled(true);
}
@Override
public void showLoading() {
loadingBG.setVisibility(View.VISIBLE);
loadingProgress.setVisibility(View.VISIBLE);
}
@Override
public void hideLoading() {
loadingBG.setVisibility(View.GONE);
loadingProgress.setVisibility(View.GONE);
}
}
四、关于Observable数据流向的一个简要图示
五、运行结果示例
相关示例代码下载链接:MVP_DEMO