Android應用架構演變

引言

總結了多年的移動開發經驗,特別是在Android端的積累,之前認爲從移動端APP談架構,其實有點舉大旗,由於大部份項目都在作業務理,且每每不是很大,並無多複雜的數據處理或高併發(只針對我的而言);而且長期認爲架構這個詞,用在Web端或者大型系統裏比較好一點,好的架構好,意味系統更穩健、高效率, 更大致量。總之,有種大材小用的感受,然而,隨着Android應用開發規模的擴大,客戶端業務邏輯也愈來愈複雜,已然不是簡單的數據展現了,APP也須要進行架構設計,拆分視圖和數據,解除模塊之間的耦合,提升模塊內部的聚合度。html

APP 程序的結構

對於開發人員來說,項目層面決定了如何搭建整個項目及劃分模塊,經常使用的Android程序結構:程序員

  • UI層
    數據展現與管理
    用戶交互
    繪製
    Adapter
  • 業務邏輯層
    持久化數據(內存中,至關於全局數據)
    數據加式(數據層的數據有時候須要進行加工成UI層須要的數據)
    數據變化的通知機制
  • 數據層
    數據訪問(DB,文件,網絡等)
    緩存(圖片,文件等)
    配置文件(shared perference)

從程序結構看,架構在APP中無處不在,只是咱們不太關注,最簡單的Demo其實都有涉及架構(一般是MVC)。從Android誕生至今,移動端的架構變動了不少次,從最初的MVC到MVP, 從冷門的Flutter(由RN引入到移動端)到Google的AAC/MVVM;好像架構的思想一直在變,但萬變不離其中。下面將依次介紹MVC、MVP、MVVM這幾種主流的架構設計,這裏不會很深刻的分析這些架構的代碼上有何區別,只是分析它們的設計思路,在項目中方便的選擇適用的架構。算法

MVC

很是經典的架構,無論哪一個平臺,都有這樣的架構,好用又實惠。Android採用XML文件實現頁面佈局,經過Java在Activity中開發業務邏輯,這種開發模式實際上已經採用了MVC的思想,分離視圖和控制器。MVC模式(Model–view–controller)是軟件工程中的一種軟件架構模式,把軟件系統分爲三個基本部分:模型(Model)、視圖(View)和控制器(Controller)。數據庫

維基百科:
MVC模式最先由Trygve Reenskaug在1978年提出,是施樂帕羅奧多研究中心(XeroxPARC)在20世紀80年代爲程序語言Smalltalk發明的一種軟件架構。MVC模式的目的是實現一種動態的程序設計,使後續對程序的修改和擴展簡化,而且使程序某一部分的重複利用成爲可能。除此以外,此模式經過對複雜度的簡化,使程序結構更加直觀。軟件系統經過對自身基本部分分離的同時也賦予了各個基本部分應有的功能。專業人員能夠經過自身的專長分組:
  • 控制器(Controller)- 負責轉發請求,對請求進行處理。
  • 視圖(View) – 界面設計人員進行圖形界面設計。
  • 模型(Model) – 程序員編寫程序應有的功能(實現算法等等)、數據庫專家進行數據管理和數據庫設計(能夠實現具體的功能)。

在Android編程中,View對應xml佈局文件,Model對應實體模型(網絡、數據庫、I/O),Controller對應Activity業務邏輯,數據處理和UI處理。以下圖所示:
編程

//Model
public interface WeatherModel {
    void getWeather(String cityNumber, OnWeatherListener listener);
}
................

public class WeatherModelImpl implements WeatherModel {
    @Override
    public void getWeather(String cityNumber, final OnWeatherListener listener) {
        /*數據層操做*/
        VolleyRequest.newInstance().newGsonRequest(http://www.weather.com.cn/data/sk/ + cityNumber + .html,
        Weather.class, new Response.Listener<weather>() {
            @Override
            public void onResponse(Weather weather) {
                if (weather != null) {
                    listener.onSuccess(weather);
                } else {
                    listener.onError();
                }
            }
        }, new Response.ErrorListener() {
            @Override
            public void onErrorResponse(VolleyError error) {
                listener.onError();
            }
        });
    }
}

//Controllor(View)層
public class MainActivity extends ActionBarActivity implements OnWeatherListener, View.OnClickListener {
    private WeatherModel weatherModel;
    private EditText cityNOInput;
    private TextView city;
    ...

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        weatherModel = new WeatherModelImpl();
        initView();
    }

    //初始化View
    private void initView() {
        cityNOInput = findView(R.id.et_city_no);
        city = findView(R.id.tv_city);
        ...
        findView(R.id.btn_go).setOnClickListener(this);
    }

    //顯示結果
    public void displayResult(Weather weather) {
        WeatherInfo weatherInfo = weather.getWeatherinfo();
        city.setText(weatherInfo.getCity());
        ...
    }

    @Override
    public void onClick(View v) {
        switch (v.getId()) {
            case R.id.btn_go:
                weatherModel.getWeather(cityNOInput.getText().toString().trim(), this);
                break;
        }
    }

    @Override
    public void onSuccess(Weather weather) {
        displayResult(weather);
    }

    @Override
    public void onError() {
        Toast.makeText(this, 獲取天氣信息失敗, Toast.LENGTH_SHORT).show();
    }

    private T findView(int id) {
        return (T) findViewById(id);
    }
}

例子分析:緩存

  • Activity裏面的控件必須關心業務和數據,才能知道本身怎麼展現。
  • 全部的邏輯都在activity裏面。

所以,在實際開發過程當中,純粹做爲View的各個XML文件功能較弱,Activity基本上都是View和Controller的合體,既要負責視圖的顯示又要加入控制邏輯,承擔的功能不少,致使代碼量很大。全部更貼切的目前常規的開發說應該是View-Model模式,大部分都是經過Activity的協調。性能優化

MVP

MVP是從MVC過渡而來,MVP架構由三部分組成:View負責顯示,Presenter負責邏輯處理,Model提供數據。Android開發從MVC過渡到MVP,最主要的變化就是將Activity中負責業務邏輯的代碼移到Presenter中,Activity只充當MVP中的View,負責界面初始化以及創建界面控件與Presenter的關聯。網絡

例如,MVC中,在業務邏輯稍微複雜一點的頁面,Activity的代碼超過一千是很容易的,但Activity並非一個標準的MVC模式中的Controller,它的首要職責是加載應用的佈局和初始化用戶界面,並接受並處理來自用戶的操做請求,進而做出響應。隨着界面及其邏輯的複雜度不斷提高,Activity類的職責不斷增長,以至變得龐大臃腫,那天然會想到進行拆分。這樣拆分以後,Presenter承擔了大量的邏輯操做,避免了Activity的臃腫。整個架構以下圖所示:
架構

//Model層
/**
 * 定義業務接口
 */
public interface IUserBiz {
    public void login(String username, String password, OnLoginListener loginListener);
}

/**
 * 結果回調接口
 */
public interface OnLoginListener {
    void loginSuccess(User user);

    void loginFailed();
}

/**
 * 具體Model的實現
 */
public class UserBiz implements IUserBiz {
    @Override
    public void login(final String username, final String password, final OnLoginListener loginListener) {
        //模擬子線程耗時操做
        new Thread() {
            @Override
            public void run() {
                try {
                    Thread.sleep(2000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                //模擬登陸成功
                if ("zhy".equals(username) && "123".equals(password)) {
                    User user = new User();
                    user.setUsername(username);
                    user.setPassword(password);
                    loginListener.loginSuccess(user);
                } else {
                    loginListener.loginFailed();
                }
            }
        }.start();
    }
}


//View
public interface IUserLoginView {
    String getUserName();

    String getPassword();

    void clearUserName();

    void clearPassword();

    void showLoading();

    void hideLoading();

    void toMainActivity(User user);

    void showFailedError();
}


//而後Activity實現這個這個接口:
public class UserLoginActivity extends ActionBarActivity implements IUserLoginView {
    private EditText mEtUsername, mEtPassword;
    private Button mBtnLogin, mBtnClear;
    private ProgressBar mPbLoading;
    private UserLoginPresenter mUserLoginPresenter = new UserLoginPresenter(this);

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

    private void initViews() {
        mEtUsername = (EditText) findViewById(R.id.id_et_username);
        mEtPassword = (EditText) findViewById(R.id.id_et_password);
        mBtnClear = (Button) findViewById(R.id.id_btn_clear);
        mBtnLogin = (Button) findViewById(R.id.id_btn_login);
        mPbLoading = (ProgressBar) findViewById(R.id.id_pb_loading);
        mBtnLogin.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                mUserLoginPresenter.login();
            }
        });
        mBtnClear.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                mUserLoginPresenter.clear();
            }
        });
    }

    @Override
    public String getUserName() {
        return mEtUsername.getText().toString();
    }

    @Override
    public String getPassword() {
        return mEtPassword.getText().toString();
    }

    @Override
    public void clearUserName() {
        mEtUsername.setText("");
    }

    @Override
    public void clearPassword() {
        mEtPassword.setText("");
    }

    @Override
    public void showLoading() {
        mPbLoading.setVisibility(View.VISIBLE);
    }

    @Override
    public void hideLoading() {
        mPbLoading.setVisibility(View.GONE);
    }

    @Override
    public void toMainActivity(User user) {
        Toast.makeText(this, user.getUsername() +
                " login success , to MainActivity", Toast.LENGTH_SHORT).show();
    }

    @Override
    public void showFailedError() {
        Toast.makeText(this,
                "login failed", Toast.LENGTH_SHORT).show();
    }
}


//Presenter
public class UserLoginPresenter {
    private IUserBiz userBiz;
    private IUserLoginView userLoginView;
    private Handler mHandler = new Handler();

    //Presenter必需要能拿到View和Model的實現類
    public UserLoginPresenter(IUserLoginView userLoginView) {
        this.userLoginView = userLoginView;
        this.userBiz = new UserBiz();
    }

    public void login() {
        userLoginView.showLoading();
        userBiz.login(userLoginView.getUserName(), userLoginView.getPassword(), new OnLoginListener() {
            @Override
            public void loginSuccess(final User user) {
                //須要在UI線程執行
                mHandler.post(new Runnable() {
                    @Override
                    public void run() {
                        userLoginView.toMainActivity(user);
                        userLoginView.hideLoading();
                    }
                });
            }

            @Override
            public void loginFailed() {
                //須要在UI線程執行
                mHandler.post(new Runnable() {
                    @Override
                    public void run() {
                        userLoginView.showFailedError();
                        userLoginView.hideLoading();
                    }
                });
            }
        });
    }

    public void clear() {
        userLoginView.clearUserName();
        userLoginView.clearPassword();
    }
}

說明:併發

  • Model經過回調的方式將數據傳到Presenter中;
  • View(Activity)負責響應用戶操做,經過Presenter暴露的方法請求數據;
  • Presenter在獲取數據後,經過View(Activity)暴露的方法實現界面控制,經過Model來獲取數據,Model包含網絡、數據庫以及I/O等。

採用MVP明顯的優勢是避免了傳統開發模式中View和Model耦合的狀況,提升了代碼可擴展性、組件複用能力、團隊協做的效率以及單元測試的便利性。但也有一些缺點,好比:

  • Model到Presenter的數據傳遞過程須要經過回調;
  • View(Activity)須要持有Presenter的引用,同時,Presenter也須要持有View(Activity)的引用,增長了控制的複雜度;
  • MVC中Activity的代碼很臃腫,轉移到MVP的Presenter中,一樣形成了Presenter在業務邏輯複雜時的代碼臃腫。

因此,MVC到MVP簡單說,就是增長了一個接口下降一層耦合。

MVVM

MVVM 架構模式是微軟在 2005 年誕生的,MVVM是Model-View-ViewModel的簡稱,它由三個部分組成,也就是 Model、View 和 ViewModel,其中視圖模型(ViewModel)其實就是 PM 模式中的展現模型,在 MVVM 中叫作視圖模型。從實際效果來看,ViewModel是View的數據模型和Presenter的結合,具體結構以下圖所示:

說明:

  • Model(模型層)經過網絡和本地數據庫獲取視圖層所需數據;
  • View(視圖層)採用XML文件進行界面的描述;
  • ViewModel(視圖-模型層)負責View和Model之間的通訊,以此分離視圖和數據。

View和Model之間經過Android Data Binding技術,實現視圖和數據的雙向綁定;ViewModel持有Model的引用,經過Model的方法請求數據;獲取數據後,經過Callback(回調)的方式回到ViewModel中,因爲ViewModel與View的雙向綁定,使得界面得以實時更新。同時,界面輸入的數據變化時,因爲雙向綁定技術,ViewModel中的數據得以實時更新,提升了數據採集的效率。

MVVM架構將Presenter更名爲ViewModel,基本上與MVP模式徹底一致,惟一的區別是,它採用雙向綁定(data-binding)View的變更,自動反映在 ViewModel,反之亦然,這就致使了咱們若是要完整的採用 MVVM 必須熟練的掌握 DataBinding 等基礎組建,這就給咱們MVVM引入項目帶了困難。

總結

其實,MVC、MVP及MVVM沒有絕對好壞,在軟件編程過程當中,也不必非此即彼,脫離實際項目比較這些模式優劣毫無心義,各類模式都有優勢和缺點,沒有好壞之分。越高級的架構實現起來越複雜,須要更多的學習成本更多的人力,因此說技術選型關鍵是在你本身項目的特色,團隊的水平,資源的配備,開發時間的限制,這些纔是重點!可是很多團隊本末倒置,把mvvm往本身的項目硬套。最重要的是讓軟件高內聚、低耦合、可維護、可擴展。

能夠理解爲移動端的架構思惟「MVX」,便是說按這個規則的分工,咱們不用費盡心思考慮狹義架構的分層問題了,就沿用Model-View-X來就能夠(固然還能夠本身加一些輔助的模塊層)。

歡迎加入個人QQ交流羣: 892872246 本羣涉獵以下技術: 1.LiveDataBus 2.Google官方架構組件 3.Jetpack架構 4.餓了麼通訊技術 5.OPenGL 6.音視頻 7.人工智能 8.Python 9.性能優化 10.Flutter等
相關文章
相關標籤/搜索