带你封装自己的MVP+Retrofit+RxJava2框架(一)

带你封装自己的MVP+Retrofit+RxJava2框架(一)Retrofit:一个 RESTful 的 HTTP网络请求框架的封装,网络请求的工作本质上是OkHttp 完成,而 Retrofit仅负责 网络请求接口的封装:掌握了它,你能优美地进行网络请求。 MVP:一种解耦模型和视图的模式,是现在很多公司的主流模式。 由此可见,在平时的…

前言

文本已经收录到我的Github个人博客,欢迎大佬们光临寒舍:我的GIthub博客

看完本篇文章的,可以看下带你封装自己的MVP+Retrofit+RxJava2框架(二),里面封装得到了改进

本篇文章需要已经具备的知识:

  • MVP的概念和基本使用
  • Retrofit框架的基本使用
  • RxJava2框架的基本使用
  • ButterKnife框架的基本使用
  • Base基类的概念

学习清单:

  • ActivityFragment基类的封装
  • MVP的封装使用

一.为什么要封装这套框架呢?

在搞清楚这个问题之前,我们回顾一下基本概念

RxJava: ReactiveXJVM上的一个实现,ReactiveX使用Observable序列组合异步和基于事件的程序;掌握了它,你可以优美地处理异步任务和事件的回调

Retrofit:一个 RESTfulHTTP网络请求框架的封装,网络请求的工作本质上是OkHttp 完成,而 Retrofit仅负责 网络请求接口的封装:掌握了它,你能优美地进行网络请求。

MVP:一种解耦模型和视图的模式,是现在很多公司的主流模式。

由此可见,在平时的开发中熟练运用这种模式,不仅可以满足生活中大部分应用程序的场景,还可以为将来的工作积攒宝贵的实战经验。

二.核心用法

本项目基于Android X 进行构建,完整代码可在我的Github上下载:带你封装自己的MVP+Retrofit+RxJava2框架

首先,看一下我们项目的基本结构,下面笔者将为大家详细介绍每个类的相关信息

项目基本结构
项目基本结构

2.1 基类Base

Base基类是封装了一些基类,方便后面新建新的Activity或者Fragment,减少耦合

2.1.1 BaseActivity

这个类是Activity的基类,注意与下面的BaseMvpActivity区分开

/** * Description : BaseActivity 基类活动 * * @author XuCanyou666 * @date 2020/2/2 */

public abstract class BaseActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(getLayoutId());
        initPresenter();
        initViews();
        ButterKnife.bind(this);
    }

    /** * 抽象方法:实例化Presenter */
    protected abstract void initPresenter();

    /** * 抽象方法:初始化控件,一般在BaseActivity中通过ButterKnife来绑定,所以该方法内部一般我们初始化界面相关的操作 * * @return 控件 */
    protected abstract void initViews();

    /** * 抽象方法:得到布局id * * @return 布局id */
    protected abstract int getLayoutId();

    /** * 启动Fragment * * @param id id * @param fragment 碎片 */
    protected void startFragment(int id, Fragment fragment) {
        FragmentTransaction fragmentTransaction = getSupportFragmentManager().beginTransaction();
        fragmentTransaction.add(id, fragment);
        fragmentTransaction.commit();
    }

}

2.1.2 BaseView

一个接口,说明了每一个View基本需要的一些操作

package com.users.xucanyou666.rxjava2_retrofit_mvp.base;

/** * created by xucanyou666 * on 2020/1/31 18:26 * email:913710642@qq.com */
public interface BaseView {

    /** * 显示进度框 */
    void showProgressDialog();

    /** * 关闭进度框 */
    void hideProgressDialog();

    /** * 出错信息的回调 * * @param result 错误信息 */
    void onError(String result);

}

2.1.3 BaseMvpActivity

  • MVP活动的基类

  • 继承自BaseActivity,它是MVP活动的基类,封装好了Presenter的相关操作

package com.users.xucanyou666.rxjava2_retrofit_mvp.base;

/** * created by xucanyou666 MVP活动的基类,封装好了presenter的相关操作 * on 2019/12/24 20:53 * email:913710642@qq.com */

public abstract class BaseMvpActivity<V extends BaseView, P extends BasePresenter> extends BaseActivity {

    private P presenter;

    /** * 初始化presenter */
    @Override
    protected void initPresenter() {
        presenter = createPresenter();
        if (presenter != null) {
            presenter.attachView((V) this);
        }
    }

    /** * 创建presenter * * @return Presenter */
    protected abstract P createPresenter();

    /** * 得到presenter * * @return presenter */
    protected P getPresenter() {
        return presenter;
    }

