Android教程網
  1. 首頁
  2. Android 技術
  3. Android 手機
  4. Android 系統教程
  5. Android 游戲
 Android教程網 >> Android技術 >> 關於Android編程 >> Android 初識 MVC、MVP框架

Android 初識 MVC、MVP框架

編輯:關於Android編程

前言

MVC、MVP、MVVP相信大家已經耳熟能詳了,作為Android最出名的三個框架,它們的應用是非常的廣泛。這篇博客就來簡單介紹下其中二種框架。也加強下自己對這方面的了解。由於自己菜鳥一枚,有不對和需要補充的地方歡迎評論~

MVC

MVC全名是:Model(模型) View(視圖) Controller(控制器) 是軟件架構中最常見的框架,簡單來說,就是通過Controller的控制去操作Model層的數據,並且返回給View作展示,具體見下圖:
這裡寫圖片描述
當用戶觸發了一個事件,View層會將這個事件發送給Controller控制層,然後Controller會通知Model層更新數據,Model層更新完數據之後會直接顯示在View層上,這就是MVC的工作原理了。

說到這裡可能就有人會說了,明明一個Activity就能搞定的事,為什麼還分三個類來寫,煩不煩?

So,那為什麼要采用架構設計呢?

通過設計,可以使我們的程序模塊化,做到模塊內部的高聚合和模塊之間的低耦合。這樣做的好處是,當我們在開發中只需專注一點即可!提高程序的開發效率,並且更容器做後期的測試以及定位問題。但是,我們不能為了設計而設計,為了架構而架構。對於不同量級的工程,具體的架構實現方法必然是不同的。

在Android中,MVC的應用無處不在,如:我們經常寫的layout.xml就對應MVC的View層,裡面都是一些View的布局代碼,而各種JavaBean和Adapter就類似與MVC中的Model層,Activity就是Controller層了。下面用一個登錄的小例子來演示一下。

首先看下常規寫法,把所有邏輯都放在Activity裡面的:
* activity_login.xml布局:
 




    

        

        
    

    

        

        
    

    
LoginActivity代碼:

public class LoginActivity extends AppCompatActivity {

    private EditText mUsername;
    private EditText mPassword;
    private ProgressDialog dialog;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_login);
        initView();
    }

    private void initView() {
        dialog = new ProgressDialog(this);
        mUsername = (EditText) findViewById(R.id.et_login_username);
        mPassword = (EditText) findViewById(R.id.et_login_password);
    }

    // 登錄
    public void login(View v){
        String username = mUsername.getText().toString();
        String password = mPassword.getText().toString();
        final User user = new User();
        user.username = username;
        user.password = password;

        if(checkUser(user)){
            dialog.show();
            // 模擬請求登錄接口
            new Thread(new Runnable() {
                @Override
                public void run() {
                    // 模擬耗時操作
                    SystemClock.sleep(2000);

                    if ("123".equals(user.username) && "123".equals(user.password)){
                        runOnUiThread(new Runnable() {
                            @Override
                            public void run() {
                                onLoginSuccess();
                            }
                        });

                    }else{
                        runOnUiThread(new Runnable() {
                            @Override
                            public void run() {
                                onLoginError();
                            }
                        });
                    }
                }
            }).start();
        }else{
            Toast.makeText(getApplicationContext(), "用戶名或密碼不能為空", Toast.LENGTH_SHORT).show();
        }
    }

    /**
     * 登錄成功
     */
    private void onLoginSuccess() {
        Toast.makeText(getApplicationContext(), "登錄成功", Toast.LENGTH_SHORT).show();
        dialog.dismiss();
    }

    /**
     * 登錄失敗
     */
    private void onLoginError() {
        Toast.makeText(getApplicationContext(), "登錄失敗", Toast.LENGTH_SHORT).show();
        dialog.dismiss();
    }

    /**
     * 判斷用戶輸入的信息是否正確
     * @param user
     * @return
     */
    private boolean checkUser(User user) {
        // 在這裡由於演示,所以只做簡單非空判斷,實際開發中會判斷更多情況
        if(TextUtils.isEmpty(user.username) || TextUtils.isEmpty(user.password)){
            return false;
        }
        return true;
    }
}
UserBean:

public class User {
    public String username;
    public String password;
}

這是一個超級簡化版的登錄,但是差不多已經有上百行代碼了,如果都寫出來的話,那麼就太多了,所以我們就需要遵循某種模式來進行拆分,下面是用MVC模式進行拆分後的代碼:

新增一個類,將請求網絡的操作抽取出來(這裡由於模擬請求網絡所以代碼並不多):


public class LoginModel {

    /**
     * 請求網絡,發送登錄消息
     * @return
     */
    public boolean sendLoginInfo(User user){
        SystemClock.sleep(2000);
        if ("123".equals(user.username) && "123".equals(user.password)){
            return true;
        }else{
            return false;
        }
    }
}

修改Activity中的請求網絡為通過創建Model調用方法去判斷登錄是否成功:


LoginModel loginModel = new LoginModel();
if(loginModel.sendLoginInfo(user)) {
    runOnUiThread(new Runnable() {
        @Override
        public void run() {
            onLoginSuccess();
        }
    });
}else{
    runOnUiThread(new Runnable() {
        @Override
        public void run() {
            onLoginError();
        }
    });
}

到這裡一個簡單版的MVC抽取就完成了,其實代碼並沒有少很多,這是因為Activity存在了兩部分內容,一部分是業務相關的,一部分是界面相關的。在這裡問題就產生了,如果界面復雜或業務多的話,會導致Activity裡面的東西會很龐大,V裡面的信息就只有一個Layout,而C也就是Activity裡的東西則異常龐大。

那麼這個時候,我們可以選擇將業務部分的代碼進行拆分,其對應的模式就是:MVP
如果將Activity中界面相關的內容進行拆分,其對應的模式就是MVVM

下面簡單講解一下MVP模式,MVVM由於涉及到data binding框架,東西比較多,下次再寫。

MVP

MVP是從經典的MVC中演變過來的,在思想上,它們有想通的地方:
Controller/Presenter負責邏輯的處理,Model用於提供數據,View負責顯示。
這裡寫圖片描述
原來的Model層不變,Activity合並到View層,Presenter用於鏈接View層和Model層。
下面用MVP模式重構上面的登錄小例子:
布局文件和Bean如上,就不貼了。

分包:
這裡寫圖片描述

注意:Activity並不是View,setContextView()中的xml文件才是。

V (Activity) 代碼:


public class LoginActivity extends AppCompatActivity {

    private EditText mUsername;
    private EditText mPassword;
    private ProgressDialog mDialog;
    private LoginPresenter mPresenter;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_login);

        mUsername = (EditText) findViewById(R.id.et_login_username);
        mPassword = (EditText) findViewById(R.id.et_login_password);
        mDialog = new ProgressDialog(this);
        mPresenter = new LoginPresenter(this);
    }

    public void login(View v){
        String username = mUsername.getText().toString();
        String password = mPassword.getText().toString();
        final User user = new User();
        user.username = username;
        user.password = password;
        if(mPresenter.checkUserInfo(user)){
            mDialog.show();
            mPresenter.login(user);
        }else{
            Toast.makeText(getApplicationContext(), "用戶名或密碼不能為空", Toast.LENGTH_SHORT).show();
        }
    }

    /**
     * 登錄成功
     */
    public void loginSuccess() {
        runOnUiThread(new Runnable() {
            @Override
            public void run() {
                Toast.makeText(getApplicationContext(), "登錄成功", Toast.LENGTH_SHORT).show();
                mDialog.dismiss();
            }
        });
    }

    /**
     * 登錄失敗
     */
    public void loginError(){
        runOnUiThread(new Runnable() {
            @Override
            public void run() {
                Toast.makeText(getApplicationContext(), "登錄失敗", Toast.LENGTH_SHORT).show();
                mDialog.dismiss();
            }
        });
    }
}

P (LoginPresenter)代碼:


public class LoginPresenter {

    private LoginActivity mView;

    public LoginPresenter(LoginActivity view){
        this.mView = view;
    }

    /**
     * 校驗用戶信息
     * @param user 用戶信息
     * @return true:校驗成功 false:校驗失敗
     */
    public boolean checkUserInfo(User user){
        // 在這裡由於演示,所以只做簡單非空判斷,實際開發中會判斷更多情況
        if(TextUtils.isEmpty(user.username) || TextUtils.isEmpty(user.password)){
            return false;
        }
        return true;
    }

    /**
     * 登錄
     * @param user 用戶信息
     */
    public void login(final User user){
        new Thread(new Runnable() {
            @Override
            public void run() {
                LoginModel loginModel = new LoginModel();
                if(loginModel.sendLoginInfo(user)){
                    mView.loginSuccess();
                }else{
                    mView.loginError();
                }
            }
        }).start();
    }

}

M(LoginModel)代碼:


public class LoginModel {

    /**
     * 請求網絡,發送登錄消息
     */
    public boolean sendLoginInfo(User user) {
        // 模擬請求網絡操作
        SystemClock.sleep(2000);
        if ("123".equals(user.username) && "123".equals(user.password)) {
            return true;
        } else {
            return false;
        }
    }
}

至此,一個簡單的MVP模式的登錄小例子就出來了,相比於MVC,MVP中的代碼已經相當簡潔了,各模塊之間分工明確。
View層負責和UI進行交互,Model層負責提供數據,Presenter層負責處理業務邏輯並處理Model層和View層的通信。

相信大家也看出來了,MVP主要解決就是把邏輯層給抽出來成P層,還有就是不和MVC中的Molder層可以操作View層一樣,而是由Presenter負責通信的角色。這樣做的好處是,如果有邏輯上的修改,那麼我們直接修改Presenter層的東西就可以了。

到這裡,並沒有完善,我們還可以擴展下,上面的LoginActivity的構參中,我們傳入了一個LoginActivity:


 public LoginPresenter(LoginActivity view){
        this.mView = view;
 }

這裡其實並不合理,因為LoginActivity是屬於V層的東西,而我們在開發過程中,V層不光會有Activity,也會有Fragment,如果按照上面寫,通用性並不好,這個時候,接口開發就派上用場了。

修改構參為接口:


 public LoginPresenter(IUserLoginView  view){
        this.mView = view;
 }

創建IUserLoginView接口:


package com.airsaid.mvpdemo.i;

/**
 * Created by zhouyou on 2016/5/4.
 */
public interface IUserLoginView {

    /**
     * 登錄成功
     */
    void loginSuccess();


    /**
     * 登錄失敗
     */
    void loginError();
}

讓LoginActivity去實現這個接口:


public class LoginActivity extends AppCompatActivity implements IUserLoginView

到這裡就擴展完成了,這只是一個MVP的簡單擴展,具體大家可以看看Android官方的MVP架構實例項目。
在這裡推薦一篇相關文章,大家感興趣可以看看:Android官方MVP架構實例項目解析

  1. 上一頁:
  2. 下一頁:
熱門文章
閱讀排行版
Copyright © Android教程網 All Rights Reserved