Android教程網
  1. 首頁
  2. Android 技術
  3. Android 手機
  4. Android 系統教程
  5. Android 游戲
 Android教程網 >> Android技術 >> 關於Android編程 >> Android MVP

Android MVP

編輯:關於Android編程

前言

前段時間,公司由個同事分享的時候,提到了MVP模式,自己之前也了解過,但是真正在自己的編碼過程中使用的非常少。最近在幫助一個朋友做畢業設計,心想這是一個很好的機會練習一把。網上也找了很多有關MVP的博客,說的也都差不多,就想找一個比較權威的,當然應該是google官網啦,就找到了Google在Github上開源項目,真找到了MVP例子,就記一篇博文,慢慢回味。

Android MVP

MVP(MVP模式):一種軟件設計模式
全稱為Model-View-Presenter,Model提供數據,View負責顯示,Controller/Presenter負責邏輯的處理。MVP與MVC有著一個重大的區別:在MVP中View並不直接使用Model,它們之間的通信是通過Presenter (MVC中的Controller)來進行的,所有的交互都發生在Presenter內部,而在MVC中View會直接從Model中讀取數據而不是通過 Controller。
goto 維基百科
goto 百度百科
MVP模式,一方面是體現單一職責原則,降低Android中類之間邏輯的耦合,使之高內聚低耦合;二是讓代碼更清晰,更簡潔,但是簡潔並不代表代碼少,更易擴展等。
#Android MVP(Google Samples/aandroid-architecture)
前言中提到過,Android官方提供了MVP Sample,這也是Google發現問題,給開發者提供的一種思路吧。
goto Google Sample
點擊上述鏈接,可以看到Google官方為大家推薦的應用開發框架,Android 架構藍圖,當然有心的你還會從Google Github發現更多有用的開源項目。

概述

這裡寫圖片描述vcC0sO/W+r3ivvbV4tCps6O8+7XEzsrM4qGj1NrV4rj2z+7Ev9bQo6zM4bmpyrnTw7K7zay1xLzcubm6zbmkvt/Ktc/Wz+DNrLXE06bTw7PM0PKhozxiciAvPg0Kudm3vda7yse9q9Xi0KnX986qss6/vKOsvt/M5crHt/G6z8rKtPO80rfFyOvP7sS/1tCjrL7N0qq+38zlx+m/9r7fzOW31s72wcujrNbYtePKx7T6wuu94bm5o6zM5c+1veG5uaOst72x47LiytS6zb/Jzqy7pKOsv8nN2NW50NShozwvcD4NCjxoMiBpZD0="samples">Samples

在android-architecture項目下,提供了7個例子[穩定版],分別對應項目不同的分支下,大家可以下載下來,跑一跑,看一看。
1. todo-mvp/ - Basic Model-View-Presenter architecture.
2. todo-mvp-loaders/ - Based on todo-mvp, fetches data using Loaders.
3. todo-databinding/ - Based on todo-mvp, uses the Data Binding Library.
4. todo-mvp-clean/ - Based on todo-mvp, uses concepts from Clean Architecture.
5. todo-mvp-dagger/ - Based on todo-mvp, uses Dagger2 for Dependency Injection.
6.todo-mvp-contentproviders/ - Based on todo-mvp-loaders, fetches data using Loaders and uses Content Providers.
7.todo-mvp-rxjava/ - Based on todo-mvp, uses RxJava for concurrency and data layer abstraction.
8.todo-mvp-tablet/
很清晰的可以看出,Sample1就是單純的介紹MVP設計模式的,2-7是在1的基礎上,添加了一些快速開發的一些框架,本文主要記錄MVP,其他將後續跟進; 8官方正在開發中,應該還不算穩定版本。

TODO-MVP

todo-mvp

如何下載,以及運行參考官方文檔即可,這個也是學習的一個過程。
它展示了一個簡單的MVP模式,沒有使用任何框架和架構。它使用手動依賴注入,以提供一個本地和遠程數據源的存儲庫。異步任務處理回調。
首先看下整體框架,左半邊是Model,右邊是View和Presenter,官方善意的注釋了下,圖片中的VIEW並不是android框架中的View,是MVP場景中的一個抽象,在代碼中就能體會到。途中藍色字體相當於抽象概念。
之所以使用Fragment:

Activity與fragment的分離,非常符合MVP的實現:Activity是創建並連接Views和Presenter的整體控制器 Table layout和多視圖界面使用Fragment框架將非常有優勢
圖1

依賴

Common Android support libraries (com.android.support.*) Android Testing Support Library (Espresso, AndroidJUnitRunner…) Mockito Guava (null checking)

源碼分析

首先看下整體的源碼結構:
這裡寫圖片描述