    /** * 销毁 */
    @Override
    protected void onDestroy() {
        super.onDestroy();
        if (presenter != null) {
            presenter.detachView();
        }
    }

}

2.1.4 BaseFragment

  • Fragment的基类

  • 需要注意的是,这里用了ButterKnife框架,对碎片进行了绑定和解绑操作

/** * Fragment的基类,封装了一些Fragment的相关操作 * created by xucanyou666 * on 2020/1/31 16:21 * email:913710642@qq.com */
public abstract class BaseFragment<T extends BasePresenter> extends Fragment implements BaseView {
    protected T mPresenter;
    protected Context mContext;
    protected Bundle mBundle;
    protected Unbinder unbinder;
    protected View view;

    /** * 恢复数据 * * @param outState bundle */
    @Override
    public void onSaveInstanceState(@NonNull Bundle outState) {
        super.onSaveInstanceState(outState);
        if (mBundle != null) {
            outState.putBundle("bundle", mBundle);
        }
    }

    /** * 绑定activity * * @param context context */
    @Override
    public void onAttach(@NonNull Context context) {
        super.onAttach(context);
        mContext = context;
    }

    /** * 运行在onAttach之后,可以接收别人传递过来的参数,实例化对象 * 可以解决返回的时候页面空白的bug * * @param savedInstanceState */
    @Override
    public void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        if (savedInstanceState != null) {
            mBundle = savedInstanceState.getBundle("bundle");
        } else {
            mBundle = getArguments() == null ? new Bundle() : getArguments();
        }
        //初始化presenter
        mPresenter = initPresenter();
    }

    protected T getPresenter() {
        return mPresenter;
    }

    /** * 运行在onCreate之后,生成View视图 * * @param inflater * @param container * @param savedInstanceState * @return */
    @Nullable
    @Override
    public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
        view = initView(inflater, container, savedInstanceState);
        unbinder = ButterKnife.bind(this, view);
        return view;
    }

    /** * 运行在onCreateView之后 * 加载数据 * * @param savedInstanceState */
    @Override
    public void onActivityCreated(@Nullable Bundle savedInstanceState) {
        super.onActivityCreated(savedInstanceState);
        mPresenter.attachView(this);

    }

    /** * 跳转Fragment * * @param toFragment 跳转去的fragment */
    public void startFragment(Fragment toFragment) {
        Log.d(TAG, "haha");
        startFragment(toFragment, null);
    }

    /** * 跳转Fragment * * @param toFragment 跳转到的fragment * @param tag fragment的标签 */
    public void startFragment(Fragment toFragment, String tag) {
        FragmentTransaction fragmentTransaction = getFragmentManager().beginTransaction();
        fragmentTransaction.hide(this).add(android.R.id.content, toFragment, tag);
        fragmentTransaction.addToBackStack(tag);
        fragmentTransaction.commitAllowingStateLoss();
    }

    @Override
    public void onDestroyView() {
        super.onDestroyView();
        unbinder.unbind();
    }

    /** * fragment进行回退 * 类似于activity的OnBackPress */
    public void onBack() {
        getFragmentManager().popBackStack();
    }

    @Override
    public void onDetach() {
        mPresenter.detachView();
        super.onDetach();
    }

    /** * 初始化Fragment应有的视图 * * @return view */
    public abstract View initView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState);

    /** * 创建presenter * * @return <T extends BasePresenter> 必须是BasePresenter的子类 */
    public abstract T initPresenter();

    /** * 得到context * * @return context */
    @Override
    public Context getContext() {
        return mContext;
    }

    /** * 得到bundle * * @return bundle */
    public Bundle getBundle() {
        return mBundle;
    }

    /** * 得到fragment * * @return fragment */
    public Fragment getFragment() {
        return this;
    }

}

2.1.5 BasePresenter


/** * created by xucanyou666 * on 2020/1/16 17:12 * email:913710642@qq.com */
public abstract class BasePresenter<V extends BaseView> {
    //将所有正在处理的Subscription都添加到CompositeSubscription中。统一退出的时候注销观察
    private CompositeDisposable mCompositeDisposable;
    private V baseView;

    /** * 和View绑定 * * @param baseView */
    public void attachView(V baseView) {
        this.baseView = baseView;
    }

    /** * 解绑View,该方法在BaseMvpActivity类中被调用 */
    public void detachView() {
        baseView = null;
        // 在界面退出等需要解绑观察者的情况下调用此方法统一解绑,防止Rx造成的内存泄漏
        if (mCompositeDisposable != null) {
            mCompositeDisposable.dispose();
        }
    }

    /** * 获取View * * @return view */
    public V getMvpView() {
        return baseView;
    }

