這篇文章主要講解了“android MVP示例代碼分析”,文中的講解內(nèi)容簡(jiǎn)單清晰,易于學(xué)習(xí)與理解,下面請(qǐng)大家跟著小編的思路慢慢深入,一起來(lái)研究和學(xué)習(xí)“android MVP示例代碼分析”吧!
讓客戶(hù)滿(mǎn)意是我們工作的目標(biāo),不斷超越客戶(hù)的期望值來(lái)自于我們對(duì)這個(gè)行業(yè)的熱愛(ài)。我們立志把好的技術(shù)通過(guò)有效、簡(jiǎn)單的方式提供給客戶(hù),將通過(guò)不懈努力成為客戶(hù)在信息化領(lǐng)域值得信任、有價(jià)值的長(zhǎng)期合作伙伴,公司提供的服務(wù)項(xiàng)目有:申請(qǐng)域名、虛擬空間、營(yíng)銷(xiāo)軟件、網(wǎng)站建設(shè)、虹口網(wǎng)站維護(hù)、網(wǎng)站推廣。
上項(xiàng)目結(jié)構(gòu)圖:
從包名上很容易分辨出功能:addedittask是添加任務(wù),data是數(shù)據(jù)管理,statistics是統(tǒng)計(jì),taskdetail是任務(wù)詳情,tasks是任務(wù)瀏覽之類(lèi)的。事實(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)圖:
可以看出來(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的圖:
接著就該上入口的Activity看一下了,這個(gè)項(xiàng)目的入口Activity是TasksActivity,所在的包是tasks,看一下有哪些東西:
***個(gè)是自定義View,第二個(gè)就是入口Activity了,第三個(gè)即上面所說(shuō)的“契約”,里面包含了View接口和Presenter接口。TasksFilterType則是一個(gè)枚舉,里面有三個(gè)過(guò)濾類(lèi)型:所有,進(jìn)行中的,完成的。TasksFragment就是MVP中的View了,TasksPresenter則是MVP中的Presenter了??匆幌耇asksActivity中的初始化代碼:
protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.tasks_act); Log.e(getClass().getSimpleName(),"onCreate"); // Set up the toolbar. Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar); setSupportActionBar(toolbar); ActionBar ab = getSupportActionBar(); ab.setHomeAsUpIndicator(R.drawable.ic_menu); ab.setDisplayHomeAsUpEnabled(true); /** * 以下的DrawerLayout暫時(shí)不看了 */ // Set up the navigation drawer. mDrawerLayout = (DrawerLayout) findViewById(R.id.drawer_layout); mDrawerLayout.setStatusBarBackground(R.color.colorPrimaryDark); NavigationView navigationView = (NavigationView) findViewById(R.id.nav_view); if (navigationView != null) { setupDrawerContent(navigationView); } // 獲取fragment并將之添加到視圖上 // 懸浮按鈕在這個(gè)taksFragment里設(shè)置的點(diǎn)擊事件 TasksFragment tasksFragment = (TasksFragment) getSupportFragmentManager().findFragmentById(R.id.contentFrame); // getSupportFragmentManager().findFragmentById() if (tasksFragment == null) { // Create the fragment tasksFragment = TasksFragment.newInstance(); // 提供方法幫助activity加載ui // 這個(gè)方法其實(shí)就是拿到一個(gè)事務(wù),然后把這個(gè)fragment add到對(duì)應(yīng)的id上了 ActivityUtils.addFragmentToActivity( getSupportFragmentManager(), tasksFragment, R.id.contentFrame); } // Create the presenter mTasksPresenter = new TasksPresenter( Injection.provideTasksRepository(getApplicationContext()), tasksFragment); // Load previously saved state, if available. if (savedInstanceState != null) { TasksFilterType currentFiltering = (TasksFilterType) savedInstanceState.getSerializable(CURRENT_FILTERING_KEY); mTasksPresenter.setFiltering(currentFiltering); } }
首先是初始化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,***則是讓?xiě)?yīng)用在橫豎屏狀態(tài)切換的情況下恢復(fù)數(shù)據(jù)。
接下來(lái)看一下View和Presenter的“契約”:
public interface TasksContract { interface View extends BaseView<Presenter> { void setLoadingIndicator(boolean active); void showTasks(List<Task> tasks); void showAddTask(); void showTaskDetailsUi(String taskId); void showTaskMarkedComplete(); void showTaskMarkedActive(); void showCompletedTasksCleared(); void showLoadingTasksError(); void showNoTasks(); void showActiveFilterLabel(); void showCompletedFilterLabel(); void showAllFilterLabel(); void showNoActiveTasks(); void showNoCompletedTasks(); void showSuccessfullySavedMessage(); boolean isActive(); void showFilteringPopUpMenu(); } interface Presenter extends BasePresenter { void result(int requestCode, int resultCode); void loadTasks(boolean forceUpdate); void addNewTask(); void openTaskDetails(@NonNull Task requestedTask); void completeTask(@NonNull Task completedTask); void activateTask(@NonNull Task activeTask); void clearCompletedTasks(); void setFiltering(TasksFilterType requestType); TasksFilterType getFiltering(); } }
這個(gè)接口里包含了View和Presenter,可以看到View和Presenter里的方法比較多,事實(shí)上這是應(yīng)該的。因?yàn)樵贛VP架構(gòu)里,View只負(fù)責(zé)根據(jù)Presenter的指示繪制UI,View將所有的用戶(hù)交互交給Presenter處理。所以Presenter的很多方法可能就是對(duì)用戶(hù)的輸入的處理,而有輸入必然有輸出,View接口定義的各個(gè)方法便是給Presenter回調(diào)的。Presenter通過(guò)回調(diào)函數(shù)將對(duì)用戶(hù)的輸入的處理結(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é)難以分離,寫(xiě)到后面可能一個(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ù):
public TasksPresenter(@NonNull TasksRepository tasksRepository, @NonNull TasksContract.View tasksView) { mTasksRepository = checkNotNull(tasksRepository, "tasksRepository cannot be null"); mTasksView = checkNotNull(tasksView, "tasksView cannot be null!"); mTasksView.setPresenter(this); }
前兩個(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)只是等待用戶(hù)的輸入了。
接下來(lái)要看的是從本文開(kāi)始到現(xiàn)在都一直忽略了的Model:TasksRepository。不過(guò)在分析TasksRepository之前,安利一下這個(gè)項(xiàng)目里的實(shí)體類(lèi),寫(xiě)的比較優(yōu)雅,我們平時(shí)寫(xiě)實(shí)體類(lèi)時(shí)***也能按照他的套路來(lái)寫(xiě)。我為什么說(shuō)他寫(xiě)的比較優(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í)體類(lèi)還復(fù)寫(xiě)了equals()、hashCode()和toString()方法,而且實(shí)現(xiàn)的方式也符合規(guī)范,關(guān)于如何復(fù)寫(xiě)這三個(gè)方法,在《effective java》上有很好的總結(jié),各位可以去讀一下。
/* * Copyright 2016, The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.example.android.architecture.blueprints.todoapp.data; import android.support.annotation.NonNull; import android.support.annotation.Nullable; import com.google.common.base.Objects; import com.google.common.base.Strings; import java.util.UUID; /** * Immutable model class for a Task. */ public final class Task { @NonNull private final String mId; @Nullable private final String mTitle; @Nullable private final String mDescription; private final boolean mCompleted; /** * Use this constructor to create a new active Task. * * @param title title of the task * @param description description of the task */ public Task(@Nullable String title, @Nullable String description) { this(title, description, UUID.randomUUID().toString(), false); } /** * Use this constructor to create an active Task if the Task already has an id (copy of another * Task). * * @param title title of the task * @param description description of the task * @param id id of the task */ public Task(@Nullable String title, @Nullable String description, @NonNull String id) { this(title, description, id, false); } /** * Use this constructor to create a new completed Task. * * @param title title of the task * @param description description of the task * @param completed true if the task is completed, false if it's active */ public Task(@Nullable String title, @Nullable String description, boolean completed) { this(title, description, UUID.randomUUID().toString(), completed); } /** * Use this constructor to specify a completed Task if the Task already has an id (copy of * another Task). * * @param title title of the task * @param description description of the task * @param id id of the task * @param completed true if the task is completed, false if it's active */ public Task(@Nullable String title, @Nullable String description, @NonNull String id, boolean completed) { mId = id; mTitle = title; mDescription = description; mCompleted = completed; } @NonNull public String getId() { return mId; } @Nullable public String getTitle() { return mTitle; } @Nullable public String getTitleForList() { if (!Strings.isNullOrEmpty(mTitle)) { return mTitle; } else { return mDescription; } } @Nullable public String getDescription() { return mDescription; } public boolean isCompleted() { return mCompleted; } public boolean isActive() { return !mCompleted; } public boolean isEmpty() { return Strings.isNullOrEmpty(mTitle) && Strings.isNullOrEmpty(mDescription); } @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; Task task = (Task) o; return Objects.equal(mId, task.mId) && Objects.equal(mTitle, task.mTitle) && Objects.equal(mDescription, task.mDescription); } @Override public int hashCode() { return Objects.hashCode(mId, mTitle, mDescription); } @Override public String toString() { return "Task with title " + mTitle; } }
先看一下TasksRepository所在的包的結(jié)構(gòu):
可以從包名上看出local是從本地讀取數(shù)據(jù),remote是遠(yuǎn)程讀取,當(dāng)然了,這里只是模擬遠(yuǎn)程讀取。本地采用了數(shù)據(jù)庫(kù)存取的方式。在TasksRepository(下文簡(jiǎn)稱(chēng)TR)內(nèi)部有兩個(gè)TasksDataSource的引用:
private final TasksDataSource mTasksRemoteDataSource; 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)方式并不是線(xiàn)程安全的:
/** * Returns the single instance of this class, creating it if necessary. * * @param tasksRemoteDataSource the backend data source * @param tasksLocalDataSource the device storage data source * @return the {@link TasksRepository} instance */ public static TasksRepository getInstance(TasksDataSource tasksRemoteDataSource, TasksDataSource tasksLocalDataSource) { if (INSTANCE == null) { INSTANCE = new TasksRepository(tasksRemoteDataSource, tasksLocalDataSource); } return INSTANCE; }
說(shuō)到底,他根本沒(méi)有線(xiàn)程安全的必要,至少在這個(gè)app里,沒(méi)有并發(fā)創(chuàng)建這個(gè)對(duì)象的場(chǎng)景,所以夠用就行了。在TR內(nèi)部使用了一個(gè)LinkedHashMap作為容器來(lái)保存Tasks,主要看一下兩個(gè)方法,首先是存儲(chǔ):
public void saveTask(@NonNull Task task) { checkNotNull(task); mTasksRemoteDataSource.saveTask(task); mTasksLocalDataSource.saveTask(task); // Do in memory cache update to keep the app UI up to date if (mCachedTasks == null) { mCachedTasks = new LinkedHashMap<>(); } mCachedTasks.put(task.getId(), task); }
會(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:
public void getTasks(@NonNull final LoadTasksCallback callback) { checkNotNull(callback); // Respond immediately with cache if available and not dirty if (mCachedTasks != null && !mCacheIsDirty) { callback.onTasksLoaded(new ArrayList<>(mCachedTasks.values())); return; } if (mCacheIsDirty) { // If the cache is dirty we need to fetch new data from the network. getTasksFromRemoteDataSource(callback); } else { // Query the local storage if available. If not, query the network. mTasksLocalDataSource.getTasks(new LoadTasksCallback() { @Override public void onTasksLoaded(List<Task> tasks) { refreshCache(tasks); callback.onTasksLoaded(new ArrayList<>(mCachedTasks.values())); } @Override public void onDataNotAvailable() { getTasksFromRemoteDataSource(callback); } }); } }
這個(gè)taskId是需要獲取Task的id,也是唯一標(biāo)識(shí),GetTaskCallback則是負(fù)責(zé)傳遞數(shù)據(jù)的接口回調(diào)。首先是從內(nèi)存中讀取數(shù)據(jù),getTaskWithId方法就是,看一下代碼:
private Task getTaskWithId(@NonNull String id) { checkNotNull(id); if (mCachedTasks == null || mCachedTasks.isEmpty()) { return null; } else { return mCachedTasks.get(id); } }
就從保存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ù)。
感謝各位的閱讀,以上就是“android MVP示例代碼分析”的內(nèi)容了,經(jīng)過(guò)本文的學(xué)習(xí)后,相信大家對(duì)android MVP示例代碼分析這一問(wèn)題有了更深刻的體會(huì),具體使用情況還需要大家實(shí)踐驗(yàn)證。這里是創(chuàng)新互聯(lián),小編將為大家推送更多相關(guān)知識(shí)點(diǎn)的文章,歡迎關(guān)注!
網(wǎng)頁(yè)標(biāo)題:androidMVP示例代碼分析
本文鏈接:http://sd-ha.com/article42/gcjghc.html
成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供搜索引擎優(yōu)化、軟件開(kāi)發(fā)、靜態(tài)網(wǎng)站、建站公司、服務(wù)器托管、移動(dòng)網(wǎng)站建設(shè)
聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶(hù)投稿、用戶(hù)轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請(qǐng)盡快告知,我們將會(huì)在第一時(shí)間刪除。文章觀(guān)點(diǎn)不代表本網(wǎng)站立場(chǎng),如需處理請(qǐng)聯(lián)系客服。電話(huà):028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時(shí)需注明來(lái)源: 創(chuàng)新互聯(lián)
全網(wǎng)營(yíng)銷(xiāo)推廣知識(shí)