欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

【Android】MVP模式

程序员文章站 2022-03-09 16:15:43
...

本文内容部分参考自:


MVP模式

在Android项目中,Activity和Fragment占据了大部分的开发工作。MVP模式就是为了专门优化Activty和Fragment的代码而产生的。
按照MVC的分层,Activity和Fragment应该属于View层,用于展示UI界面,以及接受用户的输入,此外还要承担一些生命周期的工作。Activity在Android开发中从当非常重要的角色,特别是生命周期的功能,所以开发的时候我们经常把一些业务逻辑直接写在Activity中,这会非常直观,代价是Activity会越来越臃肿,并且如果是一些通用的业务逻辑,写在Activity中就意味着这个逻辑不能复用。因此Activity不仅承担了View的角色,还承担了一部分Controller角色,这样一来V和C就耦合在一起了,虽然这样写很方便,但是业务调整的话,维护就十分困难。因此有必要在Activity中,把View和Controller抽离开来,而这就是MVP模式的工作。
【Android】MVP模式

MVP结构

MVP模式核心思想:

   MVP把Activity中的UI逻辑抽象成View接口,把业务逻辑抽象成presenter接口,Model类还是Model。


MVP模式作用

1、分离了视图逻辑和业务逻辑,减低了耦合

2、Activity只处理生命周期的任务,代码变得更加简洁

3、视图逻辑和业务逻辑分别抽象到View和Presenter接口中,提高代码的可阅读性。

4、Presenter被抽象成接口,可以具有多种实现方式,便于单元测试

5、把业务逻辑抽到了Presenter中去,避免后台线程引用着Activity导致Activity的资源无法被系统回收而引起内存泄漏和OOM.


MVP的使用

【Android】MVP模式

1、创建Presenter接口,处理所有业务逻辑的接口。

2、创建View接口,所有视图逻辑的接口,实现类是当前的activity/fragment

3、activity中包含了一个presenter接口,presenter实现中包含了一个view接口,并且依赖了Model。Fragment/Activity只保留了对presenter接口的调用。


MVP案例

工程目录

【Android】MVP模式

presenter和view的接口:

public interface LoginContract {

    interface View extends BaseView<Presenter>{
        void finish();
        void showRegister();
        void showAllStuff(String userId);
        void showToast(String message);
    }

    interface Presenter extends BasePresenter{
        void login(@NonNull String email, @NonNull String password);
        void showRegister();
    }
}

Activity:

public class LoginActivity extends AppCompatActivity {

    private LoginPresenter mLoginPresenter;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_login);
        // Set up the toolbar.
        Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
        setSupportActionBar(toolbar);
        getSupportActionBar().setDisplayShowTitleEnabled(false);

        LoginFragment loginFragment = (LoginFragment) getSupportFragmentManager().findFragmentById(R.id.contentFrame);

        if(loginFragment == null) {
            loginFragment = LoginFragment.newInstance();
            ActivityUtils.addFragmentToActivity(getSupportFragmentManager(), loginFragment, R.id.contentFrame);
        }
        AppExecutors appExecutors = new AppExecutors();
        mLoginPresenter = new LoginPresenter(UsersRepository.getInstance(UsersLocalDataSource.getInstance(appExecutors), UsersRemoteDataSource.getInstance(appExecutors),appExecutors),loginFragment);
    }
}

Fragment:

public class LoginFragment extends Fragment implements LoginContract.View{
    private static final String TAG = LoginFragment.class.getSimpleName();
    private EditText mEmailEt;
    private EditText mPasswordEt;
    private Button mLoginBt;
    private TextView mToRegisterTv;

    private LoginContract.Presenter mPresenter;

    public static LoginFragment newInstance(){
        LoginFragment fragment = new LoginFragment();
        return fragment;
    }

    @Override
    public void onResume() {
        super.onResume();
       // mPresenter.start();
    }

