眾所周知棒呛,Android Framework提供了大量靈活的方式來定義如何組織和架構(gòu)一個Android App缭乘,自由,附有價值秸脱,但同時也導致App擁有了大量的類,不一致的命名和架構(gòu)也給測試部蛇,維護和拓展帶來了困難摊唇。由此帶來了很多的App架構(gòu),MVC涯鲁、MVP巷查,MVVM等,前幾天Google官方推出了關(guān)于Android 架構(gòu)藍圖的Sample抹腿,借此來引導Android開發(fā)者解決這些問題岛请,創(chuàng)建自己的App。重點放在代碼結(jié)構(gòu)警绩,測試和維護崇败。目前推出了3個Sample,接下來還會繼續(xù)推出肩祥,至于在項目中用哪個取決于你自己后室。這些Sample不能算是經(jīng)典例子,但可以作為參考混狠。不過從剛推出就在Github趨勢上位列前茅岸霹,也能感受到大家對此的期待。
Demo運行圖
TODO-MVP
架構(gòu)圖
架構(gòu)解析
這個是很經(jīng)典的MVP架構(gòu)将饺,從代碼結(jié)構(gòu)上也能看到一些啟發(fā)贡避,按業(yè)務功能進行劃分痛黎,例如addedittask(新增任務),taskdetail(任務詳情)等贸桶,里面含有相關(guān)的Presenter舅逸,View,Activity和Fragment皇筛,這樣做的好處是可以方便的找到業(yè)務功能代碼琉历。
我們以addedittask為例:
AddEditTaskActivity:因為用了Fragment來顯示頁面,Activity沒有過多的邏輯水醋。
AddEditTaskContract:里面包含View和Presenter接口類
public interface AddEditTaskContract {
interface View extends BaseView<Presenter> {
void showEmptyTaskError();
void showTasksList();
void setTitle(String title);
void setDescription(String description);
boolean isActive();
}
interface Presenter extends BasePresenter {
void createTask(String title, String description);
void updateTask( String title, String description);
void populateTask();
}
}
AddEditTaskFragment:
public class AddEditTaskFragment extends Fragment implements AddEditTaskContract.View {
......//省略
public static AddEditTaskFragment newInstance() {
return new AddEditTaskFragment();
}
public AddEditTaskFragment() {
// Required empty public constructor
}
@Override
public void onResume() {
super.onResume();
mPresenter.start();
}
@Override
public void setPresenter(@NonNull AddEditTaskContract.Presenter presenter) {
mPresenter = checkNotNull(presenter);
}
@Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
......//省略
FloatingActionButton fab =
(FloatingActionButton) getActivity().findViewById(R.id.fab_edit_task_done);
fab.setImageResource(R.drawable.ic_done);
fab.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if (isNewTask()) {
mPresenter.createTask(
mTitle.getText().toString(),
mDescription.getText().toString());
} else {
mPresenter.updateTask(
mTitle.getText().toString(),
mDescription.getText().toString());
}
}
});
}
@Nullable
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View root = inflater.inflate(R.layout.addtask_frag, container, false);
......//省略
return root;
}
@Override
public void showEmptyTaskError() {
Snackbar.make(mTitle, getString(R.string.empty_task_message), Snackbar.LENGTH_LONG).show();
}
@Override
public void showTasksList() {
getActivity().setResult(Activity.RESULT_OK);
getActivity().finish();
}
@Override
public void setTitle(String title) {
mTitle.setText(title);
}
@Override
public void setDescription(String description) {
mDescription.setText(description);
}
@Override
public boolean isActive() {
return isAdded();
}
private boolean isNewTask() {
return mEditedTaskId == null;
}
}
AddEditTaskPresenter:Presenter實現(xiàn)類旗笔,業(yè)務邏輯的實現(xiàn)
public class AddEditTaskPresenter implements AddEditTaskContract.Presenter,
TasksDataSource.GetTaskCallback {
......//省略
/**
* Creates a presenter for the add/edit view.
*
* @param taskId ID of the task to edit or null for a new task
* @param tasksRepository a repository of data for tasks
* @param addTaskView the add/edit view
*/
public AddEditTaskPresenter(@Nullable String taskId, @NonNull TasksDataSource tasksRepository,
@NonNull AddEditTaskContract.View addTaskView) {
mTaskId = taskId;
mTasksRepository = checkNotNull(tasksRepository);
mAddTaskView = checkNotNull(addTaskView);
mAddTaskView.setPresenter(this);
}
@Override
public void start() {
if (mTaskId != null) {
populateTask();
}
}
@Override
public void createTask(String title, String description) {
Task newTask = new Task(title, description);
if (newTask.isEmpty()) {
mAddTaskView.showEmptyTaskError();
} else {
mTasksRepository.saveTask(newTask);
mAddTaskView.showTasksList();
}
}
@Override
public void updateTask(String title, String description) {
if (mTaskId == null) {
throw new RuntimeException("updateTask() was called but task is new.");
}
mTasksRepository.saveTask(new Task(title, description, mTaskId));
mAddTaskView.showTasksList(); // After an edit, go back to the list.
}
@Override
public void populateTask() {
if (mTaskId == null) {
throw new RuntimeException("populateTask() was called but task is new.");
}
mTasksRepository.getTask(mTaskId, this);
}
@Override
public void onTaskLoaded(Task task) {
// The view may not be able to handle UI updates anymore
if (mAddTaskView.isActive()) {
mAddTaskView.setTitle(task.getTitle());
mAddTaskView.setDescription(task.getDescription());
}
}
@Override
public void onDataNotAvailable() {
// The view may not be able to handle UI updates anymore
if (mAddTaskView.isActive()) {
mAddTaskView.showEmptyTaskError();
}
}
}
TODO-MVP-Loaders
架構(gòu)圖
架構(gòu)分析
和TODO-MVP的區(qū)別是通過Loader對數(shù)據(jù)進行異步加載,監(jiān)控其數(shù)據(jù)源并在內(nèi)容變化時傳遞新結(jié)果拄踪。關(guān)于Loader的使用蝇恶,可以直接看官方文檔,我就不復述了惶桐。
TODO-DataBinding
架構(gòu)圖
架構(gòu)分析
和TODO-MVP的區(qū)別是通過Data Binding來綁定app邏輯和layouts文件撮弧。關(guān)于Data Binding的使用,可以直接看官方文檔姚糊,我就不復述了贿衍。
總結(jié)
通過MVP進行業(yè)務邏輯和視圖的分離,讓View專注于處理數(shù)據(jù)的可視化以及與用戶的交互救恨,同時讓Repository只關(guān)系數(shù)據(jù)的處理贸辈,讓Presenter作為View和Repository之間的紐帶,易于維護和測試肠槽。代碼比較簡單擎淤,具體是否使用Loader和Data Binding就看個人喜好了。雖然Google并沒有把這些當做指導文檔秸仙,但也算給Android開發(fā)者帶來了一些思路嘴拢。
參考資料
Android-Architecture
Github
Loader文檔
Data Binding文檔
歡迎關(guān)注我的微博