從上圖可以很清晰的看出整個app的功能模塊,包括(Tasks, AddEditTask, TaskDetail, Statistics)
每個功能包含四大文件:Activity, Fragment, Contract, Presenter
就像上述提及的Activity相當於一個控制器;Fragment相當於MVP中的View;Contract是一個功能中View和Presenter間的協議,通俗的理解就是,這兩個是協同工作的;而Presenter就是具體的業務邏輯實現代碼。首先會根據當前的功能模塊進行分解成多個業務邏輯,然後制定View和Presenter的協議。

運行效果如下:
這裡寫圖片描述這裡寫圖片描述這裡寫圖片描述這裡寫圖片描述

以Task功能來分析下具體的MVP模式的實現:
TasksContract:主要是定義相關的業務邏輯接口,還有UI的更新接口,可以很清晰的查看該功能界面的具體業務功能,不過當頁面比較復雜時,這個文件會不會很大???或者說我們就不該把一個頁面搞太復雜。網上看過不少例子,都沒有提到這個文件,個人感覺這個Contract還是很重要的!!!

/**
 * 指定View和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();
    }
}

TasksActivity:主要是創建View(TasksFragment), 創建Presenter(TasksFragment), 並將View在Presenter構造過程中傳遞過去。

        TasksFragment tasksFragment =
                (TasksFragment) getSupportFragmentManager().findFragmentById(R.id.contentFrame);
        if (tasksFragment == null) {
            // Create the fragment
            tasksFragment = TasksFragment.newInstance();
            ActivityUtils.addFragmentToActivity(
                    getSupportFragmentManager(), tasksFragment, R.id.contentFrame);
        }

        // Create the presenter
        mTasksPresenter = new TasksPresenter(
                Injection.provideTasksRepository(getApplicationContext()), tasksFragment);

TasksFragment:進行Presenter的初始化, 根據用戶交互,調用View接口定義的相關協議接口,執行Presenter中定義的邏輯接口

/**
 * Display a grid of {@link Task}s. User can choose to view all, active or completed tasks.
 */
public class TasksFragment extends Fragment implements TasksContract.View {

    private TasksContract.Presenter mPresenter;

    ...

    @Override
    public void onResume() {
        super.onResume();
        mPresenter.start();
    }

    @Override
    public void setPresenter(@NonNull TasksContract.Presenter presenter) {
        mPresenter = checkNotNull(presenter);
    }

    @Override
    public void onActivityResult(int requestCode, int resultCode, Intent data) {
        mPresenter.result(requestCode, resultCode);
    }

