MVP + Retrofit + RxJava 搭建一个基础的Android开发框架

MVP + Retrofit + RxJava 搭建一个基础的Android开发框架

前言

  • 本项目旨在搭建一个简易的Android开发结构,避免新手开发时出现代码混乱的问题,如有不同意见欢迎评论区提出
  • 包含一个基础的框架搭建,讲解都在注释里

一、主要功能

  1. 提供基础的MVP结构,并集成相关的初始化操作
  2. 提供基础的网络管理框架,更方便的进行网络请求和管理
  3. 提供日志打印管理,可以在logcat自动打印指定级别的http日志,方便调试
  4. 提供简单的页面示例 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. 代码结构图

MVP + Retrofit + RxJava 搭建一个基础的Android开发框架

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 + Retrofit + RxJava 搭建一个基础的Android开发框架

五、运行结果示例

MVP + Retrofit + RxJava 搭建一个基础的Android开发框架

MVP + Retrofit + RxJava 搭建一个基础的Android开发框架


相关示例代码下载链接:MVP_DEMO

上一篇:主页数据加载数据有时加载不出来


下一篇:一线互联网大厂中高级Android面试真题收录,rxjava原理面试