Пример интерпретации Google Android MVP

Привыкайте писать вместе! Это 13-й день моего участия в «Новом ежедневном плане Nuggets · Апрельское задание по обновлению», нажмите, чтобы просмотреть подробности мероприятия .

Эта статья была написана, когда lucio был стажером в 2017 году и занимался Android-разработкой, сейчас вроде бы есть еще какие-то отсылки.

Пример Google MVP

Пример архитектуры Google MVP: github.com/googlesample…

Android предоставляет разработчикам высокую степень гибкости в разработке структуры кода приложения, но это также может привести к таким проблемам, как загроможденная структура кода и плохая читабельность. Пример Google MVP дает ссылку на дизайн структуры кода приложения.Проблема гибкости решена.Как говорится в документации,конкретный дизайн необходимо корректировать в соответствии с конкретной ситуацией приложения.

Пример на официальном сайте показывает реализацию различных фреймворков и инструментов на основе паттерна MVP Начнем с самой базовой архитектуры MVP.

Шаблон лучшего игрока

Модель-Вид-Презентаторнапишите сюда описание фото

  1. Связь между каждой частью двусторонняя.
  2. View и Model не контактируют, передаются через Presenter.
  3. View не обрабатывает бизнес-логику и называется «Passive View», то есть не имеет никакой инициативы.
  4. Все взаимодействия происходят в Presenter.
  5. Модель — это не просто определение сущностей, но она также должна выполнять задачи сбора, хранения и преобразования данных.

Пример интерпретации Google MVP

Возьмем в качестве примера модуль деталей TO-DO-MVP.

гугл лучший игрокНам нужно сосредоточиться на классах, которые реализует каждая часть MVP, и на том, как взаимодействуют VP и PM.

BasePresenter и BaseView два основных класса

public interface BaseView<T> {
    // 用于绑定Presenter,在BasePresenter实现类的构造器中,传入BaseView,再调用其setPresenter方法
    void setPresenter(T presenter);
}
复制代码
public interface BasePresenter {
    // 用于开始获取数据并调用View的方法更新UI,一般在Fragment的onResume方法中调用。
    void start();
}
复制代码

Интерфейс контракта TaskDetailContract

Он используется для унифицированного управления интерфейсами Presenter и View и определяет функции, реализуемые Presenter, и операции пользовательского интерфейса, реализуемые View.

/**
 * This specifies the contract between the view and the presenter.
 */
public interface TaskDetailContract {

    interface View extends BaseView<Presenter> {

        void setLoadingIndicator(boolean active);

        void showMissingTask();

        void hideTitle();

        void showTitle(String title);

        void hideDescription();

        void showDescription(String description);

        void showCompletionStatus(boolean complete);

        void showEditTask(String taskId);

        void showTaskDeleted();

        void showTaskMarkedComplete();

        void showTaskMarkedActive();

        boolean isActive();
    }

    interface Presenter extends BasePresenter {

        void editTask();

        void deleteTask();

        void completeTask();

        void activateTask();
    }
}
复制代码

TaskDetailFragment —— Просмотр

Реализуйте интерфейс TaskDetailContract.View.

@Override
public void onResume() {
        super.onResume();
        //通知mPresenter获取数据,mPresenter.start()获取到数据后,再通知View更改数据
        mPresenter.start();
}
复制代码
@Override
public void setPresenter(@NonNull TaskDetailContract.Presenter presenter) {
		//为View绑定Presenter
        mPresenter = checkNotNull(presenter);
}
复制代码
@Override
public void showCompletionStatus(final boolean complete) {
        Preconditions.checkNotNull(mDetailCompleteStatus);

		//更新UI
        mDetailCompleteStatus.setChecked(complete);
        mDetailCompleteStatus.setOnCheckedChangeListener(
                new CompoundButton.OnCheckedChangeListener() {
                    @Override
                    public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
                        if (isChecked) {
                        	//监听到事件时,通知Presenter工作。
                            mPresenter.completeTask();
                        } else {
                            mPresenter.activateTask();
                        }
                    }
                });
}
复制代码

TaskDetailPresenter —— Ведущий

Реализовать TaskDetailContract.Presenter.

public TaskDetailPresenter(@Nullable String taskId,
                           @NonNull TasksRepository tasksRepository,
                           @NonNull TaskDetailContract.View taskDetailView) {
        mTaskId = taskId;
        mTasksRepository = checkNotNull(tasksRepository, "tasksRepository cannot be null!");
        //为Presenter绑定View
  		mTaskDetailView = checkNotNull(taskDetailView, "taskDetailView cannot be null!");
		//为View绑定Presenter
        mTaskDetailView.setPresenter(this);
}
复制代码
@Override
public void start() {
		//初始更新界面
        openTask();
 }

private void openTask() {
        if (Strings.isNullOrEmpty(mTaskId)) {
            mTaskDetailView.showMissingTask();
            return;
        }

        mTaskDetailView.setLoadingIndicator(true);
        //Model获取数据
        mTasksRepository.getTask(mTaskId, new TasksDataSource.GetTaskCallback() {
            @Override
            public void onTaskLoaded(Task task) {
                // The view may not be able to handle UI updates anymore
                if (!mTaskDetailView.isActive()) {
                    return;
                }
                mTaskDetailView.setLoadingIndicator(false);
                if (null == task) {
                    //通知View更新界面
                    mTaskDetailView.showMissingTask();
                } else {
                    showTask(task);
                }
            }

            @Override
            public void onDataNotAvailable() {
                // The view may not be able to handle UI updates anymore
                if (!mTaskDetailView.isActive()) {
                    return;
                }
                mTaskDetailView.showMissingTask();
            }
        });
}
复制代码
@Override
public void completeTask() {
        if (Strings.isNullOrEmpty(mTaskId)) {
            mTaskDetailView.showMissingTask();
            return;
        }
  		//通知Model变换数据
        mTasksRepository.completeTask(mTaskId);
  		//通知View更新界面
        mTaskDetailView.showTaskMarkedComplete();
}
复制代码

TasksLocalDataSource Японский TasksRemoteDataSource Singleton —— Модель

Реализовать TasksDataSource.

ЗадачиИсточник Данных

public interface TasksDataSource {
	//回调接口可被Presenter实现,可在获取数据成功/失败后,让Presenter做出处理,如通知View更新等。
    interface LoadTasksCallback {

        void onTasksLoaded(List<Task> tasks);

        void onDataNotAvailable();
    }

    interface GetTaskCallback {

        void onTaskLoaded(Task task);

        void onDataNotAvailable();
    }
	//获取、存储、变换数据的方法
    void getTasks(@NonNull LoadTasksCallback callback);

    void getTask(@NonNull String taskId, @NonNull GetTaskCallback callback);

    void saveTask(@NonNull Task task);

    void completeTask(@NonNull Task task);

    void completeTask(@NonNull String taskId);

    void activateTask(@NonNull Task task);

    void activateTask(@NonNull String taskId);

    void clearCompletedTasks();

    void refreshTasks();

    void deleteAllTasks();

    void deleteTask(@NonNull String taskId);
}
复制代码

ЗадачиLocalDataSource

/**
 * Concrete implementation of a data source as a db.
 */
public class TasksLocalDataSource implements TasksDataSource {

    private static TasksLocalDataSource INSTANCE;

    private TasksDbHelper mDbHelper;

    // Prevent direct instantiation.
    private TasksLocalDataSource(@NonNull Context context) {
        checkNotNull(context);
        mDbHelper = new TasksDbHelper(context);
    }

    public static TasksLocalDataSource getInstance(@NonNull Context context) {
        if (INSTANCE == null) {
            INSTANCE = new TasksLocalDataSource(context);
        }
        return INSTANCE;
    }

    /**
     * Note: {@link LoadTasksCallback#onDataNotAvailable()} is fired if the database doesn't exist
     * or the table is empty.
     */
    @Override
    public void getTasks(@NonNull LoadTasksCallback callback) {
        List<Task> tasks = new ArrayList<Task>();
        SQLiteDatabase db = mDbHelper.getReadableDatabase();

        String[] projection = {
                TaskEntry.COLUMN_NAME_ENTRY_ID,
                TaskEntry.COLUMN_NAME_TITLE,
                TaskEntry.COLUMN_NAME_DESCRIPTION,
                TaskEntry.COLUMN_NAME_COMPLETED
        };

        Cursor c = db.query(
                TaskEntry.TABLE_NAME, projection, null, null, null, null, null);

        if (c != null && c.getCount() > 0) {
            while (c.moveToNext()) {
                String itemId = c.getString(c.getColumnIndexOrThrow(TaskEntry.COLUMN_NAME_ENTRY_ID));
                String title = c.getString(c.getColumnIndexOrThrow(TaskEntry.COLUMN_NAME_TITLE));
                String description =
                        c.getString(c.getColumnIndexOrThrow(TaskEntry.COLUMN_NAME_DESCRIPTION));
                boolean completed =
                        c.getInt(c.getColumnIndexOrThrow(TaskEntry.COLUMN_NAME_COMPLETED)) == 1;
                Task task = new Task(title, description, itemId, completed);
                tasks.add(task);
            }
        }
        if (c != null) {
            c.close();
        }

        db.close();

        if (tasks.isEmpty()) {
            // This will be called if the table is new or just empty.
            callback.onDataNotAvailable();
        } else {
            callback.onTasksLoaded(tasks);
        }

    }

	... ...
}
复制代码

TaskDetailActivity — глобальный контроль

Отвечает за создание View и Presenter и их связывание.

/**
 * Displays task details screen.
 */
public class TaskDetailActivity extends AppCompatActivity {

    public static final String EXTRA_TASK_ID = "TASK_ID";

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        setContentView(R.layout.taskdetail_act);

        // Set up the toolbar.
        Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
        setSupportActionBar(toolbar);
        ActionBar ab = getSupportActionBar();
        ab.setDisplayHomeAsUpEnabled(true);
        ab.setDisplayShowHomeEnabled(true);

        // Get the requested task id
        String taskId = getIntent().getStringExtra(EXTRA_TASK_ID);

        TaskDetailFragment taskDetailFragment = (TaskDetailFragment) getSupportFragmentManager()
                .findFragmentById(R.id.contentFrame);

        if (taskDetailFragment == null) {
            taskDetailFragment = TaskDetailFragment.newInstance(taskId);

            ActivityUtils.addFragmentToActivity(getSupportFragmentManager(),
                    taskDetailFragment, R.id.contentFrame);
        }

        // Create the presenter
        new TaskDetailPresenter(
                taskId,
                Injection.provideTasksRepository(getApplicationContext()),
                taskDetailFragment);
    }

    @Override
    public boolean onSupportNavigateUp() {
        onBackPressed();
        return true;
    }
}
复制代码

Прочитав пример, вы можете увидеть, что контекст всего проекта очень ясен, разделение труда в каждой части MVP понятно, использование фрагмента как View имеет большую гибкость, чем Activity, и каждая функция проекта разделена на модулей, что очень соответствует принципу «высокая сплоченность, низкая связанность». С другой стороны, различие каждой части MVP также облегчает модульное тестирование кода каждой части, например сбор данных и обновление интерфейса.

В описании проекта сказано следующее:

Основное внимание в этом проекте уделяется демонстрации того, как структурировать ваш код, проектировать архитектуру и возможное влияние принятия этих шаблонов на тестирование и поддержку вашего приложения.

Основное внимание в проекте уделяется: структуре кода, общей архитектуре, тестируемости и ремонтопригодности.

В ответ на разные приложения нам нужно вносить разные коррективы, но следование модели MVP для дизайна — очень хорошее начало.

рекомендация

отjuejin.im/post/7086091435191058463