自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

Google官方MVP示例代碼閱讀筆記

移動(dòng)開(kāi)發(fā) Android
Google這個(gè)示例項(xiàng)目,架構(gòu)非常的清晰,也是很標(biāo)準(zhǔn)的MVP模式,項(xiàng)目中解耦做的也非常好。但是相對(duì)于一個(gè)功能簡(jiǎn)單的應(yīng)用來(lái)說(shuō),代碼量還是比較多的。當(dāng)然,因?yàn)檫@只是一個(gè)小例子而已,可能會(huì)讓人覺(jué)得反而不如普通的MVC來(lái)開(kāi)發(fā)方便,但是人無(wú)遠(yuǎn)慮必有近憂。

剝絲抽繭,理清項(xiàng)目結(jié)構(gòu)

國(guó)際慣例,上項(xiàng)目結(jié)構(gòu)圖:

Google官方MVP示例代碼閱讀筆記

從包名上很容易分辨出功能:addedittask是添加任務(wù),data是數(shù)據(jù)管理,statistics是統(tǒng)計(jì),taskdetail是任務(wù)詳情,tasks是任務(wù)瀏覽之類的。事實(shí)上這個(gè)項(xiàng)目的關(guān)鍵也就是: Tasks 、 TaskDetail 、 AddEditTask 、 Statistics 。

這四個(gè)關(guān)鍵的地方都有相同之處:

  • 定義了view和presenter的契約
  • Activity負(fù)責(zé)fragment和presenter的創(chuàng)建
  • Fragment實(shí)現(xiàn)了view接口
  • presenter實(shí)現(xiàn)了presenter接口

也就是說(shuō),幾個(gè)功能每一個(gè)都是MVP的模式,只不過(guò)Model層是公用的。而且這個(gè)項(xiàng)目里View層都是Fragment,果然google推薦用Fragment自己的項(xiàng)目里也給我們做個(gè)示范……其實(shí)關(guān)于到底是不是要用Fragment,還是有些爭(zhēng)議的,那么到底要不要用呢?我覺(jué)得對(duì)于個(gè)體而言,不管你喜不喜歡,都要用一用,試一試,因?yàn)槿艘砷L(zhǎng),必須踩坑。對(duì)于正式項(xiàng)目而言,則需要綜合考量,使用Fragment的利是否大于弊。

扯遠(yuǎn)了,接下來(lái)看一下他代碼倉(cāng)庫(kù)給的一張結(jié)構(gòu)圖:

Google官方MVP示例代碼閱讀筆記

可以看出來(lái)左邊是數(shù)據(jù)管理,典型的Model層。而右邊呢,你可能認(rèn)為Activity是Presenter,事實(shí)上并不是,Presenter在Activity內(nèi),F(xiàn)ragment是View無(wú)疑。到這,我覺(jué)得關(guān)于這個(gè)項(xiàng)目結(jié)構(gòu)的簡(jiǎn)介已經(jīng)足夠了,接下來(lái)看代碼。

我覺(jué)得看一個(gè)Android項(xiàng)目的正確姿勢(shì)應(yīng)該是先把玩一下app,看一下功能。貼幾張app的圖:

Google官方MVP示例代碼閱讀筆記

Google官方MVP示例代碼閱讀筆記

Google官方MVP示例代碼閱讀筆記

Google官方MVP示例代碼閱讀筆記

接著就該上入口的Activity看一下了,這個(gè)項(xiàng)目的入口Activity是TasksActivity,所在的包是tasks,看一下有哪些東西:

Google官方MVP示例代碼閱讀筆記

