本文适合于有一定的MVP,Retrofit,RxJava,Dagger2经验的开发者

一、简介

      1.MVP

          按照惯例百度百科一下MVP贴上来。

          简称:MVP 全称:Model-View-Presenter ;MVP 是从经典的模式MVC演变而来,它们的基本思想有相通的地方:Controller/Presenter负责逻辑的处理,Model提供数据,View负责显示。

          作为一种新的模式,MVP与MVC有着一个重大的区别:在MVP中View并不直接使用Model,它们之间的通信是通过Presenter (MVC中的Controller)来进行的,所有的交互都发生在Presenter内部,而在MVC中View会直接从Model中读取数据而不是通过 Controller。

          在这里贴出百度来的MVP关系图,自己体会一下。MVP这块简单就这样了,因为我们的重点不在这里。

                                  \"\"

     2.Retrofit

           网络请求框架,可以理解成是对Okhttp的一个在封装。具体了解,推荐此文:https://www.jianshu.com/p/0fda3132cf98

     3.Rxjava

           emmmm...  核心是观察者模式,通过这个模式当我们的代码逻辑很多的时候依然能够保持代码逻辑清晰有条理。这里一样推荐一篇文件,个人觉得那个例子讲的很好~~~  链接:https://www.jianshu.com/p/cd3557b1a474

     4.Dagger

           Dagger2,一个强大的Android的注解框架,目前的使用率很高。但是门槛有点高,笔者就是从入门到放弃,再从放弃到入门。说到依赖注入笔者第一个想到的是Spring的依赖注入,然后刚学的时候发现怎么同样是依赖注入怎么差这么多。Dagger2还是要多花的心思去学习的,因为很厉害。

          接下来我们就直接进入正题,开始我们的封装

二、封装MVP 

      封装MVP其实就是写一些 View, Presenter, Activity, Fragment等等的基类。MVP我用的是Google的那套用Contract管理接口。

    

      直接上代码。

    1. Presenter 

          Presenter 封装的时候会遇到一个问题,如果我们在进行网络请求的时候我们绑定的View所在的Activity被销毁了,这个时候我们网络请求完成后再去调用到我们绑定的View的方法的时候,那就直接凉凉了,所以在这里我们引入了CompositeSub ion来防止OOM。(后面涉及到网络请求才需要用到)

/**
 * <pre>
 *    author  : cassie
 *    email   : 
 *    time    : 2018/08/28.
 *    desc    : MVP中Presenter的基类
 *    version : 1.0
 * </pre>
 */

public class  Presenter<V extends  View> {

    /**
     * MVP 中Activity View类
     */
    protected V mView;
    /**
     * 在这里先忽略我这个类 后面讲到Dagger才需要用到
     * sharedPreferences类
     */
    protected SharedPreferences sharedPreferences;
    /**
     * 在这里先忽略我这个类 后面讲到Dagger才需要用到
     * 服务请求
     */
    protected ApiService apiService;

    /**
     * 使用CompositeSub ion来防止OOM
     * 因为在网络请求的时候可能activity或者fragment已经被销毁,然后网络请求还在执行
     */
    private CompositeSub ion mCompositeSub ion;


    /**
     * 绑定view
     *
     * @param mView
     */
    protected void attach(V mView) {
        if (this.mView == null) {
            this.mView = mView;
        }
    }

    /**
     * 添加绑定sub ion
     *
     * @param sub ion
     */
    protected void addSubscribe(final Sub ion sub ion) {
        new Thread(new Runnable() {
            @Override
            public void run() {
                if (mCompositeSub ion == null) {
                    mCompositeSub ion = new CompositeSub ion();
                }
                mCompositeSub ion.add(sub ion);
            }
        }).start();

    }

