精华内容
下载资源
问答
  • mvp

    2017-01-19 16:59:51
    MVP是个好东西,可是最近项目一直用的是mvc模式,先马克下之前鼓捣过的mvp框架,过年回家再用它重构下代码。 先上依赖库 compile 'io.reactivex:rxandroid:1.2.1' compile '...

    MVP是个好东西,可是最近项目一直用的是mvc模式,先马克下之前鼓捣过的mvp框架,过年回家再用它重构下代码。

    先上依赖库

      compile 'io.reactivex:rxandroid:1.2.1'  
      compile 'com.squareup.okhttp3:okhttp:3.3.1'  
      compile 'io.reactivex:rxandroid:1.1.0'  
      compile 'io.reactivex:rxjava:1.1.0'  
      compile 'com.squareup.retrofit:retrofit:2.0.0-beta2'  
      compile 'com.squareup.retrofit:converter-gson:2.0.0-beta2'  
      compile 'com.squareup.retrofit:adapter-rxjava:2.0.0-beta2'  
      compile 'com.android.support:design:24.2.1'  
      compile 'com.android.support:recyclerview-v7:24.2.1'  
      compile 'com.android.support:cardview-v7:24.2.1'  
      compile 'com.jakewharton:butterknife:7.0.1'  
      compile 'com.github.bumptech.glide:glide:3.7.0'  
      compile 'com.github.chrisbanes.photoview:library:1.2.3'

    建议写死依赖的版本号,而不要使用“+”,避免版本升级带了一些奇葩的问题。

    依赖retrolambda

    在app.build依赖

        apply plugin: 'me.tatarka.retrolambda'

    再加上

        compileOptions {  
        sourceCompatibility JavaVersion.VERSION_1_8  
         targetCompatibility JavaVersion.VERSION_1_8  
       }

    然后在整个项目的build文件上面加入:

          classpath 'me.tatarka:gradle-retrolambda:3.2.5'

    OK,到这里我们的环境搭建就完成了


    首先我们看下项目的目录结构:


    base文件

    BaseContract(基本的文件类,我们可以在里面写上view层,model层,Presenter层的interface)

      /**
       * Created by Ly on 2016/11/2.
       */
    
      public class BaseContract  {
    }

    BaseModel(基本的model层,所有耗时操作应该写在model层中)

      /**
       * Created by Ly on 2016/11/2.
       */
    
      public interface BaseModel {
      }

    BaseView层(写入跟用户交互的方法集合类,比如showTosast,showDialog)

        /**
       * Created by Ly on 2016/11/2.
         */
    
      public interface BaseView {
          void TsShow(String msg);
      }

    看下我们的基本baseActivity.java:

    package com.Ly.BaseJustTalk.base;
    
    import android.app.ProgressDialog;
    import android.content.Context;
    import android.content.Intent;
    import android.os.Build;
    import android.os.Bundle;
    import android.support.annotation.Nullable;
    import android.support.design.widget.AppBarLayout;
    import android.support.v4.widget.SwipeRefreshLayout;
    import android.support.v7.app.ActionBar;
    import android.support.v7.app.AppCompatActivity;
    import android.support.v7.widget.Toolbar;
    import android.text.TextUtils;
    import android.util.TypedValue;
    import android.view.MenuItem;
    import android.widget.Toast;
    
    import com.Ly.BaseJustTalk.R;
    
    import butterknife.ButterKnife;
    
    /**
     * Created by Ly on 2017/1/12.
     */
    public abstract class BaseActivity<V, T extends BasePresenter<V>> extends AppCompatActivity {
    
        protected T mPresenter;
        private SwipeRefreshLayout mRefreshLayout;
        private AppBarLayout mAppBar;
        private Toolbar mToolbar;
        private ProgressDialog mProgressBar;
        protected Context mContext;
        private boolean mIsRequestDataRefresh = false;
    
        private static final String EXTRA_KEY = "extra";
    
        @Override
        protected void onCreate(@Nullable Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            dogetExtra();
            mContext = this;
            // 允许为空,不是所有的页面都要实现这个模式
            if (createPresenter() != null) {
                mPresenter = createPresenter();
                mPresenter.attachView((V) this);
            }
            setContentView(provideContentViewId());
            ButterKnife.bind(this);
    
            mAppBar = findViewById(R.id.app_bar_layout);
            mToolbar = (Toolbar) findViewById(R.id.toolbar);
            if (mToolbar != null && mAppBar != null) {
                setSupportActionBar(mToolbar); //把Toolbar当做ActionBar给设置
    
                if (canBack()) {
                    ActionBar actionBar = getSupportActionBar();
                    if (null != actionBar) {
                        //设置ActionBar一个返回箭头,主界面没有,次级界面有
                        actionBar.setDisplayHomeAsUpEnabled(true);
                    }
                    if (Build.VERSION.SDK_INT >= 21) {
                        //Z轴浮动
                        mAppBar.setElevation(10.6F);
                    }
                }
            }
            if (isSetRefresh()) {
                setupSwipeRefresh();
            }
        }
    
    
        public static void doStartActivity(Context context, Bundle bundle, Class<?> clz) {
            Intent intent = new Intent(context, clz);
            if (bundle != null) {
                intent.putExtra(EXTRA_KEY, bundle);
            }
            context.startActivity(intent);
        }
    
    
        protected abstract void dogetExtra();
    
        @Override
        public boolean onOptionsItemSelected(MenuItem item) {
            // 此时android.R.id.home即为返回箭头
            if (item.getItemId() == android.R.id.home) {
                onBackPressed();
                finish();
                return true;
            } else {
                return super.onOptionsItemSelected(item);
            }
        }
    
        @Override
        protected void onDestroy() {
            super.onDestroy();
            if (mPresenter != null) {
                mPresenter.detachView();
            }
        }
    
        /**
         * 生成下拉刷新
         */
        private void setupSwipeRefresh() {
            mRefreshLayout = findViewById(R.id.swipe_refresh);
            if (null != mRefreshLayout) {
                mRefreshLayout.setColorSchemeResources(R.color.colorAccent, R.color.colorPrimary);
                mRefreshLayout.setProgressViewOffset(true,
                        0,
                        (int) TypedValue.applyDimension
                                (TypedValue.COMPLEX_UNIT_DIP, 24, getResources()
                                        .getDisplayMetrics()));
            }
        }
    
    
        /**
         * 设置刷新
         *
         * @param requestDataRefresh
         */
        public void setRefresh(boolean requestDataRefresh) {
            if (mRefreshLayout == null) {
                return;
            }
            if (!requestDataRefresh) {
                mIsRequestDataRefresh = false;
                mRefreshLayout.postDelayed(() -> {
                    if (mRefreshLayout != null) {
                        mRefreshLayout.setRefreshing(false);
                    }
                }, 1000);
            } else {
                mRefreshLayout.setRefreshing(true);
            }
        }
    
    
        /**
         * 数据刷新
         */
        public void requestDataRefresh() {
            mIsRequestDataRefresh = true;
        }
    
        /**
         * 判断当前页面是否可以返回,
         * 主界面不可以返回  子界面可以放回
         *
         * @return
         */
        public boolean canBack() {
            return false;
        }
    
        /**
         * 判断子Activity是否需要上下拉刷新功能
         *
         * @return
         */
        public Boolean isSetRefresh() {
            return false;
        }
    
        /**
         * 创建P
         *
         * @return
         */
        protected abstract T createPresenter();
    
        /**
         * 引入布局文件
         *
         * @return
         */
        protected abstract int provideContentViewId();
    
        protected void ShowTs(String msg) {
            Toast.makeText(this, msg, Toast.LENGTH_SHORT).show();
        }
    
    
        protected void ShowDialog() {
            ShowDialog(null);
        }
    
        protected void ShowDialog(String msg) {
            if (TextUtils.isEmpty(msg)) {
                msg = getString(R.string.loading);
            }
            mProgressBar = ProgressDialog.show(this, null, msg);
        }
    
        protected void DissDialog() {
            if (mProgressBar != null && mProgressBar.isShowing()) {
                mProgressBar.dismiss();
            }
        }
    }

    相对应的BaseFragment.java 代码如下:

    package com.Ly.BaseJustTalk.base;
    
    import android.content.Context;
    import android.os.Bundle;
    import android.support.v4.app.Fragment;
    import android.support.v4.widget.SwipeRefreshLayout;
    import android.util.TypedValue;
    import android.view.LayoutInflater;
    import android.view.View;
    import android.view.ViewGroup;
    
    import com.Ly.BaseJustTalk.R;
    
    import butterknife.ButterKnife;
    
    /**
     * Created by Ly on 2011/1/12.
     */
    
    public abstract class BaseFragment<V, T extends BasePresenter<V>> extends Fragment {
        protected Context mContext;
        protected T mPresenter;
    
        private boolean mIsRequestDataRefresh = false;
        private SwipeRefreshLayout mRefreshLayout;
    
        @Override
        public void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            mContext = getActivity();
            mPresenter = createPresenter();
            mPresenter.attachView((V) this);
        }
    
        @Override
        public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
            View rootView = inflater.inflate(createViewLayoutId(), container, false);
            ButterKnife.bind(this, rootView);
            initView(rootView);
            if (isSetRefresh()) {
                setupSwipeRefresh(rootView);
            }
            return rootView;
        }
    
        @Override
        public void onDestroy() {
            super.onDestroy();
            mPresenter.detachView();
        }
    
        private void setupSwipeRefresh(View view) {
            mRefreshLayout = (SwipeRefreshLayout) view.findViewById(R.id.swipe_refresh);
            if (mRefreshLayout != null) {
                mRefreshLayout.setColorSchemeResources(R.color.refresh_progress_1,
                        R.color.refresh_progress_2, R.color.refresh_progress_3);
                mRefreshLayout.setProgressViewOffset(true, 0, (int) TypedValue
                        .applyDimension(TypedValue.COMPLEX_UNIT_DIP, 24, getResources().getDisplayMetrics()));
                mRefreshLayout.setOnRefreshListener(this::requestDataRefresh);
            }
        }
    
        public void requestDataRefresh() {
            mIsRequestDataRefresh = true;
        }
    
    
        public void setRefresh(boolean requestDataRefresh) {
            if (mRefreshLayout == null) {
                return;
            }
            if (!requestDataRefresh) {
                mIsRequestDataRefresh = false;
                mRefreshLayout.postDelayed(() -> {
                    if (mRefreshLayout != null) {
                        mRefreshLayout.setRefreshing(false);
                    }
                }, 1000);
            } else {
                mRefreshLayout.setRefreshing(true);
            }
        }
    
        protected abstract T createPresenter();
    
        protected abstract int createViewLayoutId();
    
        protected void initView(View rootView) {
        }
    
        public Boolean isSetRefresh() {
            return true;
        }
    }

    Ok,直接看实例代码来理解,就用登录界面的吧。


    登录界面的包目录

    其中LoginActiviy为用户能看到的界面;
    Contract是mvp的协议层;Model为数据处理层;Presenter可以理解为桥梁,负责沟通连接M层和V层。

    在我的理解里的MVP是这个意思:我们可以看到,这个类持有了View和Model两个模块,在方法体里面,我们调用了model的方法去做耗时,在结果方法体里面我们调用了view的方法去修改UI,同时presenter这个模块又被view持有了,view可以在声明周期里面去调用特定的方法,view和presenter相互沟通,view和model完全隔离,presenter调控model,presenter沟通全局。

    看下我们的登录界面:
    activity_login.xml

    <?xml version="1.0" encoding="utf-8"?>
    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical">
    
        <TextView
            android:layout_width="match_parent"
            android:layout_height="0dp"
            android:layout_weight="1.5"
            android:gravity="center"
            android:text="@string/text_welcome"
            android:textSize="25sp" />
    
    
        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="0dp"
            android:layout_marginLeft="@dimen/margin_left"
            android:layout_marginRight="@dimen/margin_right"
            android:layout_weight="3"
            android:orientation="vertical">
    
            <android.support.design.widget.TextInputLayout
                android:id="@+id/til_username"
                android:layout_width="match_parent"
                android:layout_height="wrap_content">
    
                <EditText
                    android:id="@+id/et_username"
                    style="@style/edit_style"
                    android:inputType="text"
                    android:layout_width="match_parent"
                    android:layout_height="wrap_content"
                    android:hint="@string/text_username" />
            </android.support.design.widget.TextInputLayout>
    
            <android.support.design.widget.TextInputLayout
                android:id="@+id/til_password"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:layout_marginTop="@dimen/margin6">
    
                <EditText
                    android:id="@+id/et_password"
                    style="@style/edit_style"
                    android:inputType="textPassword"
                    android:layout_width="match_parent"
                    android:layout_height="wrap_content"
                    android:hint="@string/text_password" />
            </android.support.design.widget.TextInputLayout>
    
            <Button
                android:id="@+id/bt_login"
                style="@style/button_style"
                android:layout_width="match_parent"
                android:layout_height="@dimen/button_height"
                android:layout_marginTop="@dimen/margin20"
                android:text="@string/text_login_reg" />
        </LinearLayout>
    </LinearLayout>

    LoginContract.java

    package com.Ly.BaseJustTalk.ui.activity.Login;
    
    import com.Ly.BaseJustTalk.base.BaseModel;
    import com.Ly.BaseJustTalk.base.BaseView;
    
    /**
     * Created by Ly on 2017/1/16.
     */
    public class LoginContract {
    
    
        interface LoginView extends BaseView {
            String getUserName();
    
            String getUserPass();
    
            //            userName错误后进行提示
            void setUserNameErr(String errMsg);
    
            //            密码错误后进行提示
            void setPassErr(String errMsg);
    
            void doJustalkLogin();
    
            void doLoginFial();
        }
    
    
        interface LoginModel extends BaseModel {
    
            /**
             * @param userName
             * @param Pass
             * @return 0 合法账户密码 给予执行下一步
             * -1 用户名为空
             * -2 用户密码为空
             * -3 用户名不合法
             * -4 用户密码不合法
             */
            int isRightUserNamePass(String userName, String Pass);
    
            /**
             * 进行justalk的登录
             *
             * @param userName
             * @param pass
             * @return
             */
            boolean doJustalkLogin(String userName, String pass);
        }
    
    
        interface LoginPresenter {
            //        验证用户输入的信息
            void doVerificationInfo();
    
            //          验证正确后进行登录
            void doJustalkLogin();
        }
    }

    LoginModel.java

    package com.Ly.BaseJustTalk.ui.activity.Login;
    
    import android.text.TextUtils;
    import android.util.Log;
    
    import com.Ly.BaseJustTalk.R;
    import com.Ly.BaseJustTalk.application.LyApplication;
    import com.justalk.cloud.juslogin.LoginDelegate;
    
    /**
     * Created by Administrator on 2017/1/16.
     */
    public class LoginModel implements LoginContract.LoginModel {
    
    
        /**
         * 验证账号合法性
         *
         * @param userName
         * @param Pass
         * @return 0 合法账户密码 给予执行下一步
         * -1 用户名为空
         * -2 用户密码为空
         * -3 用户名不合法
         * -4 用户密码不合法
         */
        @Override
        public int isRightUserNamePass(String userName, String Pass) {
            if (TextUtils.isEmpty(userName)) {
                return -1;
            } else if (TextUtils.isEmpty(Pass)) {
                return -2;
            } else if (userName.length() < 4 || userName.length() > 12) {
                return -3;
            } else if (Pass.length() < 6 || Pass.length() > 12) {
                return -4;
            } else
                return 0;
        }
    
        @Override
        public boolean doJustalkLogin( String userName, String pass) {
            if (LoginDelegate.getInitState() == LoginDelegate.InitStat.MTC_INIT_FAIL) {
                return false;
            }
            String server = LyApplication.getInstance().getString(R.string.JusTalkCloud_network_address);
            String network = null;
            if (!server.startsWith("sudp:")) {
                network = server;
                server = "sarc:arc@AccessEntry:99";
                Log.e("testtest", server);
            }
            if (LoginDelegate.login(userName, pass, server, network)) {
                return true;
            }
            return false;
        }
    }

    LoginPresenter.java

    package com.Ly.BaseJustTalk.ui.activity.Login;
    
    import com.Ly.BaseJustTalk.base.BasePresenter;
    
    /**
     * Created by Ly on 2017/1/16.
     */
    public class LoginPresenter extends BasePresenter<LoginContract.LoginView> implements
            LoginContract.LoginPresenter {
        private LoginContract.LoginView loginView;
        private LoginContract.LoginModel loginModel;
    
        public LoginPresenter(LoginContract.LoginView loginView) {
            this.loginView = loginView;
            loginModel = new LoginModel();
        }
    
        @Override
        public void doVerificationInfo() {
            int resultcode = this.loginModel.isRightUserNamePass(this.loginView.getUserName(), this.loginView.getUserPass());
            switch (resultcode) {
                case -1:
                    this.loginView.setUserNameErr("请输入用户名");
                    break;
                case -2:
                    this.loginView.setPassErr("请输入用户密码");
                    break;
                case -3:
                    this.loginView.setUserNameErr("请检查用户名格式或长度");
                    break;
                case -4:
                    this.loginView.setPassErr("请检查密码格式或长度");
                    break;
                case 0:
                    this.loginView.setUserNameErr(null);
                    this.loginView.setPassErr(null);
                    this.loginView.doJustalkLogin();
                    break;
                default:
                    this.loginView.TsShow("未知错误");
                    break;
            }
        }
    
        @Override
        public void doJustalkLogin() {
            boolean isLoginSuccess = this.loginModel.doJustalkLogin(this.loginView.getUserName(), this.loginView.getUserPass());
            if (!isLoginSuccess) {
                this.loginView.doLoginFial();
            }
        }
    }

    LoginActivity.java

    package com.Ly.BaseJustTalk.ui.activity.Login;
    
    import android.os.Bundle;
    import android.support.design.widget.TextInputLayout;
    import android.util.Log;
    import android.widget.Button;
    import android.widget.EditText;
    
    import com.Ly.BaseJustTalk.R;
    import com.Ly.BaseJustTalk.base.BaseActivity;
    import com.Ly.BaseJustTalk.ui.activity.MainActivity;
    import com.Ly.BaseJustTalk.utils.Signer;
    import com.justalk.cloud.juslogin.LoginDelegate;
    import com.justalk.cloud.lemon.MtcCli;
    import com.justalk.cloud.lemon.MtcCliConstants;
    
    import butterknife.Bind;
    import butterknife.OnClick;
    
    /**
     * Created by Ly on 2017/1/16.
     */
    public class LoginActivity extends BaseActivity<LoginContract.LoginView, LoginPresenter>
            implements LoginContract.LoginView, LoginDelegate.Callback {
        @Bind(R.id.et_username)
        EditText etUsername;
        @Bind(R.id.til_username)
        TextInputLayout tilUsername;
        @Bind(R.id.et_password)
        EditText etPassword;
        @Bind(R.id.til_password)
        TextInputLayout tilPassword;
        @Bind(R.id.bt_login)
        Button btLogin;
    
    
        private LoginContract.LoginPresenter loginPresenter = new LoginPresenter(this);
    
        @OnClick(R.id.bt_login)
        void toLogin() {
            loginPresenter.doVerificationInfo();
        }
    
        @Override
        protected LoginPresenter createPresenter() {
            return new LoginPresenter(this);
        }
    
        @Override
        protected int provideContentViewId() {
            return R.layout.activity_login;
        }
    
        @Override
        public void TsShow(String msg) {
            ShowTs(msg);
        }
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            LoginDelegate.setCallback(this);
        }
    
    
        @Override
        protected void dogetExtra() {
    
        }
    
    
        @Override
        public String getUserName() {
            return etUsername.getText().toString().trim();
        }
    
        @Override
        public String getUserPass() {
            return etPassword.getText().toString().trim();
        }
    
        @Override
        public void setUserNameErr(String errMsg) {
            tilUsername.setError(errMsg);
        }
    
        @Override
        public void setPassErr(String errMsg) {
            tilPassword.setError(errMsg);
        }
    
        @Override
        public void doJustalkLogin() {
            ShowDialog();
            if (MtcCli.Mtc_CliGetState() != MtcCliConstants.EN_MTC_CLI_STATE_LOGINED) {
                this.loginPresenter.doJustalkLogin();
            } else {
                MainActivity.doStartActivity(mContext, null, MainActivity.class);
            }
        }
    
        @Override
        public void doLoginFial() {
            ShowTs(getString(R.string.tips_login_fail));
        }
    
        @Override
        public void mtcLoginOk() {
            MainActivity.doStartActivity(mContext, null, MainActivity.class);
        }
    
        @Override
        public void mtcLoginDidFail() {
            ShowTs(getString(R.string.tips_login_fail_justalk));
        }
    
        @Override
        public void mtcLogoutOk() {
    
        }
    
        @Override
        public void mtcLogouted() {
    
        }
    
        @Override
        public void mtcAuthRequire(String s, String s1) {
            Log.e("LHT", "mtcAuthRequire: " + s + "----" + s1);
            String key =
                    "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" +
                            "tRPBKBjUTEQwaKEjPy8YnX1bUODPB+7hto8KeGJbnCdCcnJdLxPFE7ld1skKIyPi" +
                            "YkyHj73JqA41ntHML2LNqw5Mhs1pewE4QLCu6icIUNtH8+bL53EhVnfdzwIDAQAB" +
                            "AoGAA6i6c5xHEGfKzoDHQJgiC5X9ZFAtES5AG3IMJmtF9flQyeoeDzRit+/FwNXi" +
                            "M1CKohnvLAJTvPs/8TBp5us4rabQ5Hnp+ylr7I2IbYIP2LV6TKkiTq/fBOJBxZiw" +
                            "qs0tjXxRZnC2IWqoCt/ciE4DXQIYVV3gYMRcKae5KZ3F2LECQQDqL4Sd2xyG0FsW" +
                            "cKwrlFRQ1cfFrSF/jmm2onkCZgDfq0R5aIGewpbTciLj8rf/Zq0XgAmCa3qQYo6M" +
                            "7G0OgIXTAkEAxbIC2xJocvPfEFbUd+hEWDFl/3LtQWZSHVLx9SXLXWSRY4/3dyRM" +
                            "6L78eQ2yWIVF4pxJrIHTbJqhxItlVM/elQJBAJ3jRZ0L8hKufQsHEf0btzD8wQB0" +
                            "doZCZOF+bumADgy+sp7MJ7/636dVZ1KZ/RWTixWx/DdS8UJRQFygtfI2EoMCQHky" +
                            "4tFPfb1LiStJMES6nnu6/R8YZB++DQVxPmjeXMjKyN9S+ZGPLZ9axwmnvfjK68c7" +
                            "rWcWyHlCa35FP0A5l+kCQB5cEu5Au1RkY9XfUodKmFhlCvdY8Ig0JgZ8DC6m+A31" +
                            "o4xrCoGHiPldKdCo0I7gQ4WMvoVNHCQyNv5qcw9t7uk=";
            String code = Signer.signWithKey(key, s, s1, 3600);
            LoginDelegate.promptAuthCode(code);
        }
    
    
        @Override
        protected void onDestroy() {
            super.onDestroy();
            LoginDelegate.logout();
        }
    }
    展开全文
  • 初识MVP

    2018-01-08 17:39:18
    学习Android有一段时间了,一直以实现一些小功能的方式来学习。这样就导致了代码写的不怎么规范,以至于后来重新翻看自己的代码并且重构自己的项目的时候,被自己的代码恶心到了。 以至于后来学习了一些设计模式,...

    初识MVP

    学习Android有一段时间了,一直以实现一些小功能的方式来学习。这样就导致了代码写的不怎么规范,以至于后来重新翻看自己的代码并且重构自己的项目的时候,被自己的代码恶心到了。
    以至于后来学习了一些设计模式,以及架构工程的设计模式才得以改进。

    话不多说,下面谈谈我学习MVP的过程以及我对MVP的理解。

    谈及MVP,许多人都会先从MVC讲起。我认为这是很有必要的,因为MVP架构,就是因为MVC在Android无法更好解决问题的时候才提出的。那么什么是MVC,MVC有什么优缺点,为什么MVC在大型的Andorid项目中暴露出那么多缺点呢?

    带着这几个问题,下面先来从新认识一下MVC架构

    1、什么是MVC?

    关于什么是MVC,网上有很多资料,我这里也不班门弄斧,简单的给大家进行介绍。
    MVC即Model-View-Controller。M:逻辑模型,V:视图模型,C:控制器

    逻辑模型(Model)

    所谓逻辑模型,通俗的理解就是指业务逻辑层,同时对数据的操作的层,比如网络中请求一些数据、数据库中存取等

    视图模型(View)

    视图层,望文即可生义,视图层就是可以理解为展示给用户的界面,其中包括一系列View组件、布局、按钮等等。

    控制层(Controller)

    控制层是MVC中相对比较重要的一层,它负责协调M-V两层,在Android中,Activity就是一个典型的Controller,它即可响应View的事件,也可调用Model中的数据和方法,进而反馈到View层中

    所以,MVC中,基本的流程就是,M负责数据的来源和处理,然后通过C将数据反馈到V。

    2、MVC有什么优点?

    MVC对于传统的应用是有着巨大的帮助与贡献的,为什么这么说?因为,在传统的应用中,MVC三层的划分可以让工程划分的更分析,同时,这样也可以实现层次间的解耦,以及提升开发的效率。在开发的过程中,只要相互间约定好接口,就可以各自开发而且互相之间不会有什么冲突。
    另外,MVC的另一个优点就是简单。由于它的简单性,就可以让开发人员迅速掌握,并且很好的运用到工程中。

    3.MVC为何在大型Android项目中会显得有点不太“适用”?

    MVC在Android这个“特殊”的环境中有时会显得有点力不从心甚至还会出现拖后腿的现象。
    在Android中,Activity通常就充当一个Controller的角色,但Activity还具有操作View的责任、响应View的事件以及响应自身生命周期的责任。这样一来,使得一个Activity的代码数量很容易上千甚至上万,如此一来,Activity就变得非常臃肿,变得不好维护和阅读。除此以外,C-V之间耦合度也就高了,甚至可以说是M和V重合在一起了。

    基于上面的几个问题,我们就可以引出

    MVP模式

    看了上面几个问题,你可能会认为MVP的出现可能大概就是为了填MVC所挖的坑的吧。

    MVP其实就是MVC的“进化版”,和MVC一样,MV都分别代表Model和View层,Presenter则是表示层。

    下面用两个图来表示

    上面可以看出,MVC中允许Model和View中进行交互,而在MVP中,把Layout布局和Activity作为View层,增加了Presenter层,利用Presenter和Model层进行业务交互,然后Presenter再与View层交互,从而避免了View与Modele之间相互联系的现象。出现这样的根本原因在于Presenter的出现让Activity的工作量减轻了不少,它把Controller的工作从Activity身上切了出来。

    上面分析了一下MVP,接下来就是实现MVP的一个Demo

    实现一个简单的登陆功能,包结构如下

    1.准备一个User的实体类

    package com.example.mvp.mvp.bean;
    public class User {
        private String username ;
        private String password ;
        public String getUsername()
        {
            return username;
        }
    
        public void setUsername(String username)
        {
            this.username = username;
        }
    
        public String getPassword()
        {
            return password;
        }
    
        public void setPassword(String password)
        {
            this.password = password;
        }

    2.Model层

    model层需要一个接口以及它的实现类

    package com.example.mvp.mvp.model;
    
    
    import com.example.mvp.mvp.bean.User;
    
    public interface IUserBiz {
        void login(String username,String password,OnLoginListener loginListener);
        interface OnLoginListener {
            void loginSuccess(User user);
            void loginFailed();
        }
    }
    
    

    3.View层

    View层只有一个接口

    package com.example.mvp.mvp.view;
    
    
    import com.example.mvp.mvp.bean.User;
    
    public interface IUserLoginView {
        String getUserName();
        String getPassword();
        void clearPassword();
        void clearUserName();
        void showLoading();
        void hideLoading();
        void toMainActivity(User user);
        void showFailedError();
    }
    

    4.Presenter层

    presenter层有一个实体类

    package com.example.mvp.mvp.presenter;
    
    
    import android.os.Handler;
    
    import com.example.mvp.mvp.bean.User;
    import com.example.mvp.mvp.model.IUserBiz;
    import com.example.mvp.mvp.model.OnLoginListener;
    import com.example.mvp.mvp.model.UserBiz;
    import com.example.mvp.mvp.view.IUserLoginView;
    
    
    
    public class UserLoginPresenter {
        private IUserBiz userBiz;//model层引用
        private IUserLoginView userLoginView;//View层引用
        private Handler mHandler = new Handler();
    
        public UserLoginPresenter(IUserLoginView userLoginView){
            this.userLoginView = userLoginView;
            this.userBiz = new UserBiz();
        }
    
        public void login(){
            userLoginView.showLoading();
            //模拟数据加载时的线程切换(View的操作需要放在主线程)
            userBiz.login(userLoginView.getUserName(), userLoginView.getPassword(), new OnLoginListener() {
                @Override
                public void loginSuccess(final User user) {
                    mHandler.post(new Runnable() {
                        @Override
                        public void run() {
                            userLoginView.toMainActivity(user);
                            userLoginView.hideLoading();
                        }
                    });
                }
    
                @Override
                public void loginFailed() {
                    mHandler.post(new Runnable() {
                        @Override
                        public void run() {
                            userLoginView.showFailedError();
                            userLoginView.hideLoading();
                        }
                    });
                }
            });
        }
    
        public void clear(){
            userLoginView.clearUserName();
            userLoginView.clearPassword();
        }
    }
    

    5.Activity

    package com.example.mvp.mvp;
    
    import android.support.v7.app.AppCompatActivity;
    import android.os.Bundle;
    import android.view.View;
    import android.widget.Button;
    import android.widget.EditText;
    import android.widget.ProgressBar;
    import android.widget.Toast;
    
    import com.example.mvp.R;
    import com.example.mvp.mvp.bean.User;
    import com.example.mvp.mvp.presenter.UserLoginPresenter;
    import com.example.mvp.mvp.view.IUserLoginView;
    
    public class LoginActivity extends AppCompatActivity implements IUserLoginView{
        private EditText mEtUsername, mEtPassword;
        private Button mBtnLogin, mBtnClear;
        private ProgressBar mPbLoading;
        private UserLoginPresenter mUserLoginPresenter = new UserLoginPresenter(this);
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_login);
            initView();
        }
    
        private void initView(){
            mEtUsername = (EditText) findViewById(R.id.et_userName);
            mEtPassword = (EditText) findViewById(R.id.et_password);
    
            mBtnClear = (Button) findViewById(R.id.bt_clear);
            mBtnLogin = (Button) findViewById(R.id.bt_login);
    
            mPbLoading = (ProgressBar) findViewById(R.id.progress);
    
            mBtnLogin.setOnClickListener(new View.OnClickListener()
            {
                @Override
                public void onClick(View v)
                {
                    mUserLoginPresenter.login();
                }
            });
    
            mBtnClear.setOnClickListener(new View.OnClickListener()
            {
                @Override
                public void onClick(View v)
                {
                    mUserLoginPresenter.clear();
                }
            });
        }
        @Override
        public String getUserName()
        {
            return mEtUsername.getText().toString();
        }
    
        @Override
        public String getPassword()
        {
            return mEtPassword.getText().toString();
        }
    
        @Override
        public void clearUserName()
        {
            mEtUsername.setText("");
        }
    
        @Override
        public void clearPassword()
        {
            mEtPassword.setText("");
        }
    
        @Override
        public void showLoading()
        {
            mPbLoading.setVisibility(View.VISIBLE);
        }
    
        @Override
        public void hideLoading()
        {
            mPbLoading.setVisibility(View.GONE);
        }
    
        @Override
        public void toMainActivity(User user) {
            Toast.makeText(this, user.getUsername() +
                    " login success , to MainActivity", Toast.LENGTH_SHORT).show();
        }
    
        @Override
        public void showFailedError() {
            Toast.makeText(this,
                    "login failed", Toast.LENGTH_SHORT).show();
        }
    }
    

    分析、总结

    由上面的代码大概可以看出MVP的基本思想,正如前面所说,MVP的出现是淡化了Activity在安卓中充当Controller这一角色的事实,更多的是把Activity归属到View层中,使得Activity的工作变得更轻松。当然,如果相对于小型的项目,Activity完全可以充当Controller的作用,因为在小型的项目中,Activity处理的东西会相对较少,此时用上MVP的架构可能会将一件简单的事情复杂化。

    说明:上述代码是借鉴鸿洋大神的《浅谈 MVP in Android》一文完成,如有错误,敬请指出,谢谢。

    展开全文
  • MVP 总结

    2019-09-26 23:38:10
    最近一直在看MVP的东西,现在打算来总结一下,以便后边回顾学习 http://msdn.microsoft.com/zh-cn/magazine/ff955232.aspx MVP 的创建者将模型(在视图中处理的数据)与视图/控制器对巧妙地分开。他们还将控制器...

    最近一直在看MVP的东西,现在打算来总结一下,以便后边回顾学习

    http://msdn.microsoft.com/zh-cn/magazine/ff955232.aspx

    MVP 的创建者将模型(在视图中处理的数据)与视图/控制器对巧妙地分开。 他们还将控制器重命名为表示器,以强化一种概念,即在该模式中,控制器的角色就是用户与应用程序之间的调节器的角色。 表示器是向用户“呈现”UI 并接受用户所发出命令的组件。 表示器包含大多数表示逻辑,知道如何处理视图和系统的其余部分,包括后端服务和数据层。

    MVP 中的一项重要创新是,视图的详细信息抽象为接口(或基类)。 表示器与视图的抽象层交互,使表示器自身成为一个可重用性和可测试性均很高的类。 这样可实现两种有意思的方案。

    首先,表示逻辑独立于将要使用的 UI 技术。 因此,可在 Windows 和 Web 表示层中重用同一控制器。 最后,针对某一接口为表示器编码,该表示器可以与公开该接口的任何对象进行交互,而无论该对象是 Windows 窗体对象、ASP.NET 页面对象还是 WPF 窗口对象。

    其次,同一表示器可以处理同一应用程序的不同视图。 这是关于软件即服务 (SaaS) 方案的一个重要成就,在这些方案中,应用程序托管在 Web 服务器上,并作为服务提供给每一个要求使用自定义 UI 的客户。

    毫无疑问,这两项优点都不一定适用于所有情况。 这些优点是否会给您带来益处,很大程度上取决于您期望在 Windows 和 Web 前端中使用的应用程序和导航逻辑。 但是,当逻辑相同时,您可以通过 MVP 模型进行重用。

    mvp的一个例子以及讲解,测试驱动开发的好例子

    http://www.microsoft.com/china/msdn/library/architecture/architecture/architecturetopic/MVP.mspx?mfr=true

    http://www.cnblogs.com/therock/articles/2450923.html

    关于mvp很好的讲解

    http://www.cnblogs.com/artech/archive/2010/03/25/1696205.html

    http://www.cnblogs.com/artech/archive/2010/04/12/1710681.html

    CAB的14条编写符合MVP规范的规则:

    1、所有的View(包括View的接口)的名称应该以View作为后缀,比如TaskView/ITaskView;

    2、所有的Presenter名称应该以Presenter作为后缀,比如TaskViewPresenter;

    3、Presenter完成Use Case处理逻辑,对GUI控件的处理应该在View中实现;

    4、View调用Presenter的方法应该像触发事件异常,通过调用OnXxx方法的方式来实现;

    5、应该尽可能地限制View对Presenter的调用,并且调用的方式限于按照“事件”的形式,比如_presenter.OnViewReady();

    6、View不允许通过Presenter直接调用Model和Service,并且Presenter的方法应该是不具有返回值的;

    7、Presenter必须通过View接口的方式调用View

    8、除了对View接口成员的实现外,View中的其他方法不应该是public的;

    9、除了CAB ModuleController 对View的加载和限制外,View只能被Presenter调用;

    10、View接口方法应该基于Use Case的逻辑起一个有意义的名称,比如SetDataSource这样的方法名称是不合法的;

    11、View接口的成员应该仅限于方法,不应该包含属性;

    12、所有的数据应用保持在Model中

    13、定义在View接口的方法不应该包含对GUI空间名称的引用(比如AddExplorerBarGroup),因为这会使Presenter知道View太多关于实现方面的细节;

    14、尽量让View的方法名称反映Use Case的业务逻辑,这样可以使你的代码具有自表述性并更加易于理解。

    View单纯地将用户的交互请求汇报给Presenter;Presenter接收到请求之后,整合相应的资源、执行相应的处理逻辑对处理流程的某一个步骤,如果设置到业务逻辑和数据模型,则调用Model,如果涉及到对GUI控件的操作,还会调用View。View将交互请求递交给Presenter之后,不需要考虑后续需要做什么,因为Presenter会在适当的时候命令View该如何做。

    UI交互逻辑的处理流程定义在Presenter中,但是具体的实现并不是完全在Presenter中

    杜绝开发人员将程序写成基于Proxy的MVP,在我看来,唯一的办法就是尽量弱化(不可能剔除)View对Presenter的依赖。实际上,对于MVP来说,View仅仅向Presenter递交用户交互请求,仅此而已

     

     

     

    转载于:https://www.cnblogs.com/RealAlex/archive/2012/11/01/2749904.html

    展开全文
  • 初学MVP

    2016-06-28 12:48:54
    一直听群里的人说MVP模式去开发app 自己也搜藏了几篇文章 但一直没事件去学习,正好这两天有空闲的时间就尝试写个demo出来看看。概念信的东西我就不写了 网上的文章对于MVP的概念大致都是一样的。对于新东西 我一般...

    一直听群里的人说MVP模式去开发app 自己也搜藏了几篇文章 但一直没事件去学习,正好这两天有空闲的时间就尝试写个demo出来看看。概念信的东西我就不写了 网上的文章对于MVP的概念大致都是一样的。对于新东西 我一般是通过先写出个demo 以后在运用的过程再去学习此东西里面的详细内容。
    建立MVP 项目 一般需要 建立 view(界面逻辑),model(业务逻辑),presenter(调度)这几个包名。当然项目离不开 bean(实体数据) 基本上这几个包名就能组成 一个基本的MVP框架了 其它的包名根据自己的需要建立

    此次demo我采用的rxjava和retrofit来完成线程调度和网络请求 既然是新的不错的东西 为什么不用呢。
    最后我的工程结构为
    这里写图片描述
    首先从 看看api包里的LoginApi 里面是retrofit的网络请求 非常简单 强烈建议大家使用retrofit 我是看这篇文章学习的
    https://mp.weixin.qq.com/s?__biz=MzA3NTYzODYzMg==&mid=2653577186&idx=1&sn=1a5f6369faeb22b4b68ea39f25020d28&scene=1&srcid=0605oJ1NiiihvcW5jF8iy3n6&key=f5c31ae61525f82e38a1a943febc987278275256a5df936f1119c6514ebc78df82fc229de80607e2372e4c0f1451e4db&ascene=0&uin=Mjc3OTU3Nzk1&devicetype=iMac+MacBookPro10%2C1+OSX+OSX+10.10.5+build%2814F1808%29&version=11020201&pass_ticket=Yfzg8hN3fuLjjXoQDSG%2BBKWgRxryyVOFVwGFzsu7nB%2FgSZp9gdXJS%2FudxKZSNLan

    public interface LoginApi {
    
        @POST("testMvp.aspx")
        @FormUrlEncoded
        Observable<Response<User>> login(@Field("do") String action, @Field("user_info") String userInfo);
    
    }

    base里面封装着基本所有界面和所有业务都需要的方法 这几个类基本不需要做任何修改只需要拷贝到下一个工程 当然你也可以根据需要在里面扩展你想要的方法 关于封装 我借鉴的是
    https://github.com/183619962/MVPTest

    public class BaseModel {
        //retrofit请求数据的管理类
        public RetrofitManager retrofitManager;
    
        public BaseModel() {
            //初始化retrofit
            retrofitManager = RetrofitManager.builder();
        }
    }
    public class BasePresenter <T extends IBaseView,V> implements IBasePresenter, Observer<V> {
    
        public IBaseView iView;
    
        /**
         * 构造方法
         *
         * @param view 具体业务的接口对象
         */
        public BasePresenter(T view) {
            this.iView = view;
        }
    
        @Override
        public void onResume() {
    
        }
    
        @Override
        public void onDestroy() {
    
        }
    
    
        @Override
        public void onCompleted() {
            iView.hideProgress();
        }
    
        @Override
        public void onError(Throwable e) {
            iView.loadDataError(e);
            iView.hideProgress();
        }
    
        @Override
        public void onNext(V t) {
            iView.loadDataSuccess(t);
        }
    }
    public interface IBasePresenter {
        /**
         * 开始<br>
         * 用于做一些初始化的操作
         */
        void onResume();
    
        /**
         * 销毁<br>
         * 用于做一些销毁、回收等类型的操作
         */
        void onDestroy();
    }
    public interface IBaseView<T> {
        /**
         * 显示提示消息
         *
         * @param msg
         */
        void toast(String msg);
    
        /**
         * 显示进度
         *
         * @param progress
         */
        void showProgress(int progress);
    
        /**
         * 隐藏进度
         */
        void hideProgress();
    
        /**
         * 请求成功
         * @param data
         */
        void loadDataSuccess(T data);
    
        /**
         * 请求错误
         * @param throwable
         */
        void loadDataError(Throwable throwable);
    
        /**
         * 开始加载
         */
        void startLoading();
    }

    好了 前面的基本工作都做完了 界面该实现真正的功能了
    我实现的是登陆功能
    我先实现登陆功能的逻辑 也就是先实现LoginModel
    首先登陆之前需要初始化 登陆请求接口 然后才是传递参数实现登陆 基本上就这个两点 那么转换成代码就是

    public class LoginModel extends BaseModel {
    
    
        private Context context;
        private LoginApi loginApi;
    
        /**
         * 初始化登陆请求接口
         * @param context
         */
        public LoginModel(Context context) {
            this.context = context;
            loginApi = retrofitManager.getLoginApi();
        }
    
        /**
         * 实现登陆
         * @param userName
         * @param pwd
         * @param observer
         */
        public void login(String userName, String pwd, final Observer observer) {
            String userInfo = "{\"name\":\"" + userName + "\"," +
                    "\"pwd\":\"" + pwd + "\"" +
                    "}";
            loginApi.login("login",userInfo).subscribeOn(Schedulers.newThread()).observeOn(AndroidSchedulers.mainThread()).subscribe(new Observer<Response<User>>() {
                @Override
                public void onCompleted() {
    
                }
    
                @Override
                public void onError(Throwable e) {
                    observer.onError(e);
                }
    
                @Override
                public void onNext(final Response<User> jsonObjectResponse) {
                    /**
                     * 延迟一下再返回结果 这样能看到 登陆的动画 可以不用延时直接返回
                     */
                     new Handler().postDelayed(new Runnable() {
                        @Override
                        public void run() {
                            if (jsonObjectResponse.code() == 200) {
                                observer.onNext(jsonObjectResponse.body());
                                observer.onCompleted();
                            } else {
                                onError(new Throwable("网络错误"));
                            }
                        }
                    },3000);
    
                }
            });
        }
    }

    然后实现VIEW
    写之前根据逻辑来分析需要哪些方法 登陆的话 需要 用户名和用户密码 这两个是必须的 接下来可以有 清除密码 清除用户名 等 不必须的方法可以用有可以没有 根据上面的分析可以写出以下代码

    /**
    * 封装好登陆view方法 让activity(也就是view)去实现它
    */
    public interface ILoginView extends IBaseView<User>{
    
        /**
         * 获取用户名
         * @return
         */
        String getUserName();
    
        /**
         * 获取用户密码
         * @return
         */
        String getPwd();
    
        /**
         * 清除用户名
         */
        void clearnUserName();
    
        /**
         * 清除用户密码
         */
        void clearnPwd();
    
    }
    

    接下来就是presenter实现调度了
    presenter 首先它需要从view(也就是activity)成拿到数据 然后再把数据交给model(也就是业务层去处理) model处理完业务逻辑 返回数据给 presenter 再有presenter 反馈给 view(activity) 根据这个思路 我们需要定义一个LoginModel 和一个IView

    public class LoginPresenter extends BasePresenter<ILoginView,User> {
    
    
        private LoginModel loginModel;
        private Context context;
    
        /**
         * 构造方法
         *
         * @param view 具体业务的接口对象
         */
        public LoginPresenter(ILoginView view,Context context) {
            super(view);
            this.context = context;
            loginModel = new LoginModel(context);
        }
    
        /**
         * 登陆
         * @param userName
         * @param pwd
         */
        public void login(String userName,String pwd) {
            onResume();
            iView.startLoading();
            iView.showProgress(0);
            loginModel.login(userName,pwd,this);
        }
    }

    注意此处为什么没有定义IView 是因为BasePresenter里已经定义好了一个IBaseView(可以看看上面的 BasePresenter的定义)
    子类继承父类的public IBaseView 自然就存在了 当LoginPresenter被初始化时就会初始化IView

    最后一步
    真正的view(activity)
    初始化activity上的控件 控件有 Edittext userName; Edittext pwd; Button login;
    view 和presenter进行交互 view把数据交给presenter
    [ 然后presenter把数据交给model,model处理数据(登陆服务器 登陆成分返回数据给presenter presenter 反馈给activity 登陆失败model返回给presenter ,presenter反馈给activity) ] 中括号里的不走 由前面我们定义好的逻辑自动实现

    public class LoginActivity extends BaseActivity implements ILoginView {
    
        @BindView(R.id.editText)
        EditText userName;
        @BindView(R.id.editText2)
        EditText pwd;
        @BindView(R.id.button)
        Button login;
        @BindView(R.id.loginNotice)
        RelativeLayout loginNotice;
        private LoginPresenter loginPresenter;
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState,R.layout.activity_login);
            loginPresenter = new LoginPresenter(this, this);
        }
    
    
        @Override
        public String getUserName() {
            return userName.getText().toString();
        }
    
        @Override
        public String getPwd() {
            return pwd.getText().toString();
        }
    
        @Override
        public void clearnUserName() {
    
        }
    
        @Override
        public void clearnPwd() {
    
        }
    
        @Override
        public void toast(String msg) {
    
        }
    
        @Override
        public void showProgress(int progress) {
            loginNotice.setVisibility(View.VISIBLE);
        }
    
        @Override
        public void hideProgress() {
            loginNotice.setVisibility(View.GONE);
        }
    
        /**
         * 请求成功
         *
         * @param data
         */
        @Override
        public void loadDataSuccess(User data) {
            if (data.isMessageStatus()) {
                Toast.makeText(LoginActivity.this, "登陆成功!", Toast.LENGTH_SHORT).show();
            } else {
                Toast.makeText(LoginActivity.this, "登陆失败!" + data.getMessage(), Toast.LENGTH_SHORT).show();
            }
    
        }
    
        /**
         * 请求错误
         *
         * @param throwable
         */
        @Override
        public void loadDataError(Throwable throwable) {
            throwable.printStackTrace();
            Toast.makeText(LoginActivity.this, "登陆失败!", Toast.LENGTH_SHORT).show();
        }
    
        /**
         * 开始加载
         */
        @Override
        public void startLoading() {
            Log.e(TAG,"开始登陆");
        }
    
        @OnClick(R.id.button)
        public void onClick() {
            loginPresenter.login(getUserName(), getPwd());
        }
    }

    好了一个mvp的登陆功能就完成 实现更多的功能只需要 按照上面的步骤实现 model 实现IView 实现 presenter 再通过view(activity) 进行调度就行了
    本文采用的是 butterknife 有更好的 dragger2 和databinding 深度解耦

    此篇文章 我只是从我学习的角度写出来的 有写错的地方请给出意见 如果是大神请飘过

    展开全文
  • MVP模式

    2018-06-15 00:27:02
    菜鸡豪一直以来写代码都非常的混乱,写到哪里想到哪里,发现差什么就写什么,所以整个项目写下来发现代码的重用非常的严重,所以我觉得学习一个框架,改善代码风格显得迫在眉睫了,就此我开始学习MVP模式。 MVP模式...
  • MVP架构

    2020-09-11 00:59:39
    嵌入式产品UI界面设计-MVP架构 M : model P : presenter V : view M保存数据 或 获取数据,设计为单例模式,数据一直存在,不会被析构。 P是处理业务逻辑,V是UI界面。V不会直接和M交流,而是通过P。 P和V...
  • MVP框架封装

    2018-11-06 08:42:17
    封装的MVP框架,还在一直改进中,当然有看到不当之处也欢迎能够指出问题,如果你没有资源分,可去github下载地址:https://github.com/gitwangyang/MVP
  • Android MVP 实例

    2016-04-15 08:07:41
    本文是「吴小龙同学」投稿,MVP其实一直被提及比较多,我的读者们可能有一些人不理解,其实再多的理论比不上一次简单的实践,这篇文章就以一个简单的请求天气功能,来演示Andr...
  • 目录 引言 为什么用MVP架构 MVP理论知识 乞丐版MVP架构模式的代码实现 MVP中的代码复用场景 平民版MVP架构 - base层顶级父类 Fragment怎么办 时尚版MVP架构 - Model...看完之后一直懵懵懂懂的,总觉有几处关...
  • MVP这个架构一直是Android开发社区讨论的焦点,每个人都有自己的分析理解众说纷纭。直到GitHub上Google官方发布用MVP架构搭建的项目。感觉是时候分析了。 MVP架构简介 MVP架构简介  对于一个应用而...
  • MVP模式初探

    2017-08-15 21:01:53
    之前一直听说MVP的好处多多,也看过相关资料,但是没有仔细研究过,今天看了看面试题,发现竟然让用MVP模式实现登录功能,于是抓紧研究了下。 关于MVP的介绍以及与MVC的区别网上有相当多的资料可以参考,这里推荐...
  • 入门级 MVP

    2017-07-26 13:50:14
    前段时间在腾讯课堂看了一个关于 MVP 模式讲解的直播,整体看下来感觉还是相当适用于一直对于 MVP 处于理论了解的新手。 这里对于理论知识我就不多说废话了,相信大多数朋友都有所了解。 那么我们就直接开始进行贴...
  • 聊聊Google官方MVP

    千次阅读 2016-09-05 14:26:00
    MVP这个话题也是讨论了很久很久的,热度一直不减,甚至google官方也很认可MVP在Android中的地位。 而我最近一个项目也参照google的那个MVP架构,发现并怎么好用。果然架构这种东西需要自己思考,根据不同的项目进行...
  • Android MVP架构搭建

    2019-06-11 15:46:18
    目录 引言 为什么用MVP架构 MVP理论知识 乞丐版MVP架构模式的代码实现 MVP中的代码复用场景 平民版MVP架构 - base层顶级父类 Fragment怎么办 时尚版MVP架构 - Model...看完之后一直懵懵懂懂的,总觉有几处关...
  • MVP邹鋆弢邹鋆弢,Visual C++MVP,曾到美国芝加哥大学和欧洲都柏林大学进行进修,这一段出国进修的经历改变了他很多认识。与国外相比,不仅仅是教学要求的差别,更多的是在教学管理、教学理念上的差距。在那个时候,...
  • MVP模式总结

    2019-05-08 10:26:05
    本来一直使用mvc的,也使用了一段时间的mvp,因为接手的项目不大,基本上用mvc就成,但是后面接手的别人的项目,用的mvc,发现整个项目面目全非,有种无从下手的感觉,遂决定抛弃mvc,全都改用mvp,至少给继任者一点...
  • MVP模式简单介绍

    2016-12-30 15:57:42
    今天琢磨MVP模式,看着网上那么多介绍的文章看的我头痛。 这会突然恍然大悟:原来早在很久以前我就一直在项目中用,只是我不知道而已!!! 2.逻辑思维 这个东西还是很简单的,这里做个记录,以备以后用到的时候能...
  • 之前一直很钦佩那些MVP获奖者,想着自己有一天也能拿到该多好,就在10月1日邮箱收到了微软的邮件,当选了2016年10月份的MVP。今天主要分享一下获奖的喜悦也分享一下如何获得MVP奖项。 什么是微软MVPMVP(Most ...
  • 推荐一个Gradle的学习系列,Gradle相关的知识一直很匮乏,难得发现一个不错的系列: http://www.cnblogs.com/davenkin/p/gradle-learning-1.html 本文作者 本文由蚂蚁提供。 蚂蚁的博客地址: ...
  • 关于presenter一直持有Activity对象导致的内存泄漏问题 只要用过mvp这个问题可能很多人都知道。写mvp的时候,presenter会持有view,如果presenter有后台异步的长时间的动作,比如网络请求,这时如果返回退出了...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 867
精华内容 346
关键字:

一直mvp