首先说明:这篇博客参考资料 鸿神的MVP博客 内容上有着很大的相似性。

如果是比较初级的读者,只是开始接触MVP,我建议可以看一下我的另外两个博客,个人认为对理解MVP有帮助而写的比较不美观的博客

一、 MVP前奏(一)接口

二、MVP前奏(二)MVC在Android的小短腿

我觉得编程这个东西,开始不一定非要深入,只要先上手使用就好了,用着用着,认识会随着熟练度增长,有了一定的熟练度后再去深入研究问题,也能事半功倍了。


android mvp架构 android mvp框架例子_mvp




做个说明:ui包的activity几乎都继承了view中定义的接口,ui是属于V的;

biz包定义了接口和实际操作,OnloginListener是登录的回调接口,属于P层;

M层只有一个model;

至于base包,请忽略。




开始正文:

这个demo使用MVP实现了两点小功能

1.realm存一个用户信息;

2.Rxjava模拟登录。

最简单的M开始,只定义了用户模型User,因为要使用realm存储,所以User继承了RealmObject:

<span style="font-size:18px;">package com.hcsw.newtecset.model;

import io.realm.RealmObject;

/**
 * Created by Administrator on 2016/3/9.
 */
public class User extends RealmObject{
    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;
    }
}</span>

我的一篇关于Realm的简单博客



接下来是V层

由IRealmView定义RealmActivity需要实现的功能,将来IRealmView会作为RealmActivity的代表参与操作:

<span style="font-size:18px;">package com.hcsw.newtecset.view;

/**
 * Created by Administrator on 2016/3/10.
 */
public interface IRealmView {
    String getUserName();//获取输入的用户名
    String getPassword();//获取输入的密码
    void toLoginActivity();//添加成功后的操作
    void showTip(String tip);//关闭Realm
}
</span>

关于BaseActivity的代码稍后贴出。

<span style="font-size:18px;">package com.hcsw.newtecset.ui;

import android.content.Intent;
import android.view.View;
import android.widget.EditText;
import android.widget.LinearLayout;
import android.widget.Toast;

import com.hcsw.newtecset.R;
import com.hcsw.newtecset.base.BaseActivity;
import com.hcsw.newtecset.presenter.RealmPresenter;
import com.hcsw.newtecset.view.IRealmView;


public class RealmActivity extends BaseActivity implements IRealmView {

    private EditText edtName,edtPwd;
    private RealmPresenter presenter;
    @Override
    public int getContentViewId() {
        return R.layout.activity_main;
    }

    @Override
    public int getTitleBarId() {
        return -1;
    }

    @Override
    protected String getActivityTitleTxt() {
        return "设置用户信息";
    }

    @Override
    protected void initIntent() {
    }

    @Override
    public void initComponents() {
        setTitleBarAction();
        findViewById(R.id.title_bar_left_area).setVisibility(View.INVISIBLE);
        presenter = new RealmPresenter(this);
        edtName = (EditText) findViewById(R.id.edt_username);
        edtPwd = (EditText) findViewById(R.id.edt_password);
        findViewById(R.id.btn_save).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                presenter.insertUser();
            }
        });
    }


    @Override
    public void loadData() {

    }

    @Override
    public boolean isResumeLoad() {
        return false;
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        presenter.closeRealm();
    }

    @Override
    public String getUserName() {
        return edtName.getText().toString();
    }

    @Override
    public String getPassword() {
        return edtPwd.getText().toString();
    }

    @Override
    public void toLoginActivity() {
        startActivity(new Intent(this,LoginActivity.class));
    }

    @Override
    public void showTip(String tip) {
        Toast.makeText(this,tip,Toast.LENGTH_LONG).show();
    }
}
</span>


由ILoginView定义LoginActivity需要实现的功能,将来ILoginView会作为LoginActivity的代表参与操作:



package com.hcsw.newtecset.view;

/**
 * Created by Administrator on 2016/3/9.
 */
public interface IUserLoginView {

    String getUserName();//获取账号
    String getPassword();//获取密码
    void cleanUserName();//清除账号
    void cleanPassword();//清除密码,两个可以合并为一个清楚方法
    void showLoading();//显示表示“登录中”的progressbar
    void hideLoading();//隐藏表示“登录中”的progressbar
    void onLoginSuccess();//处理登录成功的方法
    void onLoginFailed();//处理登录失败的方法
}



package com.hcsw.newtecset.ui;

import android.app.ProgressDialog;
import android.content.Intent;
import android.view.View;
import android.widget.EditText;
import android.widget.ProgressBar;
import android.widget.Toast;

import com.hcsw.newtecset.R;
import com.hcsw.newtecset.base.BaseActivity;
import com.hcsw.newtecset.presenter.LoginPresenter;
import com.hcsw.newtecset.view.IUserLoginView;

/**
 * Created by Administrator on 2016/3/9.
 */
public class LoginActivity extends BaseActivity implements IUserLoginView{