    /**
     * 解绑view 以及清除subscribe
     */
    protected void deatch() {
        if (mView != null) {
            mView = null;
            Logger.e(\"unSubscribe: view null\");
        }
        if (mCompositeSub ion != null && mCompositeSub ion.hasSub ions()) {
            mCompositeSub ion.clear();
            Logger.e(\"unSubscribe: mSub ion null\");
        }
    }


}

 2. View

            View 就比较简单的大概就是一些显示/隐藏加载框,显示toast。

/**
 * <pre>
 *    author  : cassie
 *    email   : 
 *    time    : 2018/08/28.
 *    desc    : MVP中View的基类
 *    version : 1.0
 * </pre>
 */
public interface  View {

    /**
     * 显示动态加载
     */
    void showLodding(String message);

    /**
     * 显示错误信息(命名不是很好你们自己看着调)
     */
    void showError(String error);

    /**
     * 隐藏动态加载
     */
    void onHideLodding();
}

     OK接下来我们就讲 Presenter跟 Activity关联起来

  3. Activity

       单单讲MVP的话,这个 Activity不是很好,不过为了以后封装上Dagger2将就将就哈,顺带说一下笔者这里的屏幕适配用的是鸿洋大神的AndroidAutoLayout。github:https://github.com/hongyangAndroid/AndroidAutoLayout

/**
 * <pre>
 *    author  : cassie
 *    email   : 
 *    time    : 2018/08/28.
 *    desc    : Activity的基类
 *    version : 1.0
 * </pre>
 */
public abstract class  Activity<V extends  View, P extends  Presenter<V>> extends AutoLayoutActivity implements  View {

    /**
     * Context
     */
    protected Context mContext;
    /**
     * 加载框
     */
    protected ProgressDialog progressDialog;
    /**
     * Toast
     */
    protected Toast toast;
    /**
     * 视图解绑
     */
    protected Unbinder unbinder;
    /**
     * 对应的Presenter类
     */
    protected P mPrestener;

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        //加载布局
        setContentView(getLayoutResId());
        //将当前activity加入到activity管理器中
        AppManager.getInstance().addActivity(this);
        mContext = this;
        //初始化一个Toast,避免多次弹出toast
        toast = Toast.makeText(this, \"\", Toast.LENGTH_SHORT);
        unbinder = ButterKnife.bind(this); //绑定ButterKnife
        //禁止横屏
        setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
        //页面初始化调用
        initView(savedInstanceState);
        //沉浸式状态栏  只有4.4以上的Android版本才可以设置
//        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
//            Window window = getWindow();
//            window.clearFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);
//            window.addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS);
//            window.getDecorView().setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN |
//                    View.SYSTEM_UI_FLAG_LAYOUT_STABLE);
//            window.setStatusBarColor(Color.TRANSPARENT);
//        }
        createPresenter();
        //绑定view
        if (mPrestener!=null)
        mPrestener.attach((V)this);
    }


    /**
     * 加载Presenter
     *
     * @return
     */
    protected abstract void createPresenter();

    @Override
    public void showLodding(String message) {
        showLoadingDialog(message);
    }

    @Override
    public void showError(String error) {
        onHideLodding();
        SToast(error);
    }

    @Override
    public void onHideLodding() {
        dismissLoadingDialog();
    }


    /**
     * 显示加载对话框
     *
     * @param message
     */
    protected void showLoadingDialog(String message) {
        if (progressDialog == null) {
            progressDialog = new ProgressDialog(mContext);
        }
        progressDialog.setMessage(message);
        if (!isFinishing()) {
            progressDialog.show();

        }
        progressDialog.setCancelable(false);
    }

    /**
     * 显示加载对话框
     */
    protected void showLoadingDialog(String message, boolean cancelable) {
        if (progressDialog == null) {
            progressDialog = new ProgressDialog(mContext);
        }
        progressDialog.setMessage(message);
        if (!isFinishing()) {
            progressDialog.show();
        }
        progressDialog.setCancelable(cancelable);
    }

    /**
     * 隐藏加载对话框
     */
    protected void dismissLoadingDialog() {
        if (progressDialog != null && progressDialog.isShowing()) {
            progressDialog.dismiss();
        }
    }

    /**
     * 提示框
     */
    protected void SToast(String message) {
        synchronized (mContext) {
            toast.cancel();
            toast = Toast.makeText(mContext, message, Toast.LENGTH_SHORT);
            toast.show();
        }
    }


    /**
     * 提示框(中间)
     */
    protected void CToast(String message) {
        synchronized (mContext) {
            toast.cancel();
            toast = Toast.makeText(mContext, message, Toast.LENGTH_SHORT);
            toast.setGravity(Gravity.CENTER, 0, 0);
            toast.show();
        }
    }

    /**
     * 启动Activity
     *
     * @param activity 启动的Activity
     * @param bundle   携带的Bundle数据
     * @param isFinish 是否结束当前Activity
     */
    protected void startActivity(Class<?> activity, Bundle bundle, boolean isFinish) {
        Intent intent = new Intent();
        intent.setClass(mContext, activity);
        if (bundle != null) {
            intent.putExtras(bundle);
        }
        startActivity(intent);
        if (isFinish) {
            finish();
        }
    }

    /**
     * 带返回的启动Activity
     *
     * @param activity    目标activity
     * @param bundle      携带的bundle数据
     * @param requestCode 返回数
     * @param isFinish    是否结束当前Activity
     */
    protected void startActivityForResult(Class<?> activity, Bundle bundle, int requestCode,
                                          boolean isFinish) {
        Intent intent = new Intent();
        intent.setClass(mContext, activity);
        if (bundle != null) {
            intent.putExtras(bundle);
        }
        startActivityForResult(intent, requestCode);
        if (isFinish) {
            finish();
        }
    }

    /**
     * 结束当前activity
     */
    @Override
    public void finish() {
        super.finish();
    }

    /**
     * 隐藏键盘
     */
    public void unkeyboard() {
        try {
            InputMethodManager inputMethodManager = (InputMethodManager) getSystemService(Context
                    .INPUT_METHOD_SERVICE);
            if (inputMethodManager.isActive()) {
                inputMethodManager.hideSoftInputFromWindow(((Activity) mContext).getCurrentFocus
                        ().getWindowToken(), 0);
            }
        } catch (Exception e) {
            // TODO: handle exception
        }
    }

    /**
     * 提供给toolbar 的back 放回事件
     */
    public void backfinish(View view) {
        finish();
        unkeyboard();
    }

    /**
     * 获取到Context
     *
     * @return
     */
    public Context getContext() {
        return mContext;
    }

    /**
     * Activity销毁的时候调用
     */
    @Override
    protected void onDestroy() {
        //解绑ButterKnife
        unbinder.unbind();
        //解除Presenter持有的view和Sub ion
        if (mPrestener!=null)
        mPrestener.deatch();
        super.onDestroy();
    }

    /**
     * 初始化页面布局
     *
     * @param savedInstanceState 保留数据
     */
    protected abstract void initView(@Nullable Bundle savedInstanceState);  //初始化

    /**
     * 加载布局
     *
     * @return 布局R.layout.****
     */
    protected abstract int getLayoutResId();
}

ok,接下来我们就做一个简单的登陆功能。

  • 登陆接口管理类LoginContract

/**
 * 登陆的接口管理类
 * Created by admin on 2018/12/17.
 */
public interface LoginContract {

    /**
     * View接口类
     */
    interface View extends  View {
        void doLoginSuccess(LoginBean dataBean);
    }

    /**
     * Presenter静态类
     */
    abstract class Presenter extends  Presenter<View> {

        //登录
        public abstract void doLogin(String account, String password);

    }
}
  • 接着写我们的LoginActivity

     

package com.fengzhi.my .module.main.ui.activity;

import android.os.Bundle;
import android.support.annotation.Nullable;
import android.view.View;
import android.widget.EditText;

import com.fengzhi.my .R;
import com.fengzhi.my . . Activity;
import com.fengzhi.my .bean.LoginBean;
import com.fengzhi.my .module.main.contract.LoginContract;
import com.fengzhi.my .module.main.presenter.LoginPresenter;

import java.io.File;

import butterknife.BindView;
import butterknife. ;

public class LoginActivity extends  Activity<LoginContract.View, LoginPresenter> implements
        LoginContract.View {

    @BindView(R.id.username_et)
    EditText usernameEt;
    @BindView(R.id.password_et)
    EditText passwordEt;

    @Override
    protected void initView(@Nullable Bundle savedInstanceState) {

    }

    @Override
    public void doLoginSuccess(LoginBean dataBean) {
        SToast(\"dd:\" + dataBean.getUsername());

    }

    @ (R.id.login_but)
    public void onViewClicked() {
 
       mPrestener.doLogin(usernameEt.getText().toString(), passwordEt.getText().toString
                        ());


    }


    @Override
    protected void createPresenter() {
        //实例化LoginPresenter并传入View
        mPrestener = new LoginPresenter(this);

    }

    @Override
    protected int getLayoutResId() {
        return R.layout.activity_login;
    }


}
  •  还有我们的LoginPresenter
package com.fengzhi.my .module.main.presenter;



import com.fengzhi.my .bean.LoginBean;
import com.fengzhi.my .module.main.contract.LoginContract;


/**
 * Created by admin on 2018/12/17.
 */

public class LoginPresenter extends LoginContract.Presenter {

    public LoginPresenter(LoginContract.View view) {
        this.mView = view;
    }


    @Override
    public void doLogin(String account, String password) {
         //假装登陆成功
         LoginBean bean=new LoginBean ();
         bean.setUsername(\"cassie\");
         //登陆操作
         mView.doLoginSuccess(bean);
    }


}
  •   顺便把布局文件也贴出来
<?  version=\"1.0\" encoding=\"utf-8\"?>
<LinearLayout  ns:android=\"http://schemas.android.com/apk/res/android\"
     ns:tools=\"http://schemas.android.com/tools\"
    android:layout_width=\"match_parent\"
    android:orientation=\"vertical\"
    android:layout_height=\"match_parent\"
    tools:context=\"com.fengzhi.my .module.main.ui.activity.LoginActivity\">

    <include layout=\"@layout/toolbar\"/>


    <EditText
        android:id=\"@+id/username_et\"
        android:layout_marginTop=\"25px\"
        android:background=\"#fff\"
        android:paddingTop=\"14px\"
        android:paddingBottom=\"14px\"
        android:layout_gravity=\"center\"
        android:textSize=\"24px\"
        android:paddingLeft=\"8px\"
        android:paddingRight=\"8px\"
        android:hint=\"用户名\"
        android:layout_width=\"680px\"
        android:layout_height=\"70px\" />

    <EditText
        android:id=\"@+id/password_et\"
        android:layout_marginTop=\"25px\"
        android:background=\"#fff\"
        android:paddingTop=\"14px\"
        android:paddingBottom=\"14px\"
        android:layout_gravity=\"center\"
        android:textSize=\"24px\"
        android:paddingLeft=\"8px\"
        android:paddingRight=\"8px\"
        android:hint=\"密码\"
        android:layout_width=\"680px\"
        android:layout_height=\"70px\" />

    <Button
        android:id=\"@+id/login_but\"
        android:background=\"#fff\"
        android:text=\"登陆\"
        android:textSize=\"28px\"
        android:layout_marginTop=\"30px\"
        android:layout_gravity=\"center\"
        android:layout_width=\"680px\"
        android:layout_height=\"80px\" />


</LinearLayout>

好了MVP的骨架就封装到这里了,下一节我们加上Retrofit还有RxJava。

收藏 打印