標籤: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