    ProgressBar pLoading;
    LoginPresenter presenter;
    private EditText edtName,edtPwd;
    @Override
    public int getContentViewId() {
        return R.layout.activity_login;
    }

    @Override
    public int getTitleBarId() {
        return -1;
    }

    @Override
    protected String getActivityTitleTxt() {
        return "登录";
    }

    @Override
    protected void initIntent() {

    }

    @Override
    public void initComponents() {
        presenter = new LoginPresenter(this);
        setTitleBarAction();
        findViewById(R.id.title_bar_left_area).setVisibility(View.INVISIBLE);
        edtName = (EditText) findViewById(R.id.edt_username);
        edtPwd = (EditText) findViewById(R.id.edt_password);
        pLoading = (ProgressBar) findViewById(R.id.pb_loading);
        findViewById(R.id.btn_login).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                presenter.login();
            }
        });
        findViewById(R.id.btn_clean).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                presenter.clear();
            }
        });
    }

    @Override
    public void loadData() {

    }

    @Override
    public boolean isResumeLoad() {
        return false;
    }

    @Override
    public String getUserName() {
        return edtName.getText().toString();
    }

    @Override
    public String getPassword() {
        return edtPwd.getText().toString();
    }

    @Override
    public void cleanUserName() {
        edtName.setText("");
    }

    @Override
    public void cleanPassword() {
        edtPwd.setText("");
    }

    @Override
    public void showLoading() {
        pLoading.setVisibility(View.VISIBLE);
    }

    @Override
    public void hideLoading() {
        pLoading.setVisibility(View.GONE);
    }

    @Override
    public void onLoginSuccess() {
        Toast.makeText(this,"Success",Toast.LENGTH_LONG).show();
        startActivity(new Intent(this,SecondActivity.class));
    }

    @Override
    public void onLoginFailed() {
        Toast.makeText(this,"failed",Toast.LENGTH_LONG).show();
    }
}





再接下来是P层

为Realm和登录分别创建接口IRealmBiz和IUserBiz,定义需要实现的内容方法。

IRealmBiz定义了增、删、改、查和关闭数据库五个方法,由RealmBiz继承实现:

<span style="font-size:18px;">package com.hcsw.newtecset.biz;

import com.hcsw.newtecset.model.User;

/**
 * Created by Administrator on 2016/3/10.
 */
public interface IRealmBiz {
    void insertRealm(String name,String pwd);
    User selectRealm();
    void deleteRealm(User user);
    void uploadRealm(User user);
    void closeRealm();
}</span>
<span style="font-size:18px;">package com.hcsw.newtecset.biz;

import com.hcsw.newtecset.model.User;

import io.realm.Realm;

/**
 * Created by Administrator on 2016/3/10.
 */
public class RealmBiz implements IRealmBiz{
    private Realm realm;
    public RealmBiz(){
        realm = Realm.getDefaultInstance();
    }
    @Override
    public void insertRealm(String name,String pwd) {
        realm.beginTransaction();
        User user = realm.createObject(User.class);
        user.setUserName(name);
        user.setPassword(pwd);
        realm.commitTransaction();
    }

    @Override
    public User selectRealm() {
        realm.beginTransaction();
        User user = realm.where(User.class).findFirst();
        realm.commitTransaction();
        return user;
    }

    @Override
    public void deleteRealm(User user) {
        realm.beginTransaction();

        realm.commitTransaction();
    }

    @Override
    public void uploadRealm(User user) {
        realm.beginTransaction();

        realm.commitTransaction();
    }

    @Override
    public void closeRealm() {
        realm.beginTransaction();
        realm.where(User.class).findAll().clear();
        realm.commitTransaction();
        realm.close();
    }
}
</span>

关于realm = Realm.getDefaultInstance()这句,请参照后面贴出的MyApplication代码;

IUserBiz只定义了一个login方法,并设置了三个参数,由UserBiz继承实现,OnloginListener是作为登录结果监听的接口,只有成功和失败两个方法:

<span style="font-size:18px;">package com.hcsw.newtecset.biz;

/**
 * Created by Administrator on 2016/3/9.
 */
public interface IUserBiz {
    void login(String name,String password,OnloginListener listener);
}</span>


这里用到了一些Rxjava的语法。

<span style="font-size:18px;">package com.hcsw.newtecset.biz;

import com.hcsw.newtecset.model.User;

import java.util.concurrent.TimeUnit;

import io.realm.Realm;
import rx.Observable;
import rx.Observable.OnSubscribe;
import rx.Observer;
import rx.Subscriber;
import rx.android.schedulers.AndroidSchedulers;
import rx.functions.Action1;
import rx.functions.Func1;
import rx.schedulers.Schedulers;

/**
 * Created by Administrator on 2016/3/9.
 */
public class UserBiz implements IUserBiz{
    @Override
    public void login(final String name, final String password, final OnloginListener listener) {

        Observable
                .create(new OnSubscribe<Boolean>() {
                    @Override
                    public void call(Subscriber<? super Boolean> subscriber) {
                        Realm realm = Realm.getDefaultInstance();
                        User user = realm.where(User.class).findFirst();
                        String u = user.getUserName();
                        String p =user.getPassword();
                        boolean b = u.equals(name)&&p.equals(password);
                        subscriber.onNext(b);
                    }
                })
                .subscribeOn(Schedulers.io())
                .delay(2, TimeUnit.SECONDS)
                .observeOn(AndroidSchedulers.mainThread())
                .subscribe(new Action1<Boolean>() {
                    @Override
                    public void call(Boolean aBoolean) {
                        if(aBoolean){
                            User user = new User();
                            user.setUserName(name);
                            user.setPassword(password);
                            listener.loginSuccess(user);
                        }else{
                            listener.loginFailed();
                        }
                    }
                });

    }
}
</span>
<span style="font-size:18px;">package com.hcsw.newtecset.biz;

import com.hcsw.newtecset.model.User;

/**
 * Created by Administrator on 2016/3/9.
 */
public interface OnloginListener {
    void loginSuccess(User user);
    void loginFailed();
}
</span>

代码关联处理关系整合的关键类,接下来的类就如同是交通枢纽,将整个结构联系起来:

<span style="font-size:18px;">package com.hcsw.newtecset.presenter;

import com.hcsw.newtecset.biz.RealmBiz;
import com.hcsw.newtecset.view.IRealmView;

/**
 * Created by Administrator on 2016/3/10.
 */
public class RealmPresenter {
    private RealmBiz realmBiz;
    //代理处理RealmActivity中的方法
    private IRealmView iRealmActivity;
    public RealmPresenter(IRealmView iActivity){
        realmBiz = new RealmBiz();
        iRealmActivity = iActivity;
    }

    public void insertUser(){
        //参数通过iRealmActivity获取RealmActivity中的实际值
        realmBiz.insertRealm(iRealmActivity.getUserName(),iRealmActivity.getPassword());
        //结果返回RealmActivity处理
        iRealmActivity.showTip("插入数据完成");
        iRealmActivity.toLoginActivity();
    }
    //关闭数据库
    public void closeRealm(){
        realmBiz.closeRealm();
    }

}
</span>





<span style="font-size:18px;">package com.hcsw.newtecset.presenter;

import com.hcsw.newtecset.biz.OnloginListener;
import com.hcsw.newtecset.biz.UserBiz;
import com.hcsw.newtecset.model.User;
import com.hcsw.newtecset.view.IUserLoginView;

import java.util.concurrent.TimeUnit;

import rx.Observable;
import rx.Subscriber;
import rx.android.schedulers.AndroidSchedulers;
import rx.functions.Action1;

/**
 * Created by Administrator on 2016/3/9.
 */
public class LoginPresenter {
    UserBiz userBiz;
    //LoginActivity的代表
    IUserLoginView userLoginView;
    public LoginPresenter(IUserLoginView loginView){
        this.userLoginView = loginView;
        userBiz = new UserBiz();
    }

    public void login(){
        userLoginView.showLoading();
        
        userBiz.login(userLoginView.getUserName(), userLoginView.getPassword(), new OnloginListener() {
            @Override
            public void loginSuccess(final User user) {
                //Rxjava的语法,在收到成功后执行
                Observable
                        .create(new Observable.OnSubscribe<User>() {
                            @Override
                            public void call(Subscriber<? super User> subscriber) {
                                subscriber.onNext(user);
                            }
                        })
                        .observeOn(AndroidSchedulers.mainThread())
                        .subscribe(new Action1<User>() {
                            @Override
                            public void call(User user) {
                                userLoginView.hideLoading();
                                userLoginView.onLoginSuccess();
                            }
                        });
            }

            @Override
            public void loginFailed() {
                Observable
                        .create(new Observable.OnSubscribe<String>() {
                    @Override
                    public void call(Subscriber<? super String> subscriber) {
                        subscriber.onNext("");
                    }
                })
                        .observeOn(AndroidSchedulers.mainThread())
                        .subscribe(new Action1<String>() {
                            @Override
                            public void call(String user) {
                                userLoginView.hideLoading();
                                userLoginView.onLoginFailed();
                            }
                        });
            }
        });
    }

    public void clear(){
        userLoginView.cleanUserName();
        userLoginView.cleanPassword();
    }
}
</span>

上一张MVC和MVP的区别图(图片原文):

android mvp架构 android mvp框架例子_realm_02

我的手工图:

android mvp架构 android mvp框架例子_android_03



个人总结:M层没有变化;V层增加了接口,在activity继承的时候,通过接口的特性,使接口可以代理的操作Activity中的实际内容交给P层的Presenter类处理;而P层预定好处理业务,在Presenter类中将业务和数据结合,然后将结构返回V层,实现V和M的分离和不再高耦合。

个人觉得这里最重要的就是通过接口的实现的各种调用。理解接口很重要
demo下载地址