***個(gè)是自定義View,第二個(gè)就是入口Activity了,第三個(gè)即上面所說(shuō)的“契約”,里面包含了View接口和Presenter接口。TasksFilterType則是一個(gè)枚舉,里面有三個(gè)過(guò)濾類型:所有,進(jìn)行中的,完成的。TasksFragment就是MVP中的View了,TasksPresenter則是MVP中的Presenter了??匆幌耇asksActivity中的初始化代碼:

 

  1.   protected void onCreate(Bundle savedInstanceState) { 
  2.         super.onCreate(savedInstanceState); 
  3.         setContentView(R.layout.tasks_act); 
  4.         Log.e(getClass().getSimpleName(),"onCreate"); 
  5.  
  6.         // Set up the toolbar. 
  7.         Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar); 
  8.         setSupportActionBar(toolbar); 
  9.         ActionBar ab = getSupportActionBar(); 
  10.         ab.setHomeAsUpIndicator(R.drawable.ic_menu); 
  11.         ab.setDisplayHomeAsUpEnabled(true); 
  12.  
  13.         /** 
  14.          * 以下的DrawerLayout暫時(shí)不看了 
  15.          */ 
  16.         // Set up the navigation drawer. 
  17.         mDrawerLayout = (DrawerLayout) findViewById(R.id.drawer_layout); 
  18.         mDrawerLayout.setStatusBarBackground(R.color.colorPrimaryDark); 
  19.         NavigationView navigationView = (NavigationView) findViewById(R.id.nav_view); 
  20.         if (navigationView != null) { 
  21.             setupDrawerContent(navigationView); 
  22.         } 
  23.  
  24.         // 獲取fragment并將之添加到視圖上 
  25.         // 懸浮按鈕在這個(gè)taksFragment里設(shè)置的點(diǎn)擊事件 
  26.         TasksFragment tasksFragment = 
  27.                 (TasksFragment) getSupportFragmentManager().findFragmentById(R.id.contentFrame); 
  28. //        getSupportFragmentManager().findFragmentById() 
  29.         if (tasksFragment == null) { 
  30.             // Create the fragment 
  31.             tasksFragment = TasksFragment.newInstance(); 
  32.             // 提供方法幫助activity加載ui 
  33.             // 這個(gè)方法其實(shí)就是拿到一個(gè)事務(wù),然后把這個(gè)fragment add到對(duì)應(yīng)的id上了 
  34.             ActivityUtils.addFragmentToActivity( 
  35.                     getSupportFragmentManager(), tasksFragment, R.id.contentFrame); 
  36.         } 
  37.  
  38.         // Create the presenter 
  39.         mTasksPresenter = new TasksPresenter( 
  40.                 Injection.provideTasksRepository(getApplicationContext()), tasksFragment); 
  41.  
  42.         // Load previously saved state, if available. 
  43.         if (savedInstanceState != null) { 
  44.             TasksFilterType currentFiltering = 
  45.                     (TasksFilterType) savedInstanceState.getSerializable(CURRENT_FILTERING_KEY); 
  46.             mTasksPresenter.setFiltering(currentFiltering); 
  47.         } 
  48.     } 

首先是初始化toolbar和側(cè)滑,這里不必深入細(xì)節(jié),可以跳過(guò)這倆。之后初始化fragment和presenter,初始化Fragment先是嘗試通過(guò)id尋找可能已經(jīng)存在的Fragment對(duì)象,如果沒(méi)有,則重新創(chuàng)建一個(gè)Fragment對(duì)象。下一步則是創(chuàng)建一個(gè)presenter,***則是讓應(yīng)用在橫豎屏狀態(tài)切換的情況下恢復(fù)數(shù)據(jù)。

接下來(lái)看一下View和Presenter的“契約”:

 

  1. public interface TasksContract { 
  2.  
  3.     interface View extends BaseView<Presenter> { 
  4.  
  5.         void setLoadingIndicator(boolean active); 
  6.  
  7.         void showTasks(List<Task> tasks); 
  8.  
  9.         void showAddTask(); 
  10.  
  11.         void showTaskDetailsUi(String taskId); 
  12.  
  13.         void showTaskMarkedComplete(); 
  14.  
  15.         void showTaskMarkedActive(); 
  16.  
  17.         void showCompletedTasksCleared(); 
  18.  
  19.         void showLoadingTasksError(); 
  20.  
  21.         void showNoTasks(); 
  22.  
  23.         void showActiveFilterLabel(); 
  24.  
  25.         void showCompletedFilterLabel(); 
  26.  
  27.         void showAllFilterLabel(); 
  28.  
  29.         void showNoActiveTasks(); 
  30.  
  31.         void showNoCompletedTasks(); 
  32.  
  33.         void showSuccessfullySavedMessage(); 
  34.  
  35.         boolean isActive(); 
  36.  
  37.         void showFilteringPopUpMenu(); 
  38.     } 
  39.  
  40.     interface Presenter extends BasePresenter { 
  41.  
  42.         void result(int requestCode, int resultCode); 
  43.  
  44.         void loadTasks(boolean forceUpdate); 
  45.  
  46.         void addNewTask(); 
  47.  
  48.         void openTaskDetails(@NonNull Task requestedTask); 
  49.  
  50.         void completeTask(@NonNull Task completedTask); 
  51.  
  52.         void activateTask(@NonNull Task activeTask); 
  53.  
  54.         void clearCompletedTasks(); 
  55.  
  56.         void setFiltering(TasksFilterType requestType); 
  57.  
  58.         TasksFilterType getFiltering(); 
  59.     } 

這個(gè)接口里包含了View和Presenter,可以看到View和Presenter里的方法比較多,事實(shí)上這是應(yīng)該的。因?yàn)樵贛VP架構(gòu)里,View只負(fù)責(zé)根據(jù)Presenter的指示繪制UI,View將所有的用戶交互交給Presenter處理。所以Presenter的很多方法可能就是對(duì)用戶的輸入的處理,而有輸入必然有輸出,View接口定義的各個(gè)方法便是給Presenter回調(diào)的。Presenter通過(guò)回調(diào)函數(shù)將對(duì)用戶的輸入的處理結(jié)果推到View中,View再根據(jù)這個(gè)結(jié)果對(duì)UI進(jìn)行相應(yīng)的更新。而在此項(xiàng)目中,F(xiàn)ragment就是View,在Fragment的各個(gè)點(diǎn)擊事件中都調(diào)用了Presenter的對(duì)應(yīng)方法,將業(yè)務(wù)邏輯交給Presenter處理。這看起來(lái)比傳統(tǒng)的MVC強(qiáng)上很多,因?yàn)閭鹘y(tǒng)MVC中Activity既可以認(rèn)為是Controller亦可以認(rèn)為是View,職責(zé)難以分離,寫到后面可能一個(gè)Activity就有上千行的代碼,這會(huì)為后續(xù)的維護(hù)帶來(lái)不少麻煩。而MVP則將業(yè)務(wù)邏輯抽取到了Presenter中,作為View的Fragment或者Activity職責(zé)更加單一,無(wú)疑為后續(xù)的開(kāi)發(fā)維護(hù)帶來(lái)了便利。

接下來(lái)詳細(xì)的看Presenter的初始化,Presenter的創(chuàng)建是在TasksActivity中完成的,查看其構(gòu)造函數(shù):

 

  1. public TasksPresenter(@NonNull TasksRepository tasksRepository, @NonNull TasksContract.View tasksView) { 
  2.       mTasksRepository = checkNotNull(tasksRepository, "tasksRepository cannot be null"); 
  3.       mTasksView = checkNotNull(tasksView, "tasksView cannot be null!"); 
  4.  
  5.       mTasksView.setPresenter(this); 
  6.   } 

前兩個(gè)檢查傳入的參數(shù)是否為空,接著將其賦值給TasksPresenter內(nèi)的引用,調(diào)用view的setPresenter方法,將自身傳入,這樣view中就可以使用presenter對(duì)象了,比直接從activity中拿看起來(lái)要優(yōu)雅了不少。Presenter具體的邏輯就不看了,都是一些比較簡(jiǎn)單的代碼,回顧一下打開(kāi)這個(gè)app所發(fā)生的事件的流程:創(chuàng)建TasksActivity -> 初始化Toolbar -> 初始化側(cè)滑 -> 創(chuàng)建TasksFragment對(duì)象 -> 創(chuàng)建TaskPresenter對(duì)象 -> 給Fragment設(shè)置Presenter對(duì)象 -> 初始化Fragment布局,這樣一套流程下來(lái),整個(gè)流程就理清了,接下來(lái)只是等待用戶的輸入了。

接下來(lái)要看的是從本文開(kāi)始到現(xiàn)在都一直忽略了的Model:TasksRepository。不過(guò)在分析TasksRepository之前,安利一下這個(gè)項(xiàng)目里的實(shí)體類,寫的比較優(yōu)雅,我們平時(shí)寫實(shí)體類時(shí)***也能按照他的套路來(lái)寫。我為什么說(shuō)他寫的比較優(yōu)雅呢?因?yàn)楦鱾€(gè)屬性或者是帶返回值的方法都打上了@Nullable或者@NoNull注解來(lái)說(shuō)明是否可以為空,事實(shí)上空指針這個(gè)錯(cuò)可以算是平時(shí)經(jīng)常遇到的錯(cuò)了……不過(guò)如果你有良好的設(shè)計(jì)和編碼習(xí)慣,是可以避免的,帶上這兩個(gè)注解可以在編譯期給你相關(guān)的提示。不僅如此,這個(gè)實(shí)體類還復(fù)寫了equals()、hashCode()和toString()方法,而且實(shí)現(xiàn)的方式也符合規(guī)范,關(guān)于如何復(fù)寫這三個(gè)方法,在《effective java》上有很好的總結(jié),各位可以去讀一下。

 

  1. /* 
  2.  * Copyright 2016, The Android Open Source Project 
  3.  * 
  4.  * Licensed under the Apache License, Version 2.0 (the "License"); 
  5.  * you may not use this file except in compliance with the License. 
  6.  * You may obtain a copy of the License at 
  7.  * 
  8.  *      http://www.apache.org/licenses/LICENSE-2.0 
  9.  * 
  10.  * Unless required by applicable law or agreed to in writing, software 
  11.  * distributed under the License is distributed on an "AS IS" BASIS, 
  12.  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 
  13.  * See the License for the specific language governing permissions and 
  14.  * limitations under the License. 
  15.  */ 
  16.  
  17. package com.example.android.architecture.blueprints.todoapp.data; 
  18.  
  19. import android.support.annotation.NonNull; 
  20. import android.support.annotation.Nullable; 
  21.  
  22. import com.google.common.base.Objects; 
  23. import com.google.common.base.Strings; 
  24.  
  25. import java.util.UUID; 
  26.  
  27. /** 
  28.  * Immutable model class for a Task. 
  29.  */ 
  30. public final class Task { 
  31.  
  32.     @NonNull 
  33.     private final String mId; 
  34.  
  35.     @Nullable 
  36.     private final String mTitle; 
  37.  
  38.     @Nullable 
  39.     private final String mDescription; 
  40.  
  41.     private final boolean mCompleted; 
  42.  
  43.     /** 
  44.      * Use this constructor to create a new active Task. 
  45.      * 
  46.      * @param title       title of the task 
  47.      * @param description description of the task 
  48.      */ 
  49.     public Task(@Nullable String title, @Nullable String description) { 
  50.         this(title, description, UUID.randomUUID().toString(), false); 
  51.     } 
  52.  
  53.     /** 
  54.      * Use this constructor to create an active Task if the Task already has an id (copy of another 
  55.      * Task). 
  56.      * 
  57.      * @param title       title of the task 
  58.      * @param description description of the task 
  59.      * @param id          id of the task 
  60.      */ 
  61.     public Task(@Nullable String title, @Nullable String description, @NonNull String id) { 
  62.         this(title, description, id, false); 
  63.     } 
  64.  
  65.     /** 
  66.      * Use this constructor to create a new completed Task. 
  67.      * 
  68.      * @param title       title of the task 
  69.      * @param description description of the task 
  70.      * @param completed   true if the task is completed, false if it's active 
  71.      */ 
  72.     public Task(@Nullable String title, @Nullable String description, boolean completed) { 
  73.         this(title, description, UUID.randomUUID().toString(), completed); 
  74.     } 
  75.  
  76.     /** 
  77.      * Use this constructor to specify a completed Task if the Task already has an id (copy of 
  78.      * another Task). 
  79.      * 
  80.      * @param title       title of the task 
  81.      * @param description description of the task 
  82.      * @param id          id of the task 
  83.      * @param completed   true if the task is completed, false if it's active 
  84.      */ 
  85.     public Task(@Nullable String title, @Nullable String description, 
  86.                 @NonNull String id, boolean completed) { 
  87.         mId = id; 
  88.         mTitle = title; 
  89.         mDescription = description; 
  90.         mCompleted = completed; 
  91.     } 
  92.  
  93.     @NonNull 
  94.     public String getId() { 
  95.         return mId; 
  96.     } 
  97.  
  98.     @Nullable 
  99.     public String getTitle() { 
  100.         return mTitle; 
  101.     } 
  102.  
  103.     @Nullable 
  104.     public String getTitleForList() { 
  105.         if (!Strings.isNullOrEmpty(mTitle)) { 
  106.             return mTitle; 
  107.         } else { 
  108.             return mDescription; 
  109.         } 
  110.     } 
  111.  
  112.     @Nullable 
  113.     public String getDescription() { 
  114.         return mDescription; 
  115.     } 
  116.  
  117.     public boolean isCompleted() { 
  118.         return mCompleted; 
  119.     } 
  120.  
  121.     public boolean isActive() { 
  122.         return !mCompleted; 
  123.     } 
  124.  
  125.     public boolean isEmpty() { 
  126.         return Strings.isNullOrEmpty(mTitle) &amp;&amp; 
  127.                Strings.isNullOrEmpty(mDescription); 
  128.     } 
  129.  
  130.     @Override 
  131.     public boolean equals(Object o) { 
  132.         if (this == o) return true
  133.         if (o == null || getClass() != o.getClass()) return false
  134.         Task task = (Task) o; 
  135.         return Objects.equal(mId, task.mId) &amp;&amp; 
  136.                Objects.equal(mTitle, task.mTitle) &amp;&amp; 
  137.                Objects.equal(mDescription, task.mDescription); 
  138.     } 
  139.  
  140.     @Override 
  141.     public int hashCode() { 
  142.         return Objects.hashCode(mId, mTitle, mDescription); 
  143.     } 
  144.  
  145.     @Override 
  146.     public String toString() { 
  147.         return "Task with title " + mTitle; 
  148.     } 

先看一下TasksRepository所在的包的結(jié)構(gòu):

Google官方MVP示例代碼閱讀筆記

可以從包名上看出local是從本地讀取數(shù)據(jù),remote是遠(yuǎn)程讀取,當(dāng)然了,這里只是模擬遠(yuǎn)程讀取。本地采用了數(shù)據(jù)庫(kù)存取的方式。在TasksRepository(下文簡(jiǎn)稱TR)內(nèi)部有兩個(gè)TasksDataSource的引用:

 

  1. private final TasksDataSource mTasksRemoteDataSource; 
  2.   private final TasksDataSource mTasksLocalDataSource; 

TasksDataSource是data包內(nèi)的一個(gè)接口,使用接口引用,無(wú)非是想解耦,就算以后需求變更,不想采用數(shù)據(jù)庫(kù)的方式存儲(chǔ)數(shù)據(jù),只要實(shí)現(xiàn)了這個(gè)接口,TR內(nèi)部的代碼也無(wú)需變更。TR用了單例,實(shí)現(xiàn)方式并不是線程安全的:

 

  1. /** 
  2.     * Returns the single instance of this class, creating it if necessary. 
  3.     * 
  4.     * @param tasksRemoteDataSource the backend data source 
  5.     * @param tasksLocalDataSource  the device storage data source 
  6.     * @return the {@link TasksRepository} instance 
  7.     */ 
  8.    public static TasksRepository getInstance(TasksDataSource tasksRemoteDataSource, 
  9.                                              TasksDataSource tasksLocalDataSource) { 
  10.        if (INSTANCE == null) { 
  11.            INSTANCE = new TasksRepository(tasksRemoteDataSource, tasksLocalDataSource); 
  12.        } 
  13.        return INSTANCE; 
  14.    } 

說(shuō)到底,他根本沒(méi)有線程安全的必要,至少在這個(gè)app里,沒(méi)有并發(fā)創(chuàng)建這個(gè)對(duì)象的場(chǎng)景,所以夠用就行了。在TR內(nèi)部使用了一個(gè)LinkedHashMap作為容器來(lái)保存Tasks,主要看一下兩個(gè)方法,首先是存儲(chǔ):

 

  1. public void saveTask(@NonNull Task task) { 
  2.        checkNotNull(task); 
  3.        mTasksRemoteDataSource.saveTask(task); 
  4.        mTasksLocalDataSource.saveTask(task); 
  5.  
  6.        // Do in memory cache update to keep the app UI up to date 
  7.        if (mCachedTasks == null) { 
  8.            mCachedTasks = new LinkedHashMap<>(); 
  9.        } 
  10.        mCachedTasks.put(task.getId(), task); 
  11.    } 

會(huì)將傳入的task存儲(chǔ)到遠(yuǎn)程數(shù)據(jù)源和本地?cái)?shù)據(jù)源(本地?cái)?shù)據(jù)庫(kù))中,然后將這個(gè)task傳到mCachedTasks(LinkedHashMap)中。代碼比較簡(jiǎn)單,不做更多的分析,接下來(lái)看一下讀取Task:

 

  1. public void getTasks(@NonNull final LoadTasksCallback callback) { 
  2.        checkNotNull(callback); 
  3.  
  4.        // Respond immediately with cache if available and not dirty 
  5.        if (mCachedTasks != null &amp;&amp; !mCacheIsDirty) { 
  6.            callback.onTasksLoaded(new ArrayList<>(mCachedTasks.values())); 
  7.            return
  8.        } 
  9.  
  10.        if (mCacheIsDirty) { 
  11.            // If the cache is dirty we need to fetch new data from the network. 
  12.            getTasksFromRemoteDataSource(callback); 
  13.        } else { 
  14.            // Query the local storage if available. If not, query the network. 
  15.            mTasksLocalDataSource.getTasks(new LoadTasksCallback() { 
  16.                @Override 
  17.                public void onTasksLoaded(List<Task> tasks) { 
  18.                    refreshCache(tasks); 
  19.                    callback.onTasksLoaded(new ArrayList<>(mCachedTasks.values())); 
  20.                } 
  21.  
  22.                @Override 
  23.                public void onDataNotAvailable() { 
  24.                    getTasksFromRemoteDataSource(callback); 
  25.                } 
  26.            }); 
  27.        } 
  28.    } 

這個(gè)taskId是需要獲取Task的id,也是唯一標(biāo)識(shí),GetTaskCallback則是負(fù)責(zé)傳遞數(shù)據(jù)的接口回調(diào)。首先是從內(nèi)存中讀取數(shù)據(jù),getTaskWithId方法就是,看一下代碼:

 

  1. private Task getTaskWithId(@NonNull String id) { 
  2.        checkNotNull(id); 
  3.        if (mCachedTasks == null || mCachedTasks.isEmpty()) { 
  4.            return null
  5.        } else { 
  6.            return mCachedTasks.get(id); 
  7.        } 
  8.    } 

就從保存task的LinkedHashMap中讀取數(shù)據(jù)。如果這個(gè)過(guò)程讀取不到數(shù)據(jù)那么接著從本地?cái)?shù)據(jù)源中讀取數(shù)據(jù),如果本地?cái)?shù)據(jù)源也沒(méi)有拿到這個(gè)數(shù)據(jù),那么最終就從遠(yuǎn)程數(shù)據(jù)源中讀取數(shù)據(jù)。

至此,我們簡(jiǎn)單的過(guò)了一遍這個(gè)項(xiàng)目。

總結(jié) & 再談MVP

Google這個(gè)示例項(xiàng)目,架構(gòu)非常的清晰,也是很標(biāo)準(zhǔn)的MVP模式,項(xiàng)目中解耦做的也非常好。但是相對(duì)于一個(gè)功能簡(jiǎn)單的應(yīng)用來(lái)說(shuō),代碼量還是比較多的。當(dāng)然,因?yàn)檫@只是一個(gè)小例子而已,可能會(huì)讓人覺(jué)得反而不如普通的MVC來(lái)開(kāi)發(fā)方便,但是人無(wú)遠(yuǎn)慮必有近憂。我們做東西的時(shí)候要盡量做長(zhǎng)遠(yuǎn)的打算,不然以后可能就會(huì)被淹沒(méi)在頻繁的需求變更里了。Google的這個(gè)項(xiàng)目有非常多值得我們學(xué)習(xí)的地方,比如我們寫MVP的時(shí)候也可以用一個(gè)Contract類來(lái)將View和Presenter放入其中,方便我們管理(改代碼)。

我們都知道MVP與MVC的主要區(qū)別是View和Model不直接交互,而是通過(guò)Presenter來(lái)完成交互,這樣可以修改View而不影響Model,實(shí)現(xiàn)了Model和View真正的完全分離。而MVP中將業(yè)務(wù)邏輯抽取放到Presenter中,使各個(gè)模塊的職責(zé)更加清晰,層次明了。而且還有很關(guān)鍵的一點(diǎn),使用MVP架構(gòu)使得應(yīng)用能更加方便的進(jìn)行單元測(cè)試。Android中雖然有很多測(cè)試框架,但是講實(shí)話,你不研究個(gè)一段時(shí)間很難使用那些框架進(jìn)行有效的測(cè)試。而且很多測(cè)試是難以進(jìn)行的,因?yàn)橛械男枰蕾嘇ndroid環(huán)境或者UI環(huán)境。而如果使用了MVP架構(gòu),View層因?yàn)槭怯媒涌诙x的,所以完全可以自己建一個(gè)View模擬視圖對(duì)象,這樣就可以使得我們的測(cè)試不必依賴UI環(huán)境。這樣***的好處就是我們不必花費(fèi)太多的時(shí)間去研究那些測(cè)試框架,也能寫出有效的單元測(cè)試,保證我們代碼的質(zhì)量。

相較于MVP的優(yōu)點(diǎn),其缺點(diǎn)也是非常明顯的,從Google的這個(gè)示例代碼也能看出來(lái),代碼量比較大,小型Android應(yīng)用的開(kāi)發(fā)用這個(gè)反而麻煩。Presenter既負(fù)責(zé)業(yè)務(wù)邏輯,又負(fù)責(zé)Model和View的交互,到后期也難免會(huì)膨脹、臃腫,最終造成這玩意可能維護(hù)起來(lái)也不簡(jiǎn)單。

雖然MVP還是有不足的地方,但是相較于MVC,還是更容易的寫出易維護(hù)、測(cè)試的代碼的,所以各位不妨都閱讀一下Google的這個(gè)代碼~

責(zé)任編輯:未麗燕 來(lái)源: 安卓巴士
相關(guān)推薦

2010-01-25 09:44:17

GoogleChrome詞典擴(kuò)展

2010-06-23 08:56:45

HTML 5GoogleHTML5 ROCKS

2013-12-24 10:05:04

memcached

2012-03-01 09:38:43

GoogleChrome

2011-01-28 10:14:59

Android 3.0

2011-03-23 09:52:40

Chrome新Logo

2020-07-27 10:02:49

Chrome瀏覽器稍后閱讀

2020-08-28 15:28:29

代碼開(kāi)發(fā)工具

2012-10-12 10:30:37

PHPOauth

2009-11-16 09:14:57

GoogleChrome擴(kuò)展中心

2011-08-25 14:38:14

SQL Server修改表結(jié)構(gòu)字段類型

2011-02-16 10:34:48

Chrome擴(kuò)展

2011-05-30 10:30:12

谷歌錢包Google Wall谷歌

2014-06-13 11:25:04

Android 5.0

2012-02-21 09:34:26

Google+中國(guó)正式開(kāi)放

2012-02-01 14:41:13

Android手機(jī)硬件

2016-10-11 16:28:11

源代碼

2010-01-28 16:01:18

Android Jni

2010-05-21 11:07:36

2009-08-11 10:16:27

Google App GAE
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)