    /** * 将Disposable添加,在每次网络访问之前初始化时进行添加操作 * * @param subscription subscription */
    public void addDisposable(Disposable subscription) {
        //csb 如果解绑了的话添加 sb 需要新的实例否则绑定时无效的
        if (mCompositeDisposable == null || mCompositeDisposable.isDisposed()) {
            mCompositeDisposable = new CompositeDisposable();
        }
        mCompositeDisposable.add(subscription);
    }

}

2.1.6 MyApplication

  • 封装了一个可以全局获取Context的方法,参考写法自:《第一行代码–第二版》
  • 注意:记得在AndroidManifest中注册Application
package com.users.xucanyou666.rxjava2_retrofit_mvp.base;

import android.app.Application;
import android.content.Context;

/** * 基类 * created by xucanyou666 * on 2019/11/2 14:46 * email:913710642@qq.com * @author xucanyou666 */
public class MyApplication extends Application {
    private static Context context;

    @Override
    public void onCreate() {
        super.onCreate();
        context = getApplicationContext();
    }

    public static Context getContext() {
        return context;
    }
}

2.2 工具类 Util

2.2.1 RetrofitManager

Retrofit单例工具类

/** * Retrofit单例工具类 * created by xucanyou666 * on 2020/1/16 16:38 * email:913710642@qq.com */
public class RetrofitManager {
    private Retrofit mRetrofit;

    //构造器私有,这个工具类只有一个实例
    private RetrofitManager() {
        OkHttpClient.Builder httpClientBuilder = new OkHttpClient.Builder();
        httpClientBuilder.connectTimeout(15, TimeUnit.SECONDS);
        mRetrofit = new Retrofit.Builder()
                .client(httpClientBuilder.build())
                .addConverterFactory(GsonConverterFactory.create())
                .addCallAdapterFactory(RxJava2CallAdapterFactory.create())
                .baseUrl(BASE_URL)
                .build();
    }

    /** * 静态内部类单例模式 * * @return */
    public static RetrofitManager getInstance() {
        return Inner.retrofitManager;
    }

    private static class Inner {
        private static final RetrofitManager retrofitManager = new RetrofitManager();
    }

    /** * 利用泛型传入接口class返回接口实例 * * @param ser 类 * @param <T> 类的类型 * @return Observable */
    public <T> T createRs(Class<T> ser) {
        return mRetrofit.create(ser);
    }
}

2.2.2 RxJavaUtil

RxJava的工具类,执行线程调度工作

/** * created by xucanyou666 * on 2019/11/17 19:20 * email:913710642@qq.com * * @author xucanyou666 */
public class RxJavaUtil {
    /** * 线程调度工作 * * @param observable 被观察者 * @param <T> 类型 */
    public static <T> Observable  toSubscribe(Observable<T> observable) {
        return observable.subscribeOn(Schedulers.io())
                .unsubscribeOn(Schedulers.io())
                .observeOn(AndroidSchedulers.mainThread());
    }

}

2.3 常量类 Contant

常量池,特别感谢api open网提供的免费API

/** * created by xucanyou666 * on 2019/11/17 19:01 * email:913710642@qq.com */
public class StaticQuality {
    public static final String BASE_URL="https://api.gushi.ci/";
}

2.4 接口管理器 Contract

这里集中了一些Model层,Presenter层,View层的与诗歌相关的接口

/** * 诗歌的接口管理器 * created by xucanyou666 * on 2020/2/2 15:33 * email:913710642@qq.com */
public interface IPoetryContract {
    interface IPoetryModel {
        /** * 得到诗歌 * * @return 诗歌 */
        Observable<PoetryEntity> getPoetry();
    }

    interface IPoetryPresenter {
        void getPoetry();
    }

    interface IPoetryView extends BaseView {
        /** * @param author 作者 */
        void searchSuccess(String author);
    }
}

2.5 实体类 Entity



/** * 诗歌的实体类 * created by xucanyou666 * on 2020/1/23 21:23 * email:913710642@qq.com * API返回示例: * { * "content": "胡瓶落膊紫薄汗,碎叶城西秋月团。", * "origin": "从军行七首", * "author": "王昌龄", * "category": "古诗文-天气-月亮" * } */
public class PoetryEntity {
    private String content; //诗歌内容
    private String origin; //来源
    private String author; //作者
    private String category; //分类

    public String getContent() {
        return content;
    }

    public void setContent(String content) {
        this.content = content;
    }

    public String getOrigin() {
        return origin;
    }

    public void setOrigin(String origin) {
        this.origin = origin;
    }

    public String getAuthor() {
        return author;
    }

    public void setAuthor(String author) {
        this.author = author;
    }

    public String getCategory() {
        return category;
    }

    public void setCategory(String category) {
        this.category = category;
    }
}

2.6 Retrofit接口 iApiService



/** * retrofit接口 * created by xucanyou666 * on 2020/1/23 21:25 * email:913710642@qq.com */
public interface GetPoetryEntity {
    /** * 获取古诗词 * * @return 古诗词 */
    @GET("all.json")
    Observable<PoetryEntity> getPoetry();
}

2.7 视图层 View

这里为了减少代码量,方便读者们掌握核心操作,故View层都是用的同一个PresenterModel,仅作学习参考

2.7.1 MainActivity

需要注意的是,这里BaseMvpActivity<activity, presenter>Activity填入的是当前的ActivityPresenter填入的是对应的Presenter


/** * Description : MainActivity * * @author XuCanyou666 * @date 2020/2/3 */

public class MainActivity extends BaseMvpActivity<MainActivity, PoetryPresenter> implements IPoetryContract.IPoetryView {

    @BindView(R.id.btn_get_poetry)
    Button btnGetPoetry;
    @BindView(R.id.tv_poetry_author)
    TextView tvPoetryAuthor;
    @BindView(R.id.btn_goto_fragment)
    Button btnGotoFragment;
    @BindView(R.id.ll)
    LinearLayout ll;

    @Override
    protected void initViews() {

    }

    @Override
    protected int getLayoutId() {
        return R.layout.activity_main;
    }

    @Override
    protected PoetryPresenter createPresenter() {
        return PoetryPresenter.getInstance();
    }

    @Override
    public void searchSuccess(String author) {
        tvPoetryAuthor.setText(author);
    }

    @Override
    public void showProgressDialog() {

    }

    @Override
    public void hideProgressDialog() {

    }

    @Override
    public void onError(String result) {
        Toast.makeText(MyApplication.getContext(), result, Toast.LENGTH_SHORT).show();
    }

    @OnClick({R.id.btn_get_poetry, R.id.btn_goto_fragment})
    public void onViewClicked(View view) {
        switch (view.getId()) {
            case R.id.btn_get_poetry:
                getPresenter().getPoetry();
                break;
            case R.id.btn_goto_fragment:
                startFragment(R.id.ll, new MainFragment());
                break;
            default:
                break;
        }
    }
}

2.7.2 MainFragment


/** * Description : MainFragment * * @author XuCanyou666 * @date 2020/2/2 */

public class MainFragment extends BaseFragment<PoetryPresenter> implements IPoetryContract.IPoetryView {

    @BindView(R.id.btn_get_poetry)
    Button btnGetPoetry;
    @BindView(R.id.tv_poetry_author)
    TextView tvPoetryAuthor;

    @Override
    public View initView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
        return inflater.inflate(R.layout.fragment_main, container, false);
    }

    @Override
    public PoetryPresenter initPresenter() {
        return PoetryPresenter.getInstance();
    }

    @Override
    public void showProgressDialog() {

    }

    @Override
    public void hideProgressDialog() {

    }

    @Override
    public void onError(String result) {
        Toast.makeText(MyApplication.getContext(), result, Toast.LENGTH_SHORT).show();

    }

    @OnClick(R.id.btn_get_poetry)
    public void onViewClicked() {
        getPresenter().getPoetry();
    }

    @Override
    public void searchSuccess(String author) {
        tvPoetryAuthor.setText(author);
    }
}

2.8 Presenter



/** * created by xucanyou666 * on 2020/1/16 17:09 * email:913710642@qq.com */
public class PoetryPresenter extends BasePresenter<IPoetryContract.IPoetryView> implements IPoetryContract.IPoetryPresenter {

    private static final String TAG = "PoetryPresenter";

    private PoetryEntity mPoetryEntity;
    private PoetryModel mPoetryModel;

    private PoetryPresenter() {
        mPoetryModel = PoetryModel.getInstance();
    }

    public static PoetryPresenter getInstance() {
        return Inner.instance;
    }

    private static class Inner {
        private static final PoetryPresenter instance = new PoetryPresenter();
    }

    /** * 得到诗歌 */
    @Override
    public void getPoetry() {
        Observable observable = mPoetryModel.getPoetry().doOnSubscribe(new Consumer<Disposable>() {
            @Override
            public void accept(Disposable disposable) throws Exception {
                addDisposable(disposable);
            }
        });
        observable = RxJavaUtil.toSubscribe(observable);
        observable.subscribe(new Observer<PoetryEntity>() {
            @Override
            public void onSubscribe(Disposable d) {
            }

            @Override
            public void onNext(PoetryEntity poetryEntity) {
                mPoetryEntity = poetryEntity;
            }

            @Override
            public void onError(Throwable e) {
                getMvpView().onError(e.getMessage());
                Log.d(TAG, "onError: " + e.getMessage());
            }

            @Override
            public void onComplete() {
                if (mPoetryEntity != null) {
                    getMvpView().searchSuccess(mPoetryEntity.getAuthor());
                }
            }
        });

    }
}

