淺談Android中的MVC與MVP模式

來源:互聯網
上載者:User

標籤:

使用MVC或者MVP模式會增加很多的類,但是確可以讓代碼結構變得清晰,方便了後期維護拓展方便。把資料層跟視圖層分離,處理事務的邏輯單獨的放在一個類中,讓Activity僅僅具有展示功能。

下面我們就MVC模式跟MVP模式進行分別講解,總之來說各有利弊。在實際的開發中,我們根據實際情況進行取捨。個人認為MVP模式更簡單一些,因為MVP模式中會把部分邏輯Activity中,但是這就造成了Activity的相對繁瑣,沒有實現完全的隔離。而我們採用的MVC模式則是更好的處理了這個問題,但是在應用的過程中,部分邏輯可能比較繞。

下面我們通過一個使用者登入這個例子來分別理解這兩種模式。

首先來講解MVC模式:

這裡說將對MVC模式不是傳統的MVC模式,感謝極光推送IM提供的demo,這個demo所採用的就是一種MVC模式,具體的實現思路是將model層,view層,controller層分離,而Activity只是用來載入視圖使用,初始化控制項交給view層,對資料的擷取交給model層,對資料的處理交給controller層,我們的Activity是異常的清爽!

首先我們來設計view層,自訂一個view,繼承自我們需要的viewgroup,LinearLayout或者FrameLayout等。我們自訂的目的是為了下一步執行個體化控制項,從而不必在Activity中執行個體化了。這裡我們只是簡單的繼承,通常不需要自己多加邏輯,所以不要慌。代碼如下:

public class LoginView extends RelativeLayout{    private Context mContext;    /**     * 因為我們是在布局檔案中使用,所以只需要重寫這個構造方法就可以了     * @param context     * @param attrs     */    public LoginView(Context context, AttributeSet attrs) {        super(context, attrs);        this.mContext = context;    }}


現在我們就使用已經定義好的viewgroup來進行布局,只是一個簡單的登入視窗,一個使用者名稱,一個密碼,還有一個登入按鍵:

<?xml version="1.0" encoding="utf-8"?><com.baiyyyhjl.mode.mvc.view.LoginView xmlns:android="http://schemas.android.com/apk/res/android"    android:layout_width="match_parent"    android:layout_height="match_parent">    <EditText        android:id="@+id/et_username"        android:layout_width="match_parent"        android:layout_height="wrap_content"        android:hint="使用者名稱"        android:padding="10dp" />    <EditText        android:id="@+id/et_password"        android:layout_width="match_parent"        android:layout_height="wrap_content"        android:layout_below="@id/et_username"        android:hint="密碼"        android:padding="10dp" />    <Button        android:id="@+id/btn_login"        android:layout_width="wrap_content"        android:layout_height="wrap_content"        android:layout_below="@id/et_password"        android:padding="10dp"        android:text="登入" /></com.baiyyyhjl.mode.mvc.view.LoginView>


現在我們在LoginView中添加控制項的初始化的方法。

public void initModule(){        mUsername = (EditText) findViewById(R.id.et_username);        mPassword = (EditText) findViewById(R.id.et_password);        mLoginBtn = (Button) findViewById(R.id.btn_login);    }


擷取到EditText中的內容,以及對mLoginBtn點擊事件的監聽,這裡我們可以添加更多關於view的操作,一切根據實際需求來:

    public String getUserName(){        return mUsername.getText().toString().trim();    }    public String getPassword(){        return mPassword.getText().toString().trim();    }    public void setListeners(OnClickListener onClickListener){        mLoginBtn.setOnClickListener(onClickListener);    }    public void userNameError(Context context){        Toast.makeText(context, "使用者名稱不可為空", Toast.LENGTH_SHORT).show();    }    public void passWordError(Context context){        Toast.makeText(context, "密碼不可為空", Toast.LENGTH_SHORT).show();    }    public void loginSuccess(Context context){        Toast.makeText(context, "登入成功啦!", Toast.LENGTH_SHORT).show();    }    public void loginFailure(Context context){        Toast.makeText(context, "登入失敗。", Toast.LENGTH_SHORT).show();    }}


好了,一個view層已經完成,下面來說model層,主要是對資料的請求,比如擷取資料庫中的資料,網路請求等:

這裡我們假定進行網路請求,一個LoginModel,有一個返回結果的回調介面,具體類比代碼如下:

<pre style="font-family: 宋體; font-size: 15pt; background-color: rgb(255, 255, 255);"><pre name="code" class="java">public class LoginModel implements ILoginModel {    @Override    public void login(final String username, final String password, final ResultCallBack callBack) {        // 類比子線程的耗時操作,例如網路請求,這裡我們使用非同步請求        new MyAsyncTask(callBack).execute(username, password);    }    private class MyAsyncTask extends AsyncTask<String, Void, Boolean>{        ResultCallBack mCallBack;        public MyAsyncTask(ResultCallBack callBack){            this.mCallBack = callBack;        }        @Override        protected Boolean doInBackground(String... params) {            try {                Thread.sleep(2000);            } catch (InterruptedException e) {                e.printStackTrace();            }            if (params[0].equals("hjl") && params[1].equals("123456")){                return true;            } else {                return false;            }        }        @Override        protected void onPostExecute(Boolean aBoolean) {            if (aBoolean){                mCallBack.success();            } else {                mCallBack.failure();            }        }    }}




最後實現controller層:

public class LoginController implements View.OnClickListener{    private LoginView mLoginView;    private MVCLoginActivity mContext;    private ILoginModel mLoginModel;    public LoginController(LoginView loginView, MVCLoginActivity context){        this.mLoginView = loginView;        this.mContext = context;        mLoginModel = new LoginModel();    }    @Override    public void onClick(View v) {        switch (v.getId()){            case R.id.btn_login:                final String username = mLoginView.getUserName();                final String password = mLoginView.getPassword();                if (TextUtils.isEmpty(username)){                    mLoginView.userNameError(mContext);                    break;                }                if (TextUtils.isEmpty(password)){                    mLoginView.passWordError(mContext);                    break;                }                // 調用model層進行網路請求                mLoginModel.login(username, password, new ResultCallBack() {                    @Override                    public void success() {                        mLoginView.loginSuccess(mContext);                        mContext.finish();                    }                    @Override                    public void failure() {                        mLoginView.loginFailure(mContext);                    }                });                break;        }    }}

這樣MVC架構就完成了,在Activity中展示:

public class MVCLoginActivity extends AppCompatActivity {    private LoginView mLoginView = null;    private LoginController mLoginController;    @Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.mvc_activity_login);        // 控制項的綁定        mLoginView = (LoginView) findViewById(R.id.login_view);        mLoginView.initModule();        mLoginController = new LoginController(mLoginView, this);        // 事件的監聽        mLoginView.setListeners(mLoginController);    }}

一個相對複雜的登入操作,在我們的Activity中只有簡單的幾行代碼,具體的邏輯全都交給MVC,這裡我們的思想是Activity不屬於view層,而只供展示以及一些簡單邏輯的處理,我們把MVC層全部單獨為幾個類,讓Activity中僅僅幾行代碼,而且整體的代碼結構相當的清晰!總結:對於控制項的綁定等相關操作邏輯我們寫在view層中,對於資料的擷取(讀取資料庫,擷取網路資料)我們寫在model中,對於代碼的所有邏輯(點擊事件的處理,相關事件)等我們寫在controller層中。

下一項我們來介紹MVP模式,個人感覺這個模式更好理解,弊端在文章開頭已經說了。

之前看過大神分析過MVP模式,其實我第一次接觸這種模式是我剛工作不久,公司項目裡用到。當時因為基礎不大好,總感覺超級麻煩,又是這麼多類,這麼多介面的,直接寫在Activity中多省事。後來代碼寫的多一點了,就發現如果全都寫在Activity中維護起來相當困難,有時候代碼量多了,可能自己都看不懂自己寫的代碼了,也漸漸理解了設計模式的好處。


我們還是以這個登入需求為例,用MVP模式寫一遍,體會一下。model層還是進行實體模型和商務邏輯,view層這裡我們直接使用Activity,就是繫結控制項等操作放在Activity中,不再多加一個類,presenter層負責處理model和view之間的互動。

首先還是寫view層介面ILoginView,這裡我們需要把所有需要的條件考慮到:

public interface ILoginView {    String getUsername();    String getPassword();    void userError(Context context);    void passwordError(Context context);    void loginSuccess(Context context);    void loginFailure(Context context);}


接下來是model層,類比網路請求

public class LoginModel implements ILoginModel {    @Override    public void login(final String username, final String password, final ResultCallBack callBack) {        new Thread(new Runnable() {            @Override            public void run() {                try {                    Thread.sleep(2000);                } catch (InterruptedException e) {                    e.printStackTrace();                }                if (username.equals("hjl") && password.equals("123456")) {                    callBack.success();                } else {                    callBack.failure();                }            }        }).start();    }}


最後在Activity中實現:

public class MVPLoginActivity extends AppCompatActivity implements View.OnClickListener, ILoginView {    private EditText mUsername;    private EditText mPassword;    private Button mLoginBtn;    private LoginPresenter mLoginPresenter;    @Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.mvc_activity_login);        mLoginPresenter = new LoginPresenter(this);        initView();    }    private void initView() {        mUsername = (EditText) findViewById(R.id.et_username);        mPassword = (EditText) findViewById(R.id.et_password);        mLoginBtn = (Button) findViewById(R.id.btn_login);        mLoginBtn.setOnClickListener(this);    }    @Override    public String getUsername() {        return mUsername.getText().toString().trim();    }    @Override    public String getPassword() {        return mPassword.getText().toString().trim();    }    @Override    public void loginSuccess() {        Toast.makeText(this, "登入成功", Toast.LENGTH_SHORT).show();        finish();    }    @Override    public void loginFailure() {        Toast.makeText(this, "使用者名稱或密碼錯誤", Toast.LENGTH_SHORT).show();    }    @Override    public void onClick(View v) {        switch (v.getId()) {            case R.id.btn_login:                if (TextUtils.isEmpty(getUsername())) {                    Toast.makeText(this, "使用者名稱不可為空", Toast.LENGTH_SHORT).show();                    break;                }                if (TextUtils.isEmpty(getPassword())) {                    Toast.makeText(this, "密碼不可為空", Toast.LENGTH_SHORT).show();                    break;                }                mLoginPresenter.login();                break;        }    }}


以上就是android中的兩種設計模式,不知道自己寫的是否標準,但是感覺這樣寫已經達到了代碼結構的清晰。其中MVC是借鑒極光推送im的demo,從中學習,而且感覺他寫的這種方法是在很棒。第二種是項目中用到的,也很簡單清晰。

以上所說的demo下載點擊開啟連結





淺談Android中的MVC與MVP模式

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.