首先说明:这篇博客参考资料 鸿神的MVP博客 内容上有着很大的相似性。
如果是比较初级的读者,只是开始接触MVP,我建议可以看一下我的另外两个博客,个人认为对理解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>
由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的区别图(图片原文):
我的手工图:
个人觉得这里最重要的就是通过接口的实现的各种调用。理解接口很重要
demo下载地址