Network Security Internet Technology Development Database Servers Mobile Phone Android Software Apple Software Computer Software News IT Information

In addition to Weibo, there is also WeChat

Please pay attention

WeChat public account

Shulou

Android MVP sample code analysis

2025-01-18 Update From: SLTechnology News&Howtos shulou NAV: SLTechnology News&Howtos > Development >

Share

Shulou(Shulou.com)06/01 Report--

This article mainly explains the "android MVP sample code analysis", the content of the article is simple and clear, easy to learn and understand, the following please follow the editor's ideas slowly in depth, together to study and learn "android MVP sample code analysis"!

The above project structure diagram:

It is easy to tell the function from the package name: addedittask is the add task, data is the data management, statistics is the statistics, taskdetail is the task details, tasks is the task browsing and so on. In fact, the key to this project is: Tasks, TaskDetail, AddEditTask, Statistics.

All four key points have something in common:

Defines the contract between view and presenter

Activity is responsible for the creation of fragment and presenter

Fragment implements the view interface.

Presenter implements the presenter interface.

In other words, each of several functions is the mode of MVP, except that the Model layer is common. And the View layer in this project is all Fragment, so sure enough, google recommends to use Fragment's own project to give us a demonstration. In fact, there is still some controversy about whether to use Fragment or not, so do you want to use it or not? I think for individuals, whether you like it or not, you should use it and give it a try, because if you want to grow up, you must step on the pit. For formal projects, it is necessary to consider whether the advantages of using Fragment outweigh the disadvantages.

Going too far, let's take a look at a structure diagram given by his code repository:

You can see that on the left is data management, a typical Model layer. On the right, you may think that Activity is Presenter, but in fact it is not. Presenter is in Activity, and Fragment is View. At this point, I think a brief introduction to the structure of the project is enough, let's take a look at the code.

I think the correct posture to look at an Android project is to play app first and take a look at the function. Post a few pictures of app:

Then it's time to take a look at the Activity of the entry. The entry Activity of this project is TasksActivity, and the package is tasks. Take a look at what there are:

* Custom View, the second is the entry Activity, and the third is the "contract" mentioned above, which includes View API and Presenter API. TasksFilterType is an enumeration with three filter types: all, in progress, and completed. TasksFragment is the View of MVP and TasksPresenter is the Presenter of MVP. Take a look at the initialization code in TasksActivity:

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); / * * the following DrawerLayout is temporarily unavailable * / 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) } / / get the fragment and add it to the view / / the click event set by the hover button in this taksFragment TasksFragment tasksFragment = (TasksFragment) getSupportFragmentManager () .findFragmentById (R.id.contentFrame) / / getSupportFragmentManager () .findFragmentById () if (tasksFragment = = null) {/ / Create the fragment tasksFragment = TasksFragment.newInstance () / / provide methods to help activity load ui / / this method actually takes a transaction, and then adds the fragment add to the corresponding id to 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);}}

The first is to initialize toolbar and sideslip, which can be skipped without going into detail here. Then initialize fragment and presenter, initialize Fragment first try to find a Fragment object that may already exist through id, and if not, recreate a Fragment object. The next step is to create a presenter,*** that allows the application to recover data when the horizontal and vertical state is switched.

Next, take a look at the "contract" between View and Presenter:

Public interface TasksContract {interface View extends BaseView {void setLoadingIndicator (boolean active); void showTasks (List 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 ();}}

This interface includes View and Presenter. You can see that there are many methods in View and Presenter. In fact, this should be. Because in the MVP architecture, View is only responsible for drawing UI,View according to the instructions of Presenter and handing over all user interactions to Presenter. Therefore, many methods of Presenter may be to deal with the user's input, and if there is input, there must be output, and each method defined by the View API is a callback to Presenter. Presenter pushes the processing result of the user's input to View through the callback function, and View updates the UI accordingly according to this result. In this project, Fragment is View, and the corresponding method of Presenter is called in each click event of Fragment, and the business logic is handed over to Presenter for processing. This seems to be much better than the traditional MVC, because in the traditional MVC, Activity can be regarded as both Controller and View, so it is difficult to separate responsibilities, and there may be thousands of lines of code in an Activity, which will bring a lot of trouble for follow-up maintenance. On the other hand, MVP extracts the business logic into Presenter, and the responsibility of Fragment or Activity as View is more single, which undoubtedly brings convenience for subsequent development and maintenance.

Next, let's take a look at the initialization of Presenter in detail. The creation of Presenter is completed in TasksActivity. Check its constructor:

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);}

The first two check whether the passed parameter is empty, then assign it to a reference in TasksPresenter, call the setPresenter method of view, and pass itself in, so that the presenter object can be used in view, which looks a lot more elegant than taking it directly from activity. Presenter specific logic does not look at, are some relatively simple code, review the process of opening this app event: create TasksActivity-> initialize Toolbar-> initialize sideslip-> create TasksFragment object-> create TaskPresenter object-> set Presenter object to Fragment-> initialize Fragment layout. Under such a process, the whole process will be sorted out, and then just wait for the user's input.

The next thing to look at is Model:TasksRepository, which has been ignored since the beginning of this article. However, before analyzing TasksRepository, Amway takes a look at the entity classes in this project, which are written elegantly, and we can also follow his routine when we write entity classes. Why do I say that his writing is more elegant? Because each property or method with a return value is annotated with @ Nullable or @ NoNull to indicate whether it can be null or not, the error of the pointer above can be regarded as a common mistake. However, if you have good design and coding habits, it can be avoided, with these two comments can give you relevant hints at compile time. Not only that, this entity class also overrides the equals (), hashCode (), and toString () methods, and the way they are implemented conforms to the specification. There is a good summary on "effective java" on how to copy these three methods, which you can read.

/ * * 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 "ASIS" 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;}}

First take a look at the structure of the package where TasksRepository is located:

You can see from the package name that local reads data locally, remote reads remotely, and of course, it just simulates remote reading. The local database access method is adopted. There are two references to TasksDataSource within TasksRepository (hereinafter referred to as TR):

Private final TasksDataSource mTasksRemoteDataSource; private final TasksDataSource mTasksLocalDataSource

TasksDataSource is an interface in the data package. Using interface references is nothing more than decoupling. Even if the requirements change in the future, you do not want to store data as a database. As long as this interface is implemented, there is no need to change the code within TR. TR uses a singleton, which is not thread-safe:

/ * 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;}

In the final analysis, there is no need for thread safety at all, at least in this app, there is no scenario in which the object is created concurrently, so it is enough. Inside TR, a LinkedHashMap is used as a container to store the Tasks. There are two main methods. The first is storage:

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);}

The incoming task is stored in the remote data source and the local data source (local database), and then the task is passed to mCachedTasks (LinkedHashMap). The code is relatively simple, without more analysis, let's take a look at reading 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 tasks) {refreshCache (tasks); callback.onTasksLoaded (new ArrayList (mCachedTasks.values ();} @ Override public void onDataNotAvailable () {getTasksFromRemoteDataSource (callback) });}}

This taskId is the id that needs to obtain the Task, and it is also the unique identity, while GetTaskCallback is the callback of the interface responsible for passing data. The first step is to read the data from memory. The getTaskWithId method is to take a look at the code:

Private Task getTaskWithId (@ NonNull String id) {checkNotNull (id); if (mCachedTasks = = null | | mCachedTasks.isEmpty ()) {return null;} else {return mCachedTasks.get (id);}}

The data is read from the LinkedHashMap that holds the task. If the process cannot read the data, then read the data from the local data source, and if the local data source does not get the data, then eventually read the data from the remote data source.

Thank you for your reading, the above is the content of "android MVP sample code analysis", after the study of this article, I believe you have a deeper understanding of the problem of android MVP sample code analysis, and the specific use needs to be verified in practice. Here is, the editor will push for you more related knowledge points of the article, welcome to follow!

Welcome to subscribe "Shulou Technology Information " to get latest news, interesting things and hot topics in the IT industry, and controls the hottest and latest Internet news, technology news and IT industry trends.

Views: 0

*The comments in the above article only represent the author's personal views and do not represent the views and positions of this website. If you have more insights, please feel free to contribute and share.

Share To

Development

Wechat

© 2024 shulou.com SLNews company. All rights reserved.

12
Report