mvp模式的android项目构建
多日未回博客园,风萧萧兮易水寒。
话说上一次发表随笔已是去年,而且看看当时关于android视频方面的记录也只写了开篇就自动阉割成了太监,究其缘由已不堪回首。
太监终究还是太监,没必要再为它续弦。
笔锋一转,近日有看几本android应用方面优化和技巧方面的书,便觉得又该在此处记录下了。有一本专门讲技巧的书还不错,《50 Android Hacks》:50个android开发诀窍。
其中有一篇是讲MVP(Model-View-Presenter)模式的,也就是模型-视图-主导器(书中翻译为此)。
我大概理解了下该篇内容,以几乎可以和HELLO WORLD 齐名的登陆功能为需求,写了个Demo,以明了其中的缘由,如果以下内容有出错的地方,还望指正,不吝赐教。
【一笔带过】MVP模式与大名鼎鼎的MVC模式似乎就只有最后一个字母的差别,也就在于主导器代替了控制器的地位。所谓主导器,也就是扮演着主导一切(模型与视图)的地位。
【先看看我们熟悉的】模型层,通常是数据结构,我们业务需要用到的一些数据封装。这里会稍稍有些不同,我们同时封装了数据行为,即是该类具有我们实际业务逻辑的要实现的方法。--一小波代码正在袭来...
1 package com.change.mvpdemo.modle; 2 3 /** 4 * MVP模式的m(模型)层。 5 * 登陆状态,登陆的实际逻辑实现它去完成。 6 * @author Change 7 * 8 */ 9 public interface ILoginStatus extends IStatus{ 10 public static final int STATUS_VERIFY_FAIL = -1;//验证失败 11 public static final int STATUS_LOGIN_FAIL = -2;//登陆失败 12 public static final int STATUS_LOGIN_SUCCESS = 0;//登陆成功 13 public static final int STATUS_LOGIN_ING = 1;//登陆中 14 /** 15 * 登陆行为 16 * @param account 17 * @param psw 18 * @return 状态码 19 */ 20 public void login(String account,String psw,IStatusCallback callback); 21 }
【简单说说】我有个接口IStatus暂时来说没什么内容,或许可以放一些共有的状态,比如响应成功或者失败。然后数据模型以状态的形式存在,因为我有个不怎么成熟的想法就是数据往往都是根据业务状态在修改它本身的内容。然后就一个login()方法,里面的参数有请求和响应。好吧,是时候去实现它了--》
package com.change.mvpdemo.modle.impl; import android.os.AsyncTask; import android.text.TextUtils; import com.change.mvpdemo.modle.ILoginStatus; import com.change.mvpdemo.modle.IStatusCallback; /** * 实现类,真正的数据访问在这里。 * * @author Change */ public class LoginStatus implements ILoginStatus { private int status = ILoginStatus.STATUS_LOGIN_ING; private String msg = ""; @Override public void login(final String account, final String psw, final IStatusCallback callback) { new AsyncTask<String, Void, ILoginStatus>() { @Override protected ILoginStatus doInBackground(String... arg0) { if (varify(account, psw)) { try {//模拟网络请求耗时处理 Thread.sleep(2000); } catch (InterruptedException e) { e.printStackTrace(); } if ("Change".equals(account) && "123".equals(psw)) { status = ILoginStatus.STATUS_LOGIN_SUCCESS; msg = "登陆成功"; } else { status = ILoginStatus.STATUS_LOGIN_FAIL; msg = "登陆失败"; } } return LoginStatus.this; } @Override protected void onPreExecute() { callback.onStatus(LoginStatus.this); } @Override protected void onPostExecute(ILoginStatus result) { callback.onStatus(result); } }.execute(); } /** * 本地校验 * * @param account * @param psw * @return */ private boolean varify(String account, String psw) { if (TextUtils.isEmpty(account)) { status = ILoginStatus.STATUS_VERIFY_FAIL; msg = "用户名不能为空!"; return false; } if (TextUtils.isEmpty(psw)) { status = ILoginStatus.STATUS_VERIFY_FAIL; msg = "密码不能为空!"; return false; } return true; } public int getStatus() { return status; } public void setStatus(int status) { this.status = status; } public String getMsg() { return msg; } public void setMsg(String msg) { this.msg = msg; } }
【简单说说】这是我的数据模型,我有状态(status),要反馈给视图的信息(msg)。里面有本地验证方法varify()和提交服务器(此处只是模拟个耗时)login()方法的实现。根据login方法的反馈修改自身并回调。我们该关心是谁在实现这个回调了--》
【再看看我们熟悉的】视图层,万恶的视图永远都占据着用户的眼球,好吧,所有后台偷偷摸摸完成的东西都要拿出来晾晾了。--》
package com.change.mvpdemo.view; /** * MVP模式的V(视图)层 * 这是一个抽象的登陆视图,里面都是一些界面动作,想要执行这些动作的界面都会去实现它。 * @author Change * */ public interface ILoginView extends IView{ /** * 弹出提示信息。 */ public void showMsg(String msg); /** * 成功登陆跳转主页。 */ public void moveToMain(); /** * 加载中,万恶的菊花。 */ public void showLoadding(); /** * 隐藏菊花。 */ public void hideLoadding(); }
【简单说说】无论是亲切的hello world(主页)跳转,还是万恶的菊花,还是该死的提示。ILoginView一手包办。来吧,让UI交互来的更猛烈些吧。
【我们最为熟悉的】activity君隆重登场,它会毫不留情的实现ILoginView。并且将UI蹂躏的七零八落。
1 package com.change.mvpdemo.view.impl; 2 3 import android.app.Activity; 4 import android.app.ProgressDialog; 5 import android.content.Intent; 6 import android.os.Bundle; 7 import android.view.View; 8 import android.view.View.OnClickListener; 9 import android.widget.Button; 10 import android.widget.EditText; 11 import android.widget.Toast; 12 13 import com.change.mvpdemo.R; 14 import com.change.mvpdemo.presenter.LoginPersenter; 15 import com.change.mvpdemo.view.ILoginView; 16 17 /** 18 * 我的的activity,实现view抽象类,获得动作。 19 * 20 * @author Change 21 * 22 */ 23 public class LoginActivity extends Activity implements ILoginView, 24 OnClickListener { 25 private ProgressDialog dialog; 26 private EditText etAccount, etPsw; 27 private Button btnLogin; 28 private LoginPersenter mPersenter; 29 30 @Override 31 protected void onCreate(Bundle savedInstanceState) { 32 super.onCreate(savedInstanceState); 33 setContentView(R.layout.activity_login); 34 mPersenter = new LoginPersenter(); 35 mPersenter.setLoginPersenterView(this); 36 initViews(); 37 } 38 39 @Override 40 public void showMsg(String msg) { 41 Toast.makeText(this, msg, Toast.LENGTH_SHORT).show(); 42 } 43 44 @Override 45 public void moveToMain() { 46 Intent toMain = new Intent(this, MainActivity.class); 47 startActivity(toMain); 48 } 49 50 @Override 51 public void showLoadding() { 52 dialog.show(); 53 } 54 55 @Override 56 public void hideLoadding() { 57 dialog.cancel(); 58 } 59 60 @Override 61 public void initViews() { 62 dialog = new ProgressDialog(this); 63 dialog.setMessage("加载中。。。"); 64 etAccount = (EditText) findViewById(R.id.et_account); 65 etPsw = (EditText) findViewById(R.id.et_psw); 66 btnLogin = (Button) findViewById(R.id.btn_login); 67 btnLogin.setOnClickListener(this); 68 } 69 70 @Override 71 public void onClick(View arg0) { 72 switch (arg0.getId()) { 73 case R.id.btn_login: 74 mPersenter.didLoginSuccess(etAccount.getText().toString(), etPsw 75 .getText().toString()); 76 break; 77 78 default: 79 break; 80 } 81 } 82 83 }
【主导器君隆重登场】我想诸位都已经看到这厮了-_-|||-->mPersenter,没错,这厮便是我的主导器君。好的,它来了--》
package com.change.mvpdemo.presenter; import com.change.mvpdemo.modle.ILoginStatus; import com.change.mvpdemo.modle.IStatus; import com.change.mvpdemo.modle.IStatusCallback; import com.change.mvpdemo.modle.impl.LoginStatus; import com.change.mvpdemo.view.ILoginView; /** * MVP模式中的P(主导器),它负责主导所有的模型和视图。 * * @author Change * */ public class LoginPersenter { private ILoginView mLoginView;// 持有视图对象 private ILoginStatus mStatus;// 持有模型 public LoginPersenter() { mStatus = new LoginStatus(); } public void setLoginPersenterView(ILoginView _loginView) { this.mLoginView = _loginView; } public ILoginView getLoginPersenterView() { return mLoginView; } public void didLoginSuccess(String account, String psw) { mStatus = new LoginStatus(); mStatus.login(account, psw, new IStatusCallback() { @Override public void onStatus(IStatus status) { LoginStatus s = (LoginStatus) status; switch (s.getStatus()) { case ILoginStatus.STATUS_VERIFY_FAIL:// 验证失败 case ILoginStatus.STATUS_LOGIN_FAIL:// 登陆失败 mLoginView.hideLoadding(); mLoginView.showMsg(s.getMsg()); break; case ILoginStatus.STATUS_LOGIN_ING:// 登陆中 mLoginView.showLoadding(); break; case ILoginStatus.STATUS_LOGIN_SUCCESS:// 登陆成功 mLoginView.hideLoadding(); mLoginView.moveToMain(); break; default: break; } } }); } }
【其实它也不过如此】主导器中我就写了个核心的方法didLoginSuccess(),它主导模型与视图,让视图在根据模型的变化而做出正确的响应。
【该做个总结性的发言了】莱迪森,剑特闷-_-||
【MVP模式有什么用】其实回顾下模式的各个模块,似乎解耦的很干脆,是的,我们的视图和模型被瓦解出来了,这个时候我们的TDD(测试驱动开发)就能更好的实施了,我们能在后台未完成的时候很快捷的模拟出自己想要的数据,从而让我们进度不被滞后。
【世界杯要决赛了,买什么?】