    @Nullable
    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
                             Bundle savedInstanceState) {
        mNoTaskAddView.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                showAddTask();
            }
        });
        fab.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                mPresenter.addNewTask();
            }
        });

        // Set up progress indicator
        final ScrollChildSwipeRefreshLayout swipeRefreshLayout =
                (ScrollChildSwipeRefreshLayout) root.findViewById(R.id.refresh_layout);
        swipeRefreshLayout.setColorSchemeColors(
                ContextCompat.getColor(getActivity(), R.color.colorPrimary),
                ContextCompat.getColor(getActivity(), R.color.colorAccent),
                ContextCompat.getColor(getActivity(), R.color.colorPrimaryDark)
        );
        // Set the scrolling view in the custom SwipeRefreshLayout.
        swipeRefreshLayout.setScrollUpChild(listView);

        swipeRefreshLayout.setOnRefreshListener(new SwipeRefreshLayout.OnRefreshListener() {
            @Override
            public void onRefresh() {
                mPresenter.loadTasks(false);
            }
        });

        setHasOptionsMenu(true);

        return root;
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        switch (item.getItemId()) {
            case R.id.menu_clear:
                mPresenter.clearCompletedTasks();
                break;
            case R.id.menu_filter:
                showFilteringPopUpMenu();
                break;
            case R.id.menu_refresh:
                mPresenter.loadTasks(true);
                break;
        }
        return true;
    }
    @Override
    public void showFilteringPopUpMenu() {
        PopupMenu popup = new PopupMenu(getContext(), getActivity().findViewById(R.id.menu_filter));
        popup.getMenuInflater().inflate(R.menu.filter_tasks, popup.getMenu());

        popup.setOnMenuItemClickListener(new PopupMenu.OnMenuItemClickListener() {
            public boolean onMenuItemClick(MenuItem item) {
                switch (item.getItemId()) {
                    case R.id.active:
                        mPresenter.setFiltering(TasksFilterType.ACTIVE_TASKS);
                        break;
                    case R.id.completed:
                        mPresenter.setFiltering(TasksFilterType.COMPLETED_TASKS);
                        break;
                    default:
                        mPresenter.setFiltering(TasksFilterType.ALL_TASKS);
                        break;
                }
                mPresenter.loadTasks(false);
                return true;
            }
        });

        popup.show();
    }

    /**
     * Listener for clicks on tasks in the ListView.
     */
    TaskItemListener mItemListener = new TaskItemListener() {
        @Override
        public void onTaskClick(Task clickedTask) {
            mPresenter.openTaskDetails(clickedTask);
        }

        @Override
        public void onCompleteTaskClick(Task completedTask) {
            mPresenter.completeTask(completedTask);
        }

        @Override
        public void onActivateTaskClick(Task activatedTask) {
            mPresenter.activateTask(activatedTask);
        }
    };

    ...

    @Override
    public void showTasks(List tasks) {
        mListAdapter.replaceData(tasks);

        mTasksView.setVisibility(View.VISIBLE);
        mNoTasksView.setVisibility(View.GONE);
    }

    @Override
    public void showNoActiveTasks() {
        showNoTasksViews(
                getResources().getString(R.string.no_tasks_active),
                R.drawable.ic_check_circle_24dp,
                false
        );
    }

    @Override
    public void showNoTasks() {
        showNoTasksViews(
                getResources().getString(R.string.no_tasks_all),
                R.drawable.ic_assignment_turned_in_24dp,
                false
        );
    }

    @Override
    public void showNoCompletedTasks() {
        showNoTasksViews(
                getResources().getString(R.string.no_tasks_completed),
                R.drawable.ic_verified_user_24dp,
                false
        );
    }

    @Override
    public void showSuccessfullySavedMessage() {
        showMessage(getString(R.string.successfully_saved_task_message));
    }

    private void showNoTasksViews(String mainText, int iconRes, boolean showAddView) {
        mTasksView.setVisibility(View.GONE);
        mNoTasksView.setVisibility(View.VISIBLE);

        mNoTaskMainView.setText(mainText);
        mNoTaskIcon.setImageDrawable(getResources().getDrawable(iconRes));
        mNoTaskAddView.setVisibility(showAddView ? View.VISIBLE : View.GONE);
    }

    @Override
    public void showActiveFilterLabel() {
        mFilteringLabelView.setText(getResources().getString(R.string.label_active));
    }

    @Override
    public void showCompletedFilterLabel() {
        mFilteringLabelView.setText(getResources().getString(R.string.label_completed));
    }

    @Override
    public void showAllFilterLabel() {
        mFilteringLabelView.setText(getResources().getString(R.string.label_all));
    }

    @Override
    public void showAddTask() {
        Intent intent = new Intent(getContext(), AddEditTaskActivity.class);
        startActivityForResult(intent, AddEditTaskActivity.REQUEST_ADD_TASK);
    }

    @Override
    public void showTaskDetailsUi(String taskId) {
        // in it's own Activity, since it makes more sense that way and it gives us the flexibility
        // to show some Intent stubbing.
        Intent intent = new Intent(getContext(), TaskDetailActivity.class);
        intent.putExtra(TaskDetailActivity.EXTRA_TASK_ID, taskId);
        startActivity(intent);
    }

    @Override
    public void showTaskMarkedComplete() {
        showMessage(getString(R.string.task_marked_complete));
    }

    @Override
    public void showTaskMarkedActive() {
        showMessage(getString(R.string.task_marked_active));
    }

    @Override
    public void showCompletedTasksCleared() {
        showMessage(getString(R.string.completed_tasks_cleared));
    }

    @Override
    public void showLoadingTasksError() {
        showMessage(getString(R.string.loading_tasks_error));
    }

    private void showMessage(String message) {
        Snackbar.make(getView(), message, Snackbar.LENGTH_LONG).show();
    }

    @Override
    public boolean isActive() {
        return isAdded();
    }
    ...
}

TasksPresenter:監聽用戶的UI操作,實現Presenter協議中定義的業務邏輯,和Model層進行交換,然後更新UI。

/**
 * Listens to user actions from the UI ({@link TasksFragment}), retrieves the data and updates the
 * UI as required.
 */
public class TasksPresenter implements TasksContract.Presenter {
    ...
    public TasksPresenter(@NonNull TasksRepository tasksRepository, @NonNull TasksContract.View tasksView) {
        mTasksRepository = checkNotNull(tasksRepository, "tasksRepository cannot be null");
        mTasksView = checkNotNull(tasksView, "tasksView cannot be null!");

        ...

其中TasksContract.View實現了BaseView,實現setPresenter接口
TasksContract.Presenter實現了BasePresenter,實現了start接口

MyMVP

//TODO

  1. 上一頁:
  2. 下一頁:
熱門文章
閱讀排行版
Copyright © Android教程網 All Rights Reserved