本文内容部分参考自:
- 作者:Kaede Akatsuki
- 项目:Android-MVP-Pattern
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模式的工作。
MVP结构
MVP模式核心思想:
MVP把Activity中的UI逻辑抽象成View接口,把业务逻辑抽象成presenter接口,Model类还是Model。
MVP模式作用
1、分离了视图逻辑和业务逻辑,减低了耦合
2、Activity只处理生命周期的任务,代码变得更加简洁
3、视图逻辑和业务逻辑分别抽象到View和Presenter接口中,提高代码的可阅读性。
4、Presenter被抽象成接口,可以具有多种实现方式,便于单元测试
5、把业务逻辑抽到了Presenter中去,避免后台线程引用着Activity导致Activity的资源无法被系统回收而引起内存泄漏和OOM.
MVP的使用
1、创建Presenter接口,处理所有业务逻辑的接口。
2、创建View接口,所有视图逻辑的接口,实现类是当前的activity/fragment
3、activity中包含了一个presenter接口,presenter实现中包含了一个view接口,并且依赖了Model。Fragment/Activity只保留了对presenter接口的调用。
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);
}