2.9 Model



/** * created by xucanyou666 * on 2020/1/16 17:06 * email:913710642@qq.com */
public class PoetryModel implements IPoetryContract.IPoetryModel {

    private PoetryModel() {

    }

    public static PoetryModel getInstance() {
        return Inner.instance;
    }

    private static class Inner {
        private static final PoetryModel instance = new PoetryModel();
    }

    /** * 获取古诗词 * * @return 古诗词 */
    @Override
    public Observable<PoetryEntity> getPoetry() {
        return RetrofitManager.getInstance().createRs(GetPoetryEntity.class).getPoetry();
    }
}

2.10 app.build.gradle

apply plugin: 'com.android.application'

android {
    compileSdkVersion 28
    compileOptions {
        sourceCompatibility JavaVersion.VERSION_1_8
        targetCompatibility JavaVersion.VERSION_1_8
    }
    defaultConfig {
        applicationId "com.users.xucanyou666.rxjava2_retrofit_mvp"
        minSdkVersion 19
        targetSdkVersion 28
        versionCode 1
        versionName "1.0"
        testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
    }
    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
        }
    }
}

dependencies {
    // RxJava
    implementation 'io.reactivex.rxjava2:rxjava:2.1.12'
    implementation 'com.squareup.retrofit2:retrofit:2.6.0'
    // Retrofit和jxjava关联
    implementation 'com.squareup.retrofit2:adapter-rxjava2:2.4.0'
    // Retrofit使用Gson转换
    implementation 'com.squareup.retrofit2:converter-gson:2.4.0'
    // RxAndroid
    implementation 'io.reactivex.rxjava2:rxandroid:2.0.2'
    //引入ButterKnife
    implementation "com.jakewharton:butterknife:10.2.0"
    implementation 'androidx.legacy:legacy-support-v4:1.0.0'
    annotationProcessor "com.jakewharton:butterknife-compiler:10.2.0"

    implementation "com.google.android.material:material:1.0.0"
    implementation fileTree(dir: 'libs', include: ['*.jar'])
    implementation 'androidx.appcompat:appcompat:1.1.0'
    implementation 'androidx.constraintlayout:constraintlayout:1.1.3'
    testImplementation 'junit:junit:4.12'
    androidTestImplementation 'androidx.test.ext:junit:1.1.1'
    androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0'
}

三.我在使用中遇到的问题

3.1 网络权限忘记授予

  • 解决措施:加上权限即可
<uses-permission android:name="android.permission.INTERNET" />

3.2 ButterKnife框架版本问题

使用ButterKnife框架的时候

当是androidX的时候,需要implementation 10.2.0版本的ButterKnife

//引入ButterKnife
    implementation "com.jakewharton:butterknife:10.2.0"
    implementation 'androidx.legacy:legacy-support-v4:1.0.0'
    annotationProcessor "com.jakewharton:butterknife-compiler:10.2.0"

当是android 28等其他版本的时候,可以导入8.4.0版本的ButterKnife(导入10.2.0版本会出错)

implementation 'com.jakewharton:butterknife:8.4.0'
annotationProcessor 'com.jakewharton:butterknife-compiler:8.4.0' 

3.3 ButterKnife需要Java 1.8以上的支持

 compileOptions {
 sourceCompatibility JavaVersion.VERSION_1_8 targetCompatibility JavaVersion.VERSION_1_8 } 

3.4 Fragment中点击事件失效的问题

  • 点击事件失效发生的场景:Fragment中初始化控件没有用ButterKnife框架

解决措施如下:

A:方法一:

  • 将控件的初始化放在onCreateView
  • 将控件的点击事件的代码放在onActivityCreated

B:方法二:

  • Fragment中使用ButterKnife框架

如果文章对您有一点帮助的话,希望您能点一下赞,您的点赞,是我前进的动力

本文参考链接:

本文使用 mdnice 排版

今天的文章带你封装自己的MVP+Retrofit+RxJava2框架(一)分享到此就结束了,感谢您的阅读。

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
如需转载请保留出处:https://bianchenghao.cn/22369.html

(0)
编程小号编程小号

相关推荐

发表回复

您的电子邮箱地址不会被公开。 必填项已用*标注