美文网首页框架【库】android开发Android开发
Android中mvp+retrofit2+rxjava2处理m

Android中mvp+retrofit2+rxjava2处理m

作者: 你好好吖 | 来源:发表于2018-11-15 14:26 被阅读182次

    java版: https://github.com/wj576038874/mvp-rxjava-retrofit-okhttp
    kotlin版: https://github.com/wj576038874/MvpKotlin

    项目有所更新,以下代码可能有所变化,最新代码以上面Github为准


    项目结构:

    image.png

    mvp的原理,v层抽象出接口,供P层调用,M层进行数据处理,抽象出接口,供P调用,P层中可拿到M和V 的接口引用,进行方法调用等逻辑处理,再利用接口回调的方式将解析好的数据返回给V层,这样就打到M层不直接和V层打交道,实现解耦和的效果

    mvp模式会存在一个内存泄漏的隐患,如何解决,我们在p层写一个解绑和绑定的方法,最后在Activity中创建Presenter时进行绑定,在onDestroy中进行解绑,这样我们就解决了内存泄露的问题,我们可以抽取出一个基类的Presenter和一个基类的Activity来做这个事情,让子类不用在写这些重复的代码。但是问题又来了,既然是基类,肯定不止有一个子类来继承基类,那么也就是说子类当中定义的View接口和需要创建的Presenter都不相同,我们肯定在基类当中不能写死吧,那就使用泛型来设计。

    1.创建一个基类View,让所有View接口都必须实现,这个View可以什么都不做只是用来约束类型的

    2.创建一个基类的Presenter,在类上规定View泛型,然后定义绑定和解绑的抽象方法,让子类去实现

    3.创建一个基类的Activity,声明一个创建Presenter的抽象方法,因为要帮子类去绑定和解绑那么就需要拿到子类的Presenter才行,但是又不能随便一个类都能绑定的,因为只有基类的Presenter中才定义了绑定和解绑的方法,所以同样的在类上可以声明泛型在,方法上使用泛型来达到目的。

    4.修改Presenter和Activity中的代码,各自继承自己的基类并去除重复代码

    实现步骤:

    1.创建一个基类View,让所有View接口都必须实现

    package com.winfo.wenjie.mvp.base;
     
    /**
     * ProjectName: MvpRxjavaRetrofitDemo
     * PackageName com.winfo.wenjie.mvp.base
     * FileName: com.winfo.wenjie.mvp.base.IBaseMvpView.java
     * Author: wenjie
     * Date: 2016-12-12 14:47
     * Description: IBaseMvpView
     */
    public interface IBaseMvpView {
     
    }
    

    **2.创建一个基类的Presenter,在类上规定View泛型,然后定义绑定和解绑的方法,对外在提供一个获取View的方法,让子类直接通过方法来获取View使用即可 **

    package com.winfo.wenjie.mvp.base;
     
    import java.lang.ref.WeakReference;
     
    /**
     * ProjectName: MvpRxjavaRetrofitDemo
     * PackageName com.winfo.wenjie.mvp.base
     * FileName: com.winfo.wenjie.mvp.base.BaseMvpPresenter.java
     * Author: wenjie
     * Date: 2016-12-12 14:47
     * Description: BaseMvpPresenter
     */
    public class BaseMvpPresenter<V extends IBaseMvpView> {
     
        /**
         * v层泛型引用
         */
        protected V mView;
     
        private WeakReference<V> weakReferenceView;
     
        public void attachMvpView(V view) {
            weakReferenceView = new WeakReference<>(view);
            this.mView = weakReferenceView.get();
        }
     
     
        public void detachMvpView() {
            weakReferenceView.clear();
            weakReferenceView = null;
            mView = null;
        }
    }
    

    3.创建一个基类的Activity,声明一个创建Presenter的抽象方法,因为要帮子类去绑定和解绑那么就需拿到子类的Presenter才行,但是又不能随便一个类都能绑定的,因为只有基类的Presenter中才定义了绑定解绑的方法,所以同样的在类上可以声明泛型在方法上使用泛型来达到目的

    package com.winfo.wenjie.mvp.base;
     
    import android.os.Bundle;
    import android.support.annotation.Nullable;
    import android.support.v7.app.AppCompatActivity;
     
    /**
     * ProjectName: MvpRxjavaRetrofitDemo
     * PackageName com.winfo.wenjie.mvp.base
     * FileName: com.winfo.wenjie.mvp.base.BaseMvpActivity.java
     * Author: wenjie
     * Date: 2016-12-12 14:47
     * Description: BaseMvpActivity
     */
    public abstract class BaseMvpActivity<V extends IBaseMvpView, P extends BaseMvpPresenter<V>> extends AppCompatActivity implements IBaseMvpView {
     
        protected P mPresenter;
     
        @SuppressWarnings("unchecked")
        @Override
        protected void onCreate(@Nullable Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            if (mPresenter == null) {
                mPresenter = createPresenter();
            }
            mPresenter.attachMvpView((V) this);
        }
     
        protected abstract P createPresenter();
     
        @Override
        protected void onDestroy() {
            super.onDestroy();
            if (mPresenter != null) {
                mPresenter.detachMvpView();
            }
        }
    }
    

    4、新建自己的prsenter继承presenter基类

    package com.winfo.wenjie.mvp.presenter;
     
    import android.text.TextUtils;
     
    import com.winfo.wenjie.domain.Token;
    import com.winfo.wenjie.mvp.base.BaseMvpPresenter;
    import com.winfo.wenjie.mvp.model.OnLoadDatasListener;
    import com.winfo.wenjie.mvp.model.impl.LoginModel;
    import com.winfo.wenjie.mvp.view.ILoginView;
     
    /**
     * ProjectName: MvpRxjavaRetrofitDemo
     * PackageName com.winfo.wenjie.mvp.presenter
     * FileName: com.winfo.wenjie.mvp.presenter.LoginPresenter.java
     * Author: wenjie
     * Date: 2016-12-12 14:12
     * Description: p层
     */
    public class LoginPresenter extends BaseMvpPresenter<ILoginView> {
     
        /**
         * m层
         */
        private LoginModel loginModel;
     
        /**
         * mvp模式  p层持有  v 和m 的接口引用 来进行数据的传递  起一个中间层的作用
         */
        public LoginPresenter() {
            /*
             *示例化loginmodel对象  固定写法  Retrofit.create(Class);
             */
            this.loginModel = new LoginModel();
        }
        /**
         * 登陆
         */
        public void login() {
            if (mView == null) return;
            if (TextUtils.isEmpty(mView.getUserName()) || TextUtils.isEmpty(mView.getPassword())) {
                mView.showMsg("用户名或密码不能为空");
                return;
            }
            loginModel.login(mView.getDialog(), "", "", "password", mView.getUserName(), mView.getPassword(), new OnLoadDatasListener<Token>() {
                @Override
                public void onSuccess(Token token) {
                    //请求成功服务器返回的数据s
                    mView.setText(token.getAccess_token());
                }
     
                @Override
                public void onFailure(String eroor) {
                    //请求成功服务器返回的错误信息
                    mView.showMsg(eroor);
                }
            });
        }
    }
    

    5、新建activity继承activity基类

    package com.winfo.wenjie.mvp.view.impl;
     
    import android.app.Dialog;
    import android.os.Bundle;
    import android.widget.Button;
    import android.widget.EditText;
    import android.widget.TextView;
    import android.widget.Toast;
    import com.winfo.wenjie.R;
    import com.winfo.wenjie.mvp.base.BaseMvpActivity;
    import com.winfo.wenjie.mvp.presenter.LoginPresenter;
    import com.winfo.wenjie.mvp.view.ILoginView;
    import com.winfo.wenjie.utils.DialogUtils;
    import butterknife.BindView;
    import butterknife.ButterKnife;
    import butterknife.OnClick;
     
     
    /**
     * ProjectName: MvpRxjavaRetrofitDemo
     * PackageName com.winfo.wenjie.mvp.view.impl
     * FileName: com.winfo.wenjie.mvp.view.impl.MainActivity.java
     * Author: wenjie
     * Date: 2016-12-12 14:47
     * Description: v层
     */
    public class MainActivity extends BaseMvpActivity<ILoginView, LoginPresenter> implements ILoginView {
     
        @BindView(R.id.username)
        EditText etUserName;
        @BindView(R.id.password)
        EditText etPassword;
        @BindView(R.id.result)
        TextView textView;
        @BindView(R.id.login)
        Button btnLogin;
        private Dialog dialog;
     
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
            ButterKnife.bind(this);
     
            dialog = DialogUtils.createLoadingDialog(this, "登陆中...");
        }
     
        @Override
        public Dialog getDialog() {
            return dialog;
        }
     
        @Override
        public String getUserName() {
            return etUserName.getText().toString();
        }
     
        @Override
        public String getPassword() {
            return etPassword.getText().toString();
        }
     
        @Override
        public void showMsg(String msg) {
            Toast.makeText(this, msg, Toast.LENGTH_SHORT).show();
        }
     
        @Override
        public void setText(String result) {
            textView.setText("登录成功!Token:\n"+result);
        }
     
        @OnClick(R.id.login)
        public void onClick() {
            /*
             * 调用登录方法进行登陆
             */
            mPresenter.login();
        }
     
        @Override
        protected LoginPresenter createPresenter() {
            return new LoginPresenter();
        }
    }
    

    6、抽象出View的一个接口,提供M层所需要的参数数据,不直接将Activity传递到P,并继承v层基类

    package com.winfo.wenjie.mvp.view;
     
    import android.app.Dialog;
     
    import com.winfo.wenjie.mvp.base.IBaseMvpView;
     
    /**
     * ProjectName: MvpRxjavaRetrofitDemo
     * PackageName com.winfo.wenjie.mvp.view.impl
     * FileName: com.winfo.wenjie.mvp.view.impl.ILoginView.java
     * Author: wenjie
     * Date: 2016-12-12 14:11
     * Description: view层的接口 由view来实现也就是mainactivity来实现该接口
     */
    public interface ILoginView extends IBaseMvpView{
     
        /**
         * 获取view层的dialog
         * @return retuen
         */
        Dialog getDialog();
     
        /**
         * 获取用户名 参数
         * @return username
         */
        String getUserName();
     
        /**
         * 获取密码
         * @return password
         */
        String getPassword();
     
        /**
         * 弹出消息
         * @param msg msg
         */
        void showMsg(String msg);
     
        /**
         * 将数据返回给view
         * @param result resuklt
         */
        void setText(String result);
    }
    

    7、新建m层接口和m层实现类实现解耦

    package com.winfo.wenjie.mvp.model;
     
    import android.app.Dialog;
     
    import com.winfo.wenjie.domain.Token;
    import com.winfo.wenjie.mvp.model.impl.LoginModel;
     
    /**
     * ProjectName: MvpRxjavaRetrofitDemo
     * PackageName: com.winfo.wenjie.mvp.model
     * FileName: com.winfo.wenjie.mvp.model.ILoginModel.java
     * Author: wenjie
     * Date: 2017-01-03 13:54
     * Description: m层接口
     */
    public interface ILoginModel {
     
        /**
         * 登陆方法
         * @param dialog 对话框这里传递到model不是很好,但是也没办法,因为要做一个对话框消失,同时取消请求的操作
         * @param client_id client_id
         * @param client_secret client_secret
         * @param grant_type grant_type
         * @param username 用户名
         * @param password  用户密码
         * @param onLoadDatasListener 监听函数
         */
        void login(Dialog dialog,String client_id, String client_secret, String grant_type, String username, String password, OnLoadDatasListener<Token> onLoadDatasListener);
     
    }
    
    package com.winfo.wenjie.mvp.model;
     
    /**
     * ProjectName: DiycodeApp
     * PackageName: com.wenjie.diycode.mvp.model
     * FileName: com.wenjie.diycode.mvp.model.OnLoadDatasListener.java
     * Author: wenjie
     * Date: 2017-08-29 11:20
     * Description:
     */
    public interface OnLoadDatasListener<T> {
     
        /**
         * 成功
         * @param t 数据
         */
        void onSuccess(T t);
     
     
        /**
         * 失败
         * @param eroor 错误信息
         */
        void onFailure(String eroor);
     
    }
    

    M层实现类

    package com.winfo.wenjie.mvp.model.impl;
     
    import android.app.Dialog;
     
    import com.winfo.wenjie.domain.Token;
    import com.winfo.wenjie.mvp.model.ILoginModel;
    import com.winfo.wenjie.mvp.model.OnLoadDatasListener;
    import com.winfo.wenjie.request.ApiService;
    import com.winfo.wenjie.request.DialogSubscriber;
    import com.winfo.wenjie.request.OkHttpUtils;
    import com.winfo.wenjie.request.ResponseResult;
    import rx.Observable;
    import rx.Subscriber;
    import rx.android.schedulers.AndroidSchedulers;
    import rx.schedulers.Schedulers;
     
    /**
     * ProjectName: MvpRxjavaRetrofitDemo
     * PackageName com.winfo.wenjie.mvp.model
     * FileName: com.winfo.wenjie.mvp.model.impl.LoginModel.java
     * Author: wenjie
     * Date: 2016-12-12 14:47
     * Description: m层实现类
     */
    public class LoginModel implements ILoginModel {
     
        @Override
        public void login(Dialog dialog, String client_id, String client_secret, String grant_type, String username, String password, final OnLoadDatasListener<Token> onLoadDatasListener) {
             /*
             * 被订阅者
             */
            Observable<Token> observable = OkHttpUtils.getRetrofit().create(ApiService.class).getToken(client_id, client_secret, grant_type, username, password);
            /*
             * 订阅者
             */
            Subscriber<Token> subscriber = new DialogSubscriber<Token>(dialog , true) {
                @Override
                protected void onSuccess(Token token) {
                    onLoadDatasListener.onSuccess(token);
                }
     
                @Override
                protected void onFailure(String msg) {
                    onLoadDatasListener.onFailure(msg);
                }
            };
            observable.subscribeOn(Schedulers.io())
                    .unsubscribeOn(Schedulers.io())
                    .observeOn(AndroidSchedulers.mainThread())
                    .subscribe(subscriber);
        }
    }
    

    rxjava和retrofit+okhttp具体应用在M层中,通过rxjava的异步,以及okhttp网络请求结合获取数据请求接口
    下面封装了一个网络请求

    ApiService主要是项目中所有需要调用的的接口,具体为什么这么写,不做多介绍了

    package com.winfo.wenjie.request;
     
    import com.winfo.wenjie.domain.Token;
    import com.winfo.wenjie.utils.Constant;
     
    import retrofit2.http.Field;
    import retrofit2.http.FormUrlEncoded;
    import retrofit2.http.POST;
    import rx.Observable;
     
    /**
     * ProjectName: MvpRxjavaRetrofitDemo
     * PackageName: com.winfo.wenjie.request
     * FileName: com.winfo.wenjie.request.ApiService.java
     * Author: wenjie
     * Date: 2017-01-17 16:54
     * Description:
     */
    public interface ApiService {
        /**
         * 获取 Token (一般在登录时调用)
         *
         * @param client_id     客户端 id
         * @param client_secret 客户端私钥
         * @param grant_type    授权方式 - 密码
         * @param username      用户名
         * @param password      密码
         * @return Token 实体类
         */
        @POST("oauth/token")
        @FormUrlEncoded
        Observable<Token> getToken(
                @Field("client_id") String client_id, @Field("client_secret") String client_secret,
                @Field("grant_type") String grant_type, @Field("username") String username,
                @Field("password") String password);
    }
    
    package com.winfo.wenjie.request;
     
    /**
     * ProjectName: MvpRxjavaRetrofitDemo
     * PackageName com.winfo.wenjie.request
     * FileName: com.winfo.wenjie.request.DialogCancelListener.java
     * Author: wenjie
     * Date: 2016-12-12 14:32
     * Description: 对话框隐藏或者消失之后取消请求
     */
    public interface DialogCancelListener {
        /**
         * 取消网络请求
         */
        void onCancel();
    }
    

    创建一个Handler操作对话框,可以取消请求

    package com.winfo.wenjie.request;
     
    import android.app.Dialog;
    import android.content.DialogInterface;
    import android.os.Handler;
    import android.os.Message;
     
    /**
     * ProjectName: MvpRxjavaRetrofitDemo
     * PackageName com.winfo.wenjie.request
     * FileName: com.winfo.wenjie.request.DialogHandler.java
     * Author: wenjie
     * Date: 2016-12-12 14:28
     * Description: 创建一个dialoghandler类来操作dialog加载进度框  以便于 请求取消的处理
     */
    public class DialogHandler extends Handler {
     
        /**
         * 显示加载框
         */
        static final int SHOW_PROGRESS_DIALOG = 1;
     
        /**
         * 隐藏加载框
         */
        static final int DISMISS_PROGRESS_DIALOG = 2;
     
        private Dialog loadingDialog;
     
        private DialogCancelListener dialogCancelListener;
     
        /**
         * 构造方法接收一个加载框的对象  由各个view层创建之后传进来  因为每个对话框所提示的内容有所不同
         * @param dialog dialog
         */
        DialogHandler(Dialog dialog, DialogCancelListener dialogCancelListener) {
            this.loadingDialog = dialog;
            this.dialogCancelListener = dialogCancelListener;
            initDialogDismissListenner();
        }
     
        private void initDialogDismissListenner() {
            loadingDialog.setOnDismissListener(new DialogInterface.OnDismissListener() {
                @Override
                public void onDismiss(DialogInterface dialog) {
                    dialogCancelListener.onCancel();
                }
            });
        }
     
        /**
         * 显示加载框
         */
        private void showLodingDialog() {
            loadingDialog.show();
        }
     
        /**
         * 隐藏加载框
         */
        private void dismissLodingDialog() {
            loadingDialog.dismiss();
        }
     
        @Override
        public void handleMessage(Message msg) {
            switch (msg.what) {
                case SHOW_PROGRESS_DIALOG:
                    showLodingDialog();
                    break;
                case DISMISS_PROGRESS_DIALOG:
                    dismissLodingDialog();
                    break;
            }
        }
    }
    

    封装订阅者

    package com.winfo.wenjie.request;
     
    import android.app.Dialog;
    import android.text.TextUtils;
    import java.net.ConnectException;
    import java.net.SocketTimeoutException;
    import rx.Subscriber;
     
    /**
     * ProjectName: MvpRxjavaRetrofitDemo
     * PackageName com.winfo.wenjie.request
     * FileName: com.winfo.wenjie.request.DialogSubscriber.java
     * Author: wenjie
     * Date: 2016-12-12 14:23
     * Description: 订阅者
     */
    public abstract class DialogSubscriber<T> extends Subscriber<T> implements DialogCancelListener {
     
        private boolean isShowDialog;
        /**
         * 定义一个请求成功的抽象方法 子类必须实现并在实现中进行处理服务器返回的数据
         *
         * @param t 服务器返回的数据
         */
        protected abstract void onSuccess(T t);
     
        /**
         * 定义一个请求失败的抽象方法 子类必须实现并在实现中进行服务器返回数据的处理
         *
         * @param msg 服务器返回的错误信息
         */
        protected abstract void onFailure(String msg);
     
        private DialogHandler dialogHandler;
     
        /**
         *
         * @param dialog 对话框
         * @param isShowDialog 是否显示加载的对话框
         */
        protected DialogSubscriber(Dialog dialog, boolean isShowDialog) {
            this.isShowDialog = isShowDialog;
            dialogHandler = new DialogHandler(dialog , this);
        }
     
        /**
         * 显示对话框 发送一个显示对话框的消息给dialoghandler  由他自己处理(也就是dialog中hanldermesage处理该消息)
         */
        private void showProgressDialog() {
            if (dialogHandler != null) {
                dialogHandler.obtainMessage(DialogHandler.SHOW_PROGRESS_DIALOG).sendToTarget();
            }
        }
     
        /**
         * 隐藏对话框 ....
         */
        private void dismissProgressDialog() {
            if (dialogHandler != null) {
                dialogHandler.obtainMessage(DialogHandler.DISMISS_PROGRESS_DIALOG).sendToTarget();
                dialogHandler = null;
            }
        }
     
        /**
         * 请求开始
         * 先判断isShowDialog的值,如果为false就不显示对话框,为true才显示
         */
        @Override
        public void onStart() {
            if(isShowDialog){
                showProgressDialog();
            }
        }
     
        /**
         * 请求完成,隐藏对话框
         */
        @Override
        public void onCompleted() {
            dismissProgressDialog();
        }
     
     
        /**
         * 请求出错
         * 这里异常处理的不是很完善,你们自己多写一些请求可能出现的异常
         * 进行捕获,这样可以直接将异常信息返回到view层可见页面,开发时一眼也可以看出具体的问题
         * @param e e
         */
        @Override
        public void onError(Throwable e) {
            dismissProgressDialog();
            String msg;
            if (e instanceof SocketTimeoutException) {
                msg = "请求超时。请稍后重试!";
            } else if (e instanceof ConnectException) {
                msg = "请求超时。请稍后重试!";
            } else {
                msg = "请求未能成功,请稍后重试!";
            }
            if (!TextUtils.isEmpty(msg)) {
                onFailure(msg);
            }
        }
     
     
        /**
         * 请求成功
         *
         * @param t t
         */
        @Override
        public void onNext(T t) {
            /*
             * 请求成功将数据发出去
             */
            onSuccess(t);
        }
     
     
        /**
         * 请求被取消
         */
        @Override
        public void onCancel() {
    

    OkHttpUtils

    package com.winfo.wenjie.request;
     
    import java.util.concurrent.TimeUnit;
    import okhttp3.OkHttpClient;
    import retrofit2.Retrofit;
    import retrofit2.adapter.rxjava.RxJavaCallAdapterFactory;
    import retrofit2.converter.gson.GsonConverterFactory;
     
    /**
     * ProjectName: MvpRxjavaRetrofitDemo
     * PackageName: com.winfo.wenjie.request
     * FileName: com.winfo.wenjie.request.OkHttpUtils.java
     * Author: wenjie
     * Date: 2016-12-12 14:17
     * Description: 网络请求的工具类
     */
    public class OkHttpUtils {
        /**
         * okhttp
         */
        private static OkHttpClient okHttpClient;
     
     
        /**
         * Retrofit
         */
        private static Retrofit retrofit;
     
        /**
         * 获取Retrofit的实例
         *
         * @return retrofit
         */
        public static Retrofit getRetrofit() {
            if (retrofit == null) {
                retrofit = new Retrofit.Builder()
                        .baseUrl("https://www.diycode.cc/")
                        .addConverterFactory(GsonConverterFactory.create())
                        .client(getOkHttpClient())
                        .addCallAdapterFactory(RxJavaCallAdapterFactory.create())
                        .build();
            }
            return retrofit;
        }
     
     
        private static OkHttpClient getOkHttpClient() {
            if (okHttpClient == null) {
                OkHttpClient.Builder builder = new OkHttpClient.Builder();
                builder.connectTimeout(15, TimeUnit.SECONDS);
                okHttpClient = builder.build();
            }
            return okHttpClient;
        }
    }
    

    相关文章

      网友评论

        本文标题:Android中mvp+retrofit2+rxjava2处理m

        本文链接:https://www.haomeiwen.com/subject/rgryfqtx.html