    @Nullable
    @Override
    public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, Bundle savedInstanceState) {
        View root = inflater.inflate(R.layout.fragment_login, container, false);
        mEmailEt = root.findViewById(R.id.et_login_email);
        mPasswordEt = root.findViewById(R.id.et_login_password);
        mLoginBt = root.findViewById(R.id.bt_login_login);
        mToRegisterTv = root.findViewById(R.id.tv_login_toregister);

        mLoginBt.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                try{
                    mPresenter.login(mEmailEt.getText().toString(), mPasswordEt.getText().toString());
                }catch (Exception e){
                    e.printStackTrace();
                }
            }
        });
        mToRegisterTv.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                mPresenter.showRegister();
            }
        });
        return root;
    }

    @Override
    public void setPresenter(LoginContract.Presenter presenter) {
        if (presenter != null) {
            mPresenter = presenter;
        }
    }

    @Override
    public void finish() {
        getActivity().finish();
    }

    @Override
    public void showRegister() {
        try{
            Intent intent = new Intent(getContext(), RegisterActivity.class);
            startActivity(intent);
        }catch (Exception e){
            e.printStackTrace();
        }
    }

    @Override
    public void showAllStuff(String userId) {
        try{
            Intent intent = new Intent(getContext(), AllStuffActivity.class);
            intent.putExtra("USERID", userId);
            startActivity(intent);
            getActivity().finish();
        }catch (Exception e){
            e.printStackTrace();
        }
    }

    @Override
    public void showToast(final String message) {
        Log.d(TAG, "showToast: " + message);
        getActivity().runOnUiThread(new Runnable() {
            @Override
            public void run() {
                Toast.makeText(getContext(), message, Toast.LENGTH_SHORT).show();
            }
        });
    }
}

presenter:

public class LoginPresenter implements LoginContract.Presenter {

    private static final String TAG = LoginPresenter.class.getSimpleName();
    private final UsersRepository mUserRepository;

    private final LoginContract.View mLoginView;

    public LoginPresenter(@NonNull UsersRepository usersRepository, @NonNull LoginContract.View loginView) {
        mUserRepository = usersRepository;
        mLoginView = loginView;

        mLoginView.setPresenter(this);
    }

    @Override
    public void start() {

    }

    @Override
    public void login(@NonNull String email, @NonNull String password) {
        if (email.isEmpty() && password.isEmpty()) {
            mLoginView.showToast("请输入邮箱和密码");
        } else if (email.isEmpty()) {
            mLoginView.showToast("请输入邮箱");
        } else if (password.isEmpty()) {
            mLoginView.showToast("请输入密码");
        } else {
            mUserRepository.login(email, MD5Util.encrypt(password), new UsersDataSource.GetUserCallBack() {
                @Override
                public void onUserLoaded(final User user) {
                    Log.d(TAG, "onUserLoaded: 登录成功");
                    //登录成功,加载userID信息
                    final SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(MyApplication.getContext());
                    final SharedPreferences.Editor editor = sharedPreferences.edit();
                    editor.putString("USERID", user.getUserId()).apply();
                    //登录成功,调转到所有材料页面
                    mLoginView.showAllStuff(user.getUserId());
                }

                @Override
                public void onDataNotAvailable(String message) {
                    Log.d(TAG, "onDataNotAvailable: 登录失败" + message);
                    mLoginView.showToast(message);
                }
            });
        }
    }

    @Override
    public void showRegister() {
        mLoginView.showRegister();
    }
}

数据源采用了接口和回调的方式。

public interface UsersDataSource {
    interface LoadUserCallBack {
        void onUserLoaded(User user);

        void onDataNotAvailable();
    }

    interface GetUserCallBack {
        void onUserLoaded(User user);

        void onDataNotAvailable(String message);
    }

    interface SendRequestCallBack{
        void onRequestSuccess(String message);
        void onRequestFail(String message);
    }


    void getUser(@NonNull String userId, @NonNull GetUserCallBack callBack);

    void register(@NonNull User user, @NonNull String code, @NonNull SendRequestCallBack callBack);

    void updateUser(@NonNull User user);

    void deleteUser(@NonNull String userId);

    void login(@NonNull String email, @NonNull String password, @NonNull GetUserCallBack callBack);

    void sendCode(@NonNull String email, @NonNull SendRequestCallBack callBack);
}