多日未回博客園,風蕭蕭兮易水寒。
話說上一次發表隨筆已是去年,而且看看當時關於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(測試驅動開發)就能更好的實施了,我們能在后台未完成的時候很快捷的模擬出自己想要的數據,從而讓我們進度不被滯后。
【世界杯要決賽了,買什么?】