话说第一次接触MVP,还是这篇文章,后来自己也写过,但是却发现写的不伦不类,自己都不知道MVP的优点了,感觉全是缺点。直到看到了这个demo,才改变了我的看法。
先看看大神是怎么总结的?
MVP 是 MVC 的变种,其实是一种升级。要说 MVP 就要说说 MVC,在 MVC 中 Activity 其实是 View层级,但是通常在使用中 Activity即是View也是Controller,并没有将 View层 和 Controller层 进行分离, 耦合度大大提高,非常不利于项目的管理。这时候 MVP 就应运而生了。
MVP分为三层
1. Model
2. View
3. Presenter
MVP模式的核心思想
MVP 把 Activity 中的 UI逻辑 抽象成 View接口,把 业务逻辑 抽象成 Presenter接口,Model类 还是原来的 Model。在 MVP 模式中 Activity 的功能就是响应生命周期和显示界面,具体其他的工作都丢到了 Presenter层 中进行完成,Presenter 其实是 Model层 和 View层 的桥梁。
从上图可以看出:
1. 创建 IPresenter 接口,把所有业务逻辑的接口都放在这里,并创建它的实现
PresenterCompl(在这里可以方便地查看业务功能,由于接口可以有多种实现所以也方便写单元测试)。
2. 创建 IView 接口,把所有视图逻辑的接口都放在这里,其实现类是当前的 Activity/Fragment。
3. 由UML图可以看出,Activity 里包含了一个 IPresenter,而 PresenterCompl 里又包含了一个 IView
并且依赖了 Model。Activity 里只保留对 IPresenter 的调用,其它工作全部留到 PresenterCompl
中实现。
4. Model 并不是必须有的,但是一定会有 View 和 Presenter。
通过上面的介绍,MVP 的主要特点就是把 Activity 里的许多逻辑都抽离到 View 和 Presenter 接口中去,并由具体的实现类来完成。这种写法多了许多 IView 和 IPresenter 的接口。
OK,接下来看看实际项目中的应用,首先看看IView的实现:
package com.itheima.leon.qqdemo.view;
/**
* 创建者: Leon
* 创建时间: 2016/10/16 19:04
* 描述: TODO
*/
public interface SplashView {
//没有登录,进入登录界面
void onNotLogin();
//已经登录过,进入主界面
void onLoggedIn();
}
也就是View需要实现的业务是登录或者进入主界面,但是什么时间如何进去,是由IPresenter来决定的!那么IPresenter是如何来决定的呢?
package com.itheima.leon.qqdemo.presenter;
/**
* 创建者: Leon
* 创建时间: 2016/10/16 19:13
* 描述: TODO
*/
public interface SplashPresenter {
public static final String TAG = "SplashPresenter";
//检查登录状态
void checkLoginStatus();
}
IPresenter需要实现的就是在检查登录状态中决定是进入主界面或者登录界面的,那么接下来是去Activity里面实现这些接口吗?其实是单独写了一个IPresenter的实现类,好处后面会提到,先看实现代码:
package com.itheima.leon.qqdemo.presenter.impl;
import com.hyphenate.chat.EMClient;
import com.itheima.leon.qqdemo.presenter.SplashPresenter;
import com.itheima.leon.qqdemo.view.SplashView;
/**
* 创建者: Leon
* 创建时间: 2016/10/16 19:14
* 描述: TODO
*/
public class SplashPresenterImpl implements SplashPresenter {
public static final String TAG = "SplashPresenterImpl";
public SplashView mSplashView;
public SplashPresenterImpl(SplashView splashView) {
mSplashView = splashView;
}
@Override
public void checkLoginStatus() {
if (EMClient.getInstance().isLoggedInBefore() && EMClient.getInstance().isConnected()) {
mSplashView.onLoggedIn();
} else {
mSplashView.onNotLogin();
}
}
}
OK,MVP中的Moudle我们这里暂时用不上,IPresenter写好并实现了,那么IView我们也去实现吧!
package com.itheima.leon.qqdemo.ui.activity;
import com.itheima.leon.qqdemo.R;
import com.itheima.leon.qqdemo.presenter.SplashPresenter;
import com.itheima.leon.qqdemo.presenter.impl.SplashPresenterImpl;
import com.itheima.leon.qqdemo.view.SplashView;
/**
* 创建者: Leon
* 创建时间: 2016/10/15 22:11
* 描述: TODO
*/
public class SplashActivity extends BaseActivity implements SplashView {
public static final String TAG = "SplashActivity";
private static final int DELAY = 2000;
@Override
public int getLayoutRes() {
return R.layout.activity_splash;
}
@Override
protected void init() {
super.init();
}
@Override
public void onNotLogin() {
postDelay(new Runnable() {
@Override
public void run() {
startActivity(LoginActivity.class);
}
}, DELAY);
}
@Override
public void onLoggedIn() {
startActivity(MainActivity.class);
}
}
上面的代码也实现了IView接口,但是我们还没有将SplashPresenterImpl将二者间连接起来,所以需要修改部分代码:
private SplashPresenter mSplashPresenter;
@Override
protected void init() {
super.init();
mSplashPresenter = new SplashPresenterImpl(this);
mSplashPresenter.checkLoginStatus();
}
这里我们就实现了MVP的实现,也许大家和我开始一样迷惑为什么没有onCreate()等生命周期方法,这是因为BaseActivity已经抽象的实现了这些方法,我们先看看源码,然后明天学习第二个Activity里面的知识点,代码奉上:
package com.itheima.leon.qqdemo.ui.activity;
import android.app.ProgressDialog;
import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
import android.os.Handler;
import android.support.annotation.Nullable;
import android.support.v7.app.AppCompatActivity;
import android.view.inputmethod.InputMethodManager;
import android.widget.Toast;
import butterknife.ButterKnife;
/**
* 创建者: Leon
* 创建时间: 2016/10/15 22:11
* 描述: TODO
*/
public abstract class BaseActivity extends AppCompatActivity {
public static final String TAG = "BaseActivity";
private Handler mHandler = new Handler();
private ProgressDialog mProgressDialog;
private InputMethodManager mInputMethodManager;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(getLayoutRes());
ButterKnife.bind(this);
init();
}
protected void init() {}
public abstract int getLayoutRes();
protected void startActivity(Class activity) {
startActivity(activity, true);
}
protected void startActivity(Class activity, boolean finish) {
Intent intent = new Intent(this, activity);
startActivity(intent);
if (finish) {
finish();
}
}
protected void post(Runnable runnable) {
postDelay(runnable, 0);
}
protected void postDelay(Runnable runnable, long millis) {
mHandler.postDelayed(runnable, millis);
}
protected void showProgress(String msg) {
if (mProgressDialog == null) {
mProgressDialog = new ProgressDialog(this);
mProgressDialog.setCancelable(true);
}
mProgressDialog.setMessage(msg);
mProgressDialog.show();
}
protected void hideProgress() {
if (mProgressDialog != null) {
mProgressDialog.dismiss();
}
}
protected void toast(String msg) {
Toast.makeText(this, msg, Toast.LENGTH_SHORT).show();
}
protected void hideKeyBoard() {
if (mInputMethodManager == null) {
mInputMethodManager = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE);
}
mInputMethodManager.hideSoftInputFromWindow(getCurrentFocus().getWindowToken(), 0);
}
}
以上基于对原作者的尊重,我只是增加了注释,并未对作者等内容进行修改。但是我们既然是学习,也不能只学习不思考,现在假设 leader要求在欢迎页增加动画,那么我们应该来如何实现呢?我认为这个内容属于IView的业务,至于在什么时候来该调用业务就应该由IPresenter来实现,所以我们对IView进行如下扩展:
public interface SplashView {
void onNotLogin();
void onLoggedIn();
void showAnimation();
}
public interface SplashPresenter {
public static final String TAG = "SplashPresenter";
void displayAnimation();
void checkLoginStatus();
}
public class SplashPresenterImpl implements SplashPresenter {
public static final String TAG = "SplashPresenterImpl";
public SplashView mSplashView;
public SplashPresenterImpl(SplashView splashView) {
mSplashView = splashView;
}
@Override
public void displayAnimation() {
mSplashView.showAnimation();
}
@Override
public void checkLoginStatus() {
if (EMClient.getInstance().isLoggedInBefore() && EMClient.getInstance().isConnected()) {
mSplashView.onLoggedIn();
} else {
mSplashView.onNotLogin();
}
}
}
public class SplashActivity extends BaseActivity implements SplashView {
public static final String TAG = "SplashActivity";
private static final int DELAY = 2000;
private SplashPresenter mSplashPresenter;
private SwitchHandler mHandler = new SwitchHandler(this);
@Override
public int getLayoutRes() {
return R.layout.activity_splash;
}
@Override
protected void init() {
super.init();
mSplashPresenter = new SplashPresenterImpl(this);
mSplashPresenter.displayAnimation();
}
@Override
public void onNotLogin() {
startActivity(LoginActivity.class)
}
@Override
public void onLoggedIn() {
startActivity(MainActivity.class);
}
@Override
public void showAnimation() {
//activity切换的淡入淡出效果
overridePendingTransition(android.R.anim.fade_in, android.R.anim.fade_out);
mHandler.sendEmptyMessageDelayed(1, 1000);
}
private class SwitchHandler extends Handler {
private WeakReference<SplashActivity> mWeakReference;
SwitchHandler(SplashActivity activity) {
mWeakReference = new WeakReference<SplashActivity>(activity);
}
@Override
public void handleMessage(Message msg) {
SplashActivity activity = mWeakReference.get();
if (activity != null) {
mSplashPresenter.checkLoginStatus();
}
}
}
}
参考文章:
http://chuansong.me/n/632293551521
学习的项目地址: