Android 官方樣本:android-architecture 學習筆記(二)之todo-mvp

來源:互聯網
上載者:User

標籤:available   loaded   inject   font   exception   now()   injection   completed   hlist   

項目地址:https://github.com/googlesamples/android-architecture/tree/todo-mvp/
在第一篇說過,todo-mvp只是android-architecture項目的一個分支

項目結構

todo-mvp主要使用了mvp架構來實現,

圖中的Repository就是資料來源,即M,包括Local資料和Remote資料;Fragment為V;Activity中依賴了P,V(Fragment)與P相互依賴,P依賴了M(Repository),即P分離了M與V;當然有時我們不需要Fragment,那麼可以直接使用Activity來作為V

項目的主要包結構及Base介面

(不含測試package):

除BaseView、BasePresenter兩個介面,其他package都以業務功能來劃分的:
addedittask —— 新增工作
data —— 資料來源
statistics —— 任務統計
taskdetail —— 任務詳情
tasks —— 工作清單
util —— 工具類

BaseView、BasePresenter兩個介面:

public interface BaseView<T> {    void setPresenter(T presenter);}public interface BasePresenter {    void start();}
資料來源Model

先來看下data模組,即M

Task —— 它就是一個java bean
TasksDataSource —— Task資料操作的介面
TasksRepository —— 實現了TasksDataSource,依賴了TasksLocalDataSource、TasksRemoteDataSource,實現根據不同情形,擷取Local或Remote的相關資料
TasksRemoteDataSource —— 遠端資料,一般可能走網路,當然這裡沒有,只是類比
TasksPersistenceContract —— 約定了資料庫表欄位
TasksDbHelper —— 資料庫操作
TasksLocalDataSource —— 本機資料源

註:項目中預設只操作了Local資料,若也想操作Remote,只需要將TasksRepository中的屬性mCacheIsDirty=true,即可

tasks模組分析

接下來挑一個業務模組分析一下(其它模組大同小異),比如tasks

ScrollChildSwipeRefreshLayout —— 這是一個自訂Layout,不用理會
TasksActivity —— Activity
TasksContract —— 契約介面(每個功能模組都有一個),約定了兩個子介面View和Presenter,及各自的公用方法;分別實現BaseView、BasePresenter兩個介面
TasksFilterType —— enum類,任務過濾類型
TasksFragment —— Fragment,實現TasksContract.View
TasksPresenter —— Presenter,實現TasksContract.Presenter

再來分析一個具體的業務功能,比如展示tasks列表
在TasksContract#Presenter中,有一個方法:

void loadTasks(boolean forceUpdate);

相應的TasksContract#View中,有一個方法:

void showTasks(List<Task> tasks);

看下TasksPresenter的相關的一些代碼:

public class TasksPresenter implements TasksContract.Presenter {    private final TasksRepository mTasksRepository;    private final TasksContract.View mTasksView;    private TasksFilterType mCurrentFiltering = TasksFilterType.ALL_TASKS;    private boolean mFirstLoad = true;    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);    }    @Override    public void start() {        loadTasks(false);    }    @Override    public void loadTasks(boolean forceUpdate) {        // Simplification for sample: a network reload will be forced on first load.        loadTasks(forceUpdate || mFirstLoad, true);        mFirstLoad = false;    }    private void loadTasks(boolean forceUpdate, final boolean showLoadingUI) {        if (showLoadingUI) {            mTasksView.setLoadingIndicator(true);        }        if (forceUpdate) {            mTasksRepository.refreshTasks();        }        // The network request might be handled in a different thread so make sure Espresso knows        // that the app is busy until the response is handled.        EspressoIdlingResource.increment(); // App is busy until further notice        mTasksRepository.getTasks(new TasksDataSource.LoadTasksCallback() {            @Override            public void onTasksLoaded(List<Task> tasks) {                List<Task> tasksToShow = new ArrayList<Task>();                // This callback may be called twice, once for the cache and once for loading                // the data from the server API, so we check before decrementing, otherwise                // it throws "Counter has been corrupted!" exception.                if (!EspressoIdlingResource.getIdlingResource().isIdleNow()) {                    EspressoIdlingResource.decrement(); // Set app as idle.                }                // We filter the tasks based on the requestType                for (Task task : tasks) {                    switch (mCurrentFiltering) {                        case ALL_TASKS:                            tasksToShow.add(task);                            break;                        case ACTIVE_TASKS:                            if (task.isActive()) {                                tasksToShow.add(task);                            }                            break;                        case COMPLETED_TASKS:                            if (task.isCompleted()) {                                tasksToShow.add(task);                            }                            break;                        default:                            tasksToShow.add(task);                            break;                    }                }                // The view may not be able to handle UI updates anymore                if (!mTasksView.isActive()) {                    return;                }                if (showLoadingUI) {                    mTasksView.setLoadingIndicator(false);                }                processTasks(tasksToShow);            }            @Override            public void onDataNotAvailable() {                // The view may not be able to handle UI updates anymore                if (!mTasksView.isActive()) {                    return;                }                mTasksView.showLoadingTasksError();            }        });    }    private void processTasks(List<Task> tasks) {        if (tasks.isEmpty()) {            // Show a message indicating there are no tasks for that filter type.            processEmptyTasks();        } else {            // Show the list of tasks            mTasksView.showTasks(tasks);            // Set the filter label‘s text.            showFilterLabel();        }    }}

從上,看出TasksPresenter依賴了TasksRepository、TasksContract.View,即P依賴了M和V;當loadTasks(boolean forceUpdate, final boolean showLoadingUI)被調用後,先從M中擷取資料,再調用processTasks(List tasks),其內部調用mTasksView.showTasks(tasks)將資料顯示在V上;最後還要說的一點是在TasksPresenter的構造方法中,mTasksView.setPresenter(this) 將P傳遞給了V

再來看下V如何通過P,來擷取資料並顯示
TasksFragment,即V的主要代碼:

public class TasksFragment extends Fragment implements TasksContract.View {    private TasksContract.Presenter mPresenter;    public TasksFragment() {        // Requires empty public constructor    }    public static TasksFragment newInstance() {        return new TasksFragment();    }    @Override    public void onCreate(@Nullable Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        mListAdapter = new TasksAdapter(new ArrayList<Task>(0), mItemListener);    }    @Override    public void onResume() {        super.onResume();        mPresenter.start();    }    @Override    public void setPresenter(@NonNull TasksContract.Presenter presenter) {        mPresenter = checkNotNull(presenter);    }    @Nullable    @Override    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {        swipeRefreshLayout.setOnRefreshListener(new SwipeRefreshLayout.OnRefreshListener() {            @Override            public void onRefresh() {                mPresenter.loadTasks(false);            }        });        return root;    }    @Override    public void showTasks(List<Task> tasks) {        mListAdapter.replaceData(tasks);        mTasksView.setVisibility(View.VISIBLE);        mNoTasksView.setVisibility(View.GONE);    }}

看TasksFragment的onResume()中,調用了mPresenter.start(),而mPresenter.start()中,就調用了TasksPresenter#loadTasks(false);TasksFragment的onCreateView()中還註冊了一個監聽回調,即下拉重新整理時,也會調用TasksPresenter#loadTasks(false);

P和V的初始化在Activity中完成
TasksActivity的主要代碼:

public class TasksActivity extends AppCompatActivity {    private TasksPresenter mTasksPresenter;    @Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.tasks_act);        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);        // Load previously saved state, if available.        if (savedInstanceState != null) {            TasksFilterType currentFiltering =                    (TasksFilterType) savedInstanceState.getSerializable(CURRENT_FILTERING_KEY);            mTasksPresenter.setFiltering(currentFiltering);        }    }}

至此,一條完整的MVP架構實現的業務鏈就分析完成了

Android 官方樣本:android-architecture 學習筆記(二)之todo-mvp

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.