Android MVP架構搭建


目錄

  • 引言
  • 為什么用MVP架構
  • MVP理論知識
  • 乞丐版MVP架構模式的代碼實現
  • MVP中的代碼復用場景
  • 平民版MVP架構 - base層頂級父類
  • Fragment怎么辦
  • 時尚版MVP架構 - Model層的單獨優化

引言

記得第一次接觸MVP開發是上大學的時候,當時看了數十篇關於MVP的文章,這里不得不吐槽一下國內技術帖子的質量真是參次不齊啊。看完之后一直懵懵懂懂的,總覺有幾處關鍵的地方沒搞清但是文章中卻一帶而過了,比如:

  • 關於如何在Activity中高效的復用Presenter和View;
  • Mode層定義到什么程度才算是比較理想的解耦;
  • Model層與Presenter層如何比較優雅的相互通信。

抱着這些問題,我自己摸索着構建出了一套個性化風格MVP架構,使用過程中也優化了幾次,如今一年多過去了再看這套架構也就算是個能用吧,所以決定新的架構優化。

本文講述了MVP的核心概念和如何從最初的乞丐版MVP架構一步步升級到平民版MVP架構,時尚版MVP架構,以及即將開始更新的旗艦版MVP架構,為了保證思路清晰,文中包含大量代碼與文字,跟着文中的例子便可寫出一個完整的MVP架構。

為什么用MVP架構

其實我們日常開發中的Activity,Fragment和XML界面就相當於是一個 MVC 的架構模式,Activity中不僅要處理各種 UI 操作還要請求數據以及解析。

這種開發方式的缺點就是業務量大的時候一個Activity 文件分分鍾飆到上千行代碼,想要改一處業務邏輯光是去找就要費半天勁,而且有點地方邏輯處理是一樣的無奈是不同的 Activity 就沒辦法很好的寫成通用方法。

那 MVP 為啥好用呢?

MVP 模式將Activity 中的業務邏輯全部分離出來,讓Activity 只做 UI 邏輯的處理,所有跟Android API無關的業務邏輯由 Presenter 層來完成。

將業務處理分離出來后最明顯的好處就是管理方便,但是缺點就是增加了代碼量。

MVP 理論知識

在MVP 架構中跟MVC類似的是同樣也分為三層。

Activity 和Fragment 視為View層,負責處理 UI。

Presenter 為業務處理層,既能調用UI邏輯,又能請求數據,該層為純Java類,不涉及任何Android API。

Model 層中包含着具體的數據請求,數據源。

三層之間調用順序為view->presenter->model,為了調用安全着想不可反向調用!不可跨級調用!

那Model 層如何反饋給Presenter 層的呢?Presenter 又是如何操控View 層呢?看圖!

MVP架構調用關系

上圖中說明了低層的不會直接給上一層做反饋,而是通過 View 、 Callback 為上級做出了反饋,這樣就解決了請求數據與更新界面的異步操作。上圖中 View 和 Callback 都是以接口的形式存在的,其中 View 是經典 MVP 架構中定義的,Callback 是我自己加的。

View 中定義了 Activity 的具體操作,主要是些將請求到的數據在界面中更新之類的。

Callback 中定義了請求數據時反饋的各種狀態:成功、失敗、異常等。

乞丐版MVP架構模式的代碼實現

下面我們用 MVP 模式構造一個簡易模擬請求網絡的小程序。效果圖如下:

成功加載到數據

加載數據失敗

數據獲取異常

因為是模擬網絡數據請求,所以有三個請求數據的按鈕分別對應成功、失敗、異常三種不同的反饋狀態。

下面是Demo中的Java文件目錄:

Java文件目錄

Callback接口

Callback 接口是Model層給Presenter層反饋請求信息的傳遞載體,所以需要在Callback中定義數據請求的各種反饋狀態:

  1. public interface MvpCallback {
  2. /**
  3. * 數據請求成功
  4. * @param data 請求到的數據
  5. */
  6. void onSuccess(String data);
  7. /**
  8. * 使用網絡API接口請求方式時,雖然已經請求成功但是由
  9. * 於{@code msg}的原因無法正常返回數據。
  10. */
  11. void onFailure(String msg);
  12. /**
  13. * 請求數據失敗,指在請求網絡API接口請求方式時,出現無法聯網、
  14. * 缺少權限,內存泄露等原因導致無法連接到請求數據源。
  15. */
  16. void onError();
  17. /**
  18. * 當請求數據結束時,無論請求結果是成功,失敗或是拋出異常都會執行此方法給用戶做處理,通常做網絡
  19. * 請求時可以在此處隱藏“正在加載”的等待控件
  20. */
  21. void onComplete();
  22. }

Model 類

Model 類中定了具體的網絡請求操作。為模擬真實的網絡請求,利用postDelayed方法模擬耗時操作,通過判斷請求參數反饋不同的請求狀態:

  1. public class MvpModel {
  2. /**
  3. * 獲取網絡接口數據
  4. * @param param 請求參數
  5. * @param callback 數據回調接口
  6. */
  7. public static void getNetData(final String param, final MvpCallback callback){
  8. // 利用postDelayed方法模擬網絡請求數據的耗時操作
  9. new Handler().postDelayed(new Runnable() {
  10. @Override
  11. public void run() {
  12. switch (param){
  13. case "normal":
  14. callback.onSuccess("根據參數"+param+"的請求網絡數據成功");
  15. break;
  16. case "failure":
  17. callback.onFailure("請求失敗:參數有誤");
  18. break;
  19. case "error":
  20. callback.onError();
  21. break;
  22. }
  23. callback.onComplete();
  24. }
  25. },2000);
  26. }
  27. }

View 接口

View接口是Activity與Presenter層的中間層,它的作用是根據具體業務的需要,為Presenter提供調用Activity中具體UI邏輯操作的方法。

  1. public interface MvpView {
  2. /**
  3. * 顯示正在加載進度框
  4. */
  5. void showLoading();
  6. /**
  7. * 隱藏正在加載進度框
  8. */
  9. void hideLoading();
  10. /**
  11. * 當數據請求成功后,調用此接口顯示數據
  12. * @param data 數據源
  13. */
  14. void showData(String data);
  15. /**
  16. * 當數據請求失敗后,調用此接口提示
  17. * @param msg 失敗原因
  18. */
  19. void showFailureMessage(String msg);
  20. /**
  21. * 當數據請求異常,調用此接口提示
  22. */
  23. void showErrorMessage();
  24. }

Presenter類

Presenter類是具體的邏輯業務處理類,該類為純Java類,不包含任何Android API,負責請求數據,並對數據請求的反饋進行處理。

Presenter類的構造方法中有一個View接口的參數,是為了能夠通過View接口通知Activity進行更新界面等操作。

  1. public class MvpPresenter {
  2. // View接口
  3. private MvpView mView;
  4. public MvpPresenter(MvpView view){
  5. this.mView = view;
  6. }
  7. /**
  8. * 獲取網絡數據
  9. * @param params 參數
  10. */
  11. public void getData(String params){
  12. //顯示正在加載進度條
  13. mView.showLoading();
  14. // 調用Model請求數據
  15. MvpModel.getNetData(params, new MvpCallback() {
  16. @Override
  17. public void onSuccess(String data) {
  18. //調用view接口顯示數據
  19. mView.showData(data);
  20. }
  21. @Override
  22. public void onFailure(String msg) {
  23. //調用view接口提示失敗信息
  24. mView.showFailureMessage(msg);
  25. }
  26. @Override
  27. public void onError() {
  28. //調用view接口提示請求異常
  29. mView.showErrorMessage();
  30. }
  31. @Override
  32. public void onComplete() {
  33. // 隱藏正在加載進度條
  34. mView.hideLoading();
  35. }
  36. });
  37. }
  38. }

xml布局文件

沒什么好說的,直接上代碼:

  1. <?xml version="1.0" encoding="utf-8"?>
  2. <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
  3. xmlns:tools="http://schemas.android.com/tools"
  4. android:layout_width="match_parent"
  5. android:layout_height="match_parent"
  6. android:padding="16dp"
  7. android:orientation="vertical"
  8. tools:context="com.jessewu.mvpdemo.MainActivity">
  9. <TextView
  10. android:id="@+id/text"
  11. android:layout_width="match_parent"
  12. android:layout_height="0dp"
  13. android:layout_weight="1"
  14. android:text="點擊按鈕獲取網絡數據"/>
  15. <Button
  16. android:layout_width="match_parent"
  17. android:layout_height="wrap_content"
  18. android:text="獲取數據【成功】"
  19. android:onClick="getData"
  20. />
  21. <Button
  22. android:layout_width="match_parent"
  23. android:layout_height="wrap_content"
  24. android:text="獲取數據【失敗】"
  25. android:onClick="getDataForFailure"
  26. />
  27. <Button
  28. android:layout_width="match_parent"
  29. android:layout_height="wrap_content"
  30. android:text="獲取數據【異常】"
  31. android:onClick="getDataForError"
  32. />
  33. </LinearLayout>

Activity

在Activity代碼中需要強調的是如果想要調用Presenter就要先實現Presenter需要的對應的View接口。

  1. public class MainActivity extends AppCompatActivity implements MvpView {
  2. //進度條
  3. ProgressDialog progressDialog;
  4. TextView text;
  5. MvpPresenter presenter;
  6. @Override
  7. protected void onCreate(Bundle savedInstanceState) {
  8. super.onCreate(savedInstanceState);
  9. setContentView(R.layout.activity_main);
  10. text = (TextView)findViewById(R.id.text);
  11. // 初始化進度條
  12. progressDialog = new ProgressDialog(this);
  13. progressDialog.setCancelable(false);
  14. progressDialog.setMessage("正在加載數據");
  15. //初始化Presenter
  16. presenter = new MvpPresenter(this);
  17. }
  18. // button 點擊事件調用方法
  19. public void getData(View view){
  20. presenter.getData("normal");
  21. }
  22. // button 點擊事件調用方法
  23. public void getDataForFailure(View view){
  24. presenter.getData("failure");
  25. }
  26. // button 點擊事件調用方法
  27. public void getDataForError(View view){
  28. presenter.getData("error");
  29. }
  30. @Override
  31. public void showLoading() {
  32. if (!progressDialog.isShowing()) {
  33. progressDialog.show();
  34. }
  35. }
  36. @Override
  37. public void hideLoading() {
  38. if (progressDialog.isShowing()) {
  39. progressDialog.dismiss();
  40. }
  41. }
  42. @Override
  43. public void showData(String data) {
  44. text.setText(data);
  45. }
  46. @Override
  47. public void showFailureMessage(String msg) {
  48. Toast.makeText(this, msg, Toast.LENGTH_SHORT).show();
  49. text.setText(msg);
  50. }
  51. @Override
  52. public void showErrorMessage() {
  53. Toast.makeText(this, "網絡請求數據出現異常", Toast.LENGTH_SHORT).show();
  54. text.setText("網絡請求數據出現異常");
  55. }
  56. }

至此,已經完整的實現了一個簡易的MVP架構。

注意!以上代碼中還存在很大的問題,不能用到實際開發中,下面會討論目前存在的問題以及如何優化。

MVP中的代碼復用場景

因為上節中乞丐版MVP Demo的代碼只實現了一個Activity的請求操作,容易出現一個概念的混淆:

每個Activity都需要有與它對應的一套MVP(Model,View,Presenter)嗎?

答案肯定是否定的!

首先不需要數據請求的Activity當然就同樣不需要MVP輔助。與其他Activity中存在相同邏輯的Activity,就不需要重復生成對應的MVP。但是這個存在相同邏輯的定義,不同的場景有不同的說法:

需求.jpg

場景1:業務邏輯完全相同

場景1中Activity A和Activity C都只有一個“買東西”的邏輯,屬於典型的邏輯相同,所以Activity C就可以直接用Activity A寫好的MVP無需再做任何處理。

場景2、3:包含部分相同業務邏輯

場景2和場景3的邏輯類似,都屬於一個業務邏輯中包含另外一個可以單獨存在的業務邏輯,這種情況采用繼承的方法即可:

繼承關系.png

場景4

場景4中Activity C想要同時調用獨立服務於Activity A 和 Activity B的業務邏輯,只需要將兩個業務邏輯對應的Presenter分別實例化並調用業務方法即可:

  1. private PresenterA presenterA;
  2. private PresenterB presenterB;
  3. ...
  4. ...
  5. private void getData(){
  6. presenterA.getData();
  7. presenterB.getData();
  8. }

不要忘了實現兩個Presenter對應的View:

  1. public class ActivityC extends Activity implements ViewA,ViewB{
  2. ...
  3. }

場景5

場景5屬於場景3與場景4的結合體,同樣需要先把A和B的業務邏輯拆分開,然后同時調用,這里就不舉例子了。

總結

通過上面一攬子場景的分析,得出的第一個結論就是MVP的結構太過於繁重,所以為了避免多寫重復代碼和日后需要進行無意義的修改,在開發前一定要設計好邏輯調用圖,這樣才能事半功倍。

對於上面經典的通過業務邏輯繼承實現包含重復邏輯的方法,其實也可以在一個Presenter中寫好完整的邏輯方法,對於不同的Activity需要哪個業務邏輯方法就調用哪個,這樣豈不就簡單多了。但是從架構設計角度看這種做法是不嚴謹的,可能存在漏洞,所以為保持軟件架構的健壯還是不要偷懶的好。

平民版MVP架構 - base層頂級父類

之前說過乞丐版MVP架構模式中還存在很多問題不能應用到實際的開發中,大概存在的問題有:

  • 構架存在漏洞
  • 代碼冗余量大
  • 通用性差

針對這些問題我們需要進一步優化,單車變摩托,升級為可以在實際開發中使用的平民版MVP架構。

調用View可能引發的空指針異常

舉一個例子,在上述乞丐版MVP架構中的應用請求網絡數據時需要等待后台反饋數據后更新界面,但是在請求過程中當前Activity突然因為某種原因被銷毀,Presenter收到后台反饋並調用View接口處理UI邏輯時由於Activity已經被銷毀,就會引發空指針異常。

想要避免這種情況的發生就需要每次調用View前都知道宿主Activity的生命狀態。

之前是在Presenter的構造方法中得到View接口的引用,現在我們需要修改Presenter引用View接口的方式讓View接口與宿主Activity共存亡:

  1. public class MvpPresenter {
  2. // View接口
  3. private MvpView mView;
  4. public MvpPresenter(){
  5. //構造方法中不再需要View參數
  6. }
  7.  /**
  8. * 綁定view,一般在初始化中調用該方法
  9. */
  10. public void attachView(MvpView mvpView) {
  11. this.mView= mvpView;
  12. }
  13. /**
  14. * 斷開view,一般在onDestroy中調用
  15. */
  16. public void detachView() {
  17. this.mView= null;
  18. }
  19. /**
  20. * 是否與View建立連接
  21. * 每次調用業務請求的時候都要出先調用方法檢查是否與View建立連接
  22. */
  23. public boolean isViewAttached(){
  24. return mView!= null;
  25. }
  26. /**
  27. * 獲取網絡數據
  28. * @param params 參數
  29. */
  30. public void getData(String params){
  31. //顯示正在加載進度條
  32. mView.showLoading();
  33. // 調用Model請求數據
  34. MvpModel.getNetData(params, new MvpCallback() {
  35. @Override
  36. public void onSuccess(String data) {
  37. //調用view接口顯示數據
  38. if(isViewAttached()){
  39. mView.showData(data);
  40. }
  41. }
  42. @Override
  43. public void onFailure(String msg) {
  44. //調用view接口提示失敗信息
  45. if(isViewAttached()){
  46. mView.showFailureMessage(msg);
  47. }
  48. }
  49. @Override
  50. public void onError() {
  51. //調用view接口提示請求異常
  52. if(isViewAttached()){
  53. mView.showErrorMessage();
  54. }
  55. }
  56. @Override
  57. public void onComplete() {
  58. // 隱藏正在加載進度條
  59. if(isViewAttached()){
  60. mView.hideLoading();
  61. }
  62. }
  63. });
  64. }
  65. }

上面Presenter代碼中比之前增加了三個方法:

  • attachView() 綁定View引用。
  • detachView 斷開View引用。
  • isViewAttached() 判斷View引用是否存在。

其中attachView()detachView()是為Activity准備的,isViewAttached()作用是Presenter內部每次調用View接口中的方法是判斷View 的引用是否存在。

把綁定View的方法寫到Activity的生命周期中:

  1. public class MainActivity extends Activity implements MvpView{
  2. MvpPresenter presenter;
  3. @Override
  4. protected void onCreate(Bundle savedInstanceState) {
  5. super.onCreate(savedInstanceState);
  6. setContentView(R.layout.activity_main);
  7. //初始化Presenter
  8. presenter = new MvpPresenter();
  9. // 綁定View引用
  10. presenter.attachView(this);
  11. }
  12. @Override
  13. protected void onDestroy() {
  14. super.onDestroy();
  15. // 斷開View引用
  16. presenter.detachView();
  17. }
  18. }

結合Activity構建base層

寫到這里,相信大多數人都會驚訝於MVP模式代碼量的巨大,冗余代碼實在太多,所以接下需要為MVP中所有單元都設計一個頂級父類來減少重復的冗余代碼。同樣的道理,我們也為Activity設計一個父類方便與MVP架構更完美的結合。最后將所有父類單獨分到一個base包中供外界繼承調用。

Callback

在乞丐版中Callback接口中的onSuccess()方法需要根據請求數據類型的不同設置為不同類型的參數,所以每當有新的數據類型都需要新建一個Callback,解決方法是引入泛型的概念,用調用者去定義具體想要接收的數據類型:

  1. public interface Callback<T> {
  2. /**
  3. * 數據請求成功
  4. * @param data 請求到的數據
  5. */
  6. void onSuccess(T data);
  7. /**
  8. * 使用網絡API接口請求方式時,雖然已經請求成功但是由
  9. * 於{@code msg}的原因無法正常返回數據。
  10. */
  11. void onFailure(String msg);
  12. /**
  13. * 請求數據失敗,指在請求網絡API接口請求方式時,出現無法聯網、
  14. * 缺少權限,內存泄露等原因導致無法連接到請求數據源。
  15. */
  16. void onError();
  17. /**
  18. * 當請求數據結束時,無論請求結果是成功,失敗或是拋出異常都會執行此方法給用戶做處理,通常做網絡
  19. * 請求時可以在此處隱藏“正在加載”的等待控件
  20. */
  21. void onComplete();
  22. }

BaseView

View接口中定義Activity的UI邏輯。因為有很多方法幾乎在每個Activity中都會用到,例如顯示和隱藏正在加載進度條,顯示Toast提示等,索性將這些方法變成通用的:

  1. public interface BaseView {
  2. /**
  3. * 顯示正在加載view
  4. */
  5. void showLoading();
  6. /**
  7. * 關閉正在加載view
  8. */
  9. void hideLoading();
  10. /**
  11. * 顯示提示
  12. * @param msg
  13. */
  14. void showToast(String msg);
  15. /**
  16. * 顯示請求錯誤提示
  17. */
  18. void showErr();
  19. /**
  20. * 獲取上下文
  21. * @return 上下文
  22. */
  23. Context getContext();
  24. }

BasePresenter

Presenter中可共用的代碼就是對View引用的方法了,值得注意的是,上面已經定義好了BaseView,所以我們希望Presenter中持有的View都是BaseView的子類,這里同樣需要泛型來約束:

  1. public class BasePresenter<V extends IBaseView> {
  2. /**
  3. * 綁定的view
  4. */
  5. private V mvpView;
  6. /**
  7. * 綁定view,一般在初始化中調用該方法
  8. */
  9. @Override
  10. public void attachView(V mvpView) {
  11. this.mvpView = mvpView;
  12. }
  13. /**
  14. * 斷開view,一般在onDestroy中調用
  15. */
  16. @Override
  17. public void detachView() {
  18. this.mvpView = null;
  19. }
  20. /**
  21. * 是否與View建立連接
  22. * 每次調用業務請求的時候都要出先調用方法檢查是否與View建立連接
  23. */
  24. public boolean isViewAttached(){
  25. return mvpView != null;
  26. }
  27. /**
  28. * 獲取連接的view
  29. */
  30. public V getView(){
  31. return mvpView;
  32. }

BaseActivity

BaseActivity主要是負責實現 BaseView 中通用的UI邏輯方法,如此這些通用的方法就不用每個Activity都要去實現一遍了。

  1. public abstract class BaseActivity extends Activity implements IBaseView {
  2. private ProgressDialog mProgressDialog;
  3. @Override
  4. protected void onCreate(Bundle savedInstanceState) {
  5. super.onCreate(savedInstanceState);
  6. mProgressDialog = new ProgressDialog(this);
  7. mProgressDialog.setCancelable(false);
  8. }
  9. @Override
  10. public void showLoading() {
  11. if (!mProgressDialog.isShowing()) {
  12. mProgressDialog.show();
  13. }
  14. }
  15. @Override
  16. public void hideLoading() {
  17. if (mProgressDialog.isShowing()) {
  18. mProgressDialog.dismiss();
  19. }
  20. }
  21. @Override
  22. public void showToast(String msg) {
  23. Toast.makeText(this, msg, Toast.LENGTH_SHORT).show();
  24. }
  25. @Override
  26. public void showErr() {
  27. showToast(getResources().getString(R.string.api_error_msg));
  28. }
  29. @Override
  30. public Context getContext() {
  31. return BaseActivity.this;
  32. }

平民版MVP架構代碼實現

封裝好了base層我們的平民版MVP架構就完成了,下面再來實現一遍之前用乞丐版MVP實現的應用。

Model

  1. public class MvpModel {
  2. /**
  3. * 獲取網絡接口數據
  4. * @param param 請求參數
  5. * @param callback 數據回調接口
  6. */
  7. public static void getNetData(final String param, final MvpCallback<String> callback){
  8. // 利用postDelayed方法模擬網絡請求數據的耗時操作
  9. new Handler().postDelayed(new Runnable() {
  10. @Override
  11. public void run() {
  12. switch (param){
  13. case "normal":
  14. callback.onSuccess("根據參數"+param+"的請求網絡數據成功");
  15. break;
  16. case "failure":
  17. callback.onFailure("請求失敗:參數有誤");
  18. break;
  19. case "error":
  20. callback.onError();
  21. break;
  22. }
  23. callback.onComplete();
  24. }
  25. },2000);
  26. }
  27. }

View 接口

  1. public interface MvpView extends BaseView{
  2. /**
  3. * 當數據請求成功后,調用此接口顯示數據
  4. * @param data 數據源
  5. */
  6. void showData(String data);
  7. }

Presenter類

  1. public class MvpPresenter extends BasePresenter<MvpView > {
  2. /**
  3. * 獲取網絡數據
  4. * @param params 參數
  5. */
  6. public void getData(String params){
  7. if (!isViewAttached()){
  8. //如果沒有View引用就不加載數據
  9. return;
  10. }
  11. //顯示正在加載進度條
  12. getView().showLoading();
  13. // 調用Model請求數據
  14. MvpModel.getNetData(params, new MvpCallback()<String> {
  15. @Override
  16. public void onSuccess(String data) {
  17. //調用view接口顯示數據
  18. if(isViewAttached()){
  19. getView().showData(data);
  20. }
  21. }
  22. @Override
  23. public void onFailure(String msg) {
  24. //調用view接口提示失敗信息
  25. if(isViewAttached()){
  26. getView().showToast(msg);
  27. }
  28. }
  29. @Override
  30. public void onError() {
  31. //調用view接口提示請求異常
  32. if(isViewAttached()){
  33. getView().showErr();
  34. }
  35. }
  36. @Override
  37. public void onComplete() {
  38. // 隱藏正在加載進度條
  39. if(isViewAttached()){
  40. getView().hideLoading();
  41. }
  42. }
  43. });
  44. }
  45. }

Activity

  1. public class MainActivity extends BaseActivity implements MvpView {
  2. TextView text;
  3. MvpPresenter presenter;
  4. @Override
  5. protected void onCreate(Bundle savedInstanceState) {
  6. super.onCreate(savedInstanceState);
  7. setContentView(R.layout.activity_main);
  8. text = (TextView)findViewById(R.id.text);
  9. //初始化Presenter
  10. presenter = new MvpPresenter();
  11. presenter.attachView(this);
  12. }
  13. @Override
  14. protected void onDestroy() {
  15. super.onDestroy();
  16. //斷開View引用
  17. presenter.detachView();
  18. }
  19. @Override
  20. public void showData(String data) {
  21. text.setText(data);
  22. }
  23. // button 點擊事件調用方法
  24. public void getData(View view){
  25. presenter.getData("normal");
  26. }
  27. // button 點擊事件調用方法
  28. public void getDataForFailure(View view){
  29. presenter.getData("failure");
  30. }
  31. // button 點擊事件調用方法
  32. public void getDataForError(View view){
  33. presenter.getData("error");
  34. }
  35. }

Fragment怎么辦?

日常開發中,並不是所有的UI處理都在Activity中進行,Fragment也是其中很重要的一員,那么如何將Fragment結合到MVP中呢?

實現BaseFragement做法跟BaseActivity很類似,需要注意一下Fragement與宿主Activity的鏈接情況就可以。

  1. public abstract class BaseFragment extends Fragment implements BaseView {
  2. public abstract int getContentViewId();
  3. protected abstract void initAllMembersView(Bundle savedInstanceState);
  4. protected Context mContext;
  5. protected View mRootView;
  6. @Nullable
  7. @Override
  8. public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
  9. mRootView = inflater.inflate(getContentViewId(), container, false);
  10. this.mContext = getActivity();
  11. initAllMembersView(savedInstanceState);
  12. return mRootView;
  13. }
  14. @Override
  15. public void showLoading() {
  16. checkActivityAttached();
  17. ((BaseFragmentActivity) mContext).showLoading();
  18. }
  19. @Override
  20. public void showLoading(String msg) {
  21. checkActivityAttached();
  22. ((BaseFragmentActivity) mContext).showLoading(msg);
  23. }
  24. @Override
  25. public void hideLoading() {
  26. checkActivityAttached();
  27. ((BaseFragmentActivity) mContext).hideLoading();
  28. }
  29. @Override
  30. public void showToast(String msg) {
  31. checkActivityAttached();
  32. ((BaseFragmentActivity) mContext).showToast(msg);
  33. }
  34. @Override
  35. public void showErr() {
  36. checkActivityAttached();
  37. ((BaseFragmentActivity) mContext).showErr();
  38. }
  39. protected boolean isAttachedContext(){
  40. return getActivity() != null;
  41. }
  42. /**
  43. * 檢查activity連接情況
  44. */
  45. public void checkActivityAttached() {
  46. if (getActivity() == null) {
  47. throw new ActivityNotAttachedException();
  48. }
  49. }
  50. public static class ActivityNotAttachedException extends RuntimeException {
  51. public ActivityNotAttachedException() {
  52. super("Fragment has disconnected from Activity ! - -.");
  53. }
  54. }
  55. }

時尚版MVP架構 - Model層的單獨優化

在從乞丐版MVP架構優化成平民版MVP架構的過程中,幾乎每個單元都做了很大優化並封裝到了base層,但是唯獨Model層沒什么變化。所以,時尚版MVP架構的優化主要就是對Model層的優化。

Model層相比其他單元來說比較特殊,因為它們更像一個整體,只是單純的幫上層拿數據而已。再就是MVP的理念是讓業務邏輯互相獨立,這就導致每個的網絡請求也被獨立成了單個Model,不光沒必要這么做而且找起來賊麻煩,所以時尚版MVP架構中Model層被整體封裝成了龐大且獨立單一模塊。

時尚版MVP架構圖

優化之后的Model層是一個龐大而且獨立的模塊,對外提供統一的請求數據方法與請求規則,這樣做的好處有很多:

  • 數據請求單獨編寫,無需配合上層界面測試。
  • 統一管理,修改方便。
  • 實現不同數據源(NetAAPI,cache,database)的無縫切換。

寫到這里本片文章實在是太長了,所以時尚版MVP架構的實現就留到下片文章繼續把。

未完待續

下篇會完善時尚版MVP架構,以及最新的旗艦版MVP架構設計,敬請期待~

最后

硬廣一波最新開發的RecyclerView通用集合適配器SuperAdapter,它的作用是幫助開發者快速構建RecyclerView的Adapter,並封裝了許多常用功能,非常好用。之前有人跟我說已經有好多人做過這個了,我想說的是我很感謝之前做過這些的大佬們,是他們給了我靈感和思路,但是不得不承認目前網上的類似項目都存在問題,實際開發中會有些問題,我在前人的思路上進行了大幅度優化,意圖打造一個既好用又實用的工具。

目前這個項目還沒有全部完成,還有許多實用的功能可以開發,有興趣的朋友可以一起搞啊。最后,文章鏈接傳送門:RecyclerView多功能集合適配器:SuperAdapter


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM