Android MVP(Model View Presenter)
来源:互联网 发布:mac玩qq堂 编辑:程序博客网 时间:2024/05/16 06:39
背景
前一段时间对公司项目进行了重构,在架构上面由原来的MVC模式切换到现在使用的MVP模式,经历了一段时间线上的的维护和升级决定写一些关于MVP模式的思考和反思。本文主要讲一些对MVP模式的介绍、和MVC模式的对比、以及怎么使用MVP模式来构建应用,最后给出一些意见。
一、关于mvp模式的一些介绍
MVP模式主要是对关注点进行分离,在MVP中将应用程序划分为三层。
* Model 持有应用的业务逻辑.它控制数据的创建 存储和修改
* View 显示用户数据界面,并将用户的交互事件给Presenter处理
* Presenter 从Model接收数据在View上显示,并处理View的交互事件
在这三层中主要的是Presenter层,它将所有层连接在一起。
二、MVC跟MVP之间的区别
其实MVC跟MVP是很像的唯一的不同的是Presenter跟Controller,在MVC模式中三个层是可以互相交流的View层可以直接从Model中取数据显示,但是在MVP中View只跟Presenter层进行交互。
其实很多人都不明白这样分层的用意,这样分层完全的将关注点进行了分离,提高了程序的可测试性,在写单元测试的时候会很方便和更清晰。从另外一个方面 可以提高工作的协调性,如果是一个大项目的话,可以一些工作人员专注于页面的编写,提供一些接口给编写业务的同事,大大的解耦了工作的耦合性。
三、MVP的一些实现
在MVP的实现上国内有很多的示例,这些示例不尽相同,我们这次重构使用的MVP其实是进行了很多的简化,当然这也是调研了很久的国内用的比较多的实现方式。以登录为案例,我们主要有四个文件(LoginActivity,ILoginView,LoginPresenter,LoginModel)。具体如下:
LoginActivity
public class LoginActivity extends Activity implements ILoginView {
oncreate(){
presenter=new LoginPresenter();
presenter.setView(this);
}
onDestory(){
presenter.setView(null);
presenter=null;
}
}
ILoginView
interface ILoginView{
void loginSuccessful();
void loginFail();
}
LoginPresenter
public class LoginPresenter{
ILogin view;
doLogin(String username,String psd){
// to server do login
//result successful
view.loginSuccessful();
}
}
上面的代码我写的很随意,其实大家可以看到 在上面基本上没有用的Model层,在实际用的过程中Model我们只用来当成一个数据对象来用,并没有像在介绍中的Model中处理逻辑,而是把所有的逻辑都放在了Presenter层。这种做法在一些简单的页面会显的很简洁,所以用的人不少。
之前看过国外一个MVP的示例感觉不错,可以值得参考:
在这个示例中是一个记事本APP,简单的业务逻辑就是可以添加和删除事项。
在这里主要是依靠一个接口将所有层连接起来,接口逻辑如下:
首先创建这个接口MVP_Main
public interface MVP_Main { /** * Required View methods available to Presenter. * A passive layer, responsible to show data * and receive user interactions */ interface RequiredViewOps { // View operations permitted to Presenter Context getAppContext(); Context getActivityContext(); void notifyItemInserted(int layoutPosition); void notifyItemRangeChanged(int positionStart, int itemCount); } /** * Operations offered to View to communicate with Presenter. * Processes user interactions, sends data requests to Model, etc. */ interface ProvidedPresenterOps { // Presenter operations permitted to View void clickNewNote(EditText editText); // setting up recycler adapter int getNotesCount(); NotesViewHolder createViewHolder(ViewGroup parent, int viewType); void bindViewHolder(NotesViewHolder holder, int position); } /** * Required Presenter methods available to Model. */ interface RequiredPresenterOps { // Presenter operations permitted to Model Context getAppContext(); Context getActivityContext(); } /** * Operations offered to Model to communicate with Presenter * Handles all data business logic. */ interface ProvidedModelOps { // Model operations permitted to Presenter int getNotesCount(); Note getNote(int position); int insertNote(Note note); boolean loadData(); }}
接下来是View层,这里的View是一个Activity,View可以是Fragment也可以是一个控件。
public class MainActivity extends AppCompatActivity implements View.OnClickListener, MVP_Main.RequiredViewOps { private MVP_Main.ProvidedPresenterOps mPresenter; private EditText mTextNewNote; private ListNotes mListAdapter; @Override public void onClick(View v) { switch (v.getId()) { case R.id.fab:{ // Adds a new note mPresenter.clickNewNote(mTextNewNote); } } } @Override public Context getActivityContext() { return this; } @Override public Context getAppContext() { return getApplicationContext(); } // Notify the RecyclerAdapter that a new item was inserted @Override public void notifyItemInserted(int adapterPos) { mListAdapter.notifyItemInserted(adapterPos); } // notify the RecyclerAdapter that items has changed @Override public void notifyItemRangeChanged(int positionStart, int itemCount){ mListAdapter.notifyItemRangeChanged(positionStart, itemCount); } // notify the RecyclerAdapter that data set has changed @Override public void notifyDataSetChanged() { mListAdapter.notifyDataSetChanged(); } // Recycler adapter // This class could have their own Presenter, but for the sake of // simplicity, will use only one Presenter. // The adapter is passive and all the processing occurs // in the Presenter layer. private class ListNotes extends RecyclerView.Adapter<NotesViewHolder> { @Override public int getItemCount() { return mPresenter.getNotesCount(); } @Override public NotesViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { return mPresenter.createViewHolder(parent, viewType); } @Override public void onBindViewHolder(NotesViewHolder holder, int position) { mPresenter.bindViewHolder(holder, position); } }}
在Presenter层有一些小的技术点,我们在持有Activity的引用的时候,应该持有的是弱引用,因为Activity可以随时被销毁,另外可能会造成内存泄漏,所以在getView的时候一定要处理NULL异常。
public class MainPresenter implements MVP_Main.ProvidedPresenterOps, MVP_Main.RequiredPresenterOps { // View reference. We use as a WeakReference // because the Activity could be destroyed at any time // and we don't want to create a memory leak private WeakReference<MVP_Main.RequiredViewOps> mView; // Model reference private MVP_Main.ProvidedModelOps mModel; /** * Presenter Constructor * @param view MainActivity */ public MainPresenter(MVP_Main.RequiredViewOps view) { mView = new WeakReference<>(view); } /** * Return the View reference. * Throw an exception if the View is unavailable. */ private MVP_Main.RequiredViewOps getView() throws NullPointerException{ if ( mView != null ) return mView.get(); else throw new NullPointerException("View is unavailable"); } /** * Retrieves total Notes count from Model * @return Notes list size */ @Override public int getNotesCount() { return mModel.getNotesCount(); } /** * Creates the RecyclerView holder and setup its view * @param parent Recycler viewGroup * @param viewType Holder type * @return Recycler ViewHolder */ @Override public NotesViewHolder createViewHolder(ViewGroup parent, int viewType) { NotesViewHolder viewHolder; LayoutInflater inflater = LayoutInflater.from(parent.getContext()); View viewTaskRow = inflater.inflate(R.layout.holder_notes, parent, false); viewHolder = new NotesViewHolder(viewTaskRow); return viewHolder; } /** * Binds ViewHolder with RecyclerView * @param holder Holder to bind * @param position Position on Recycler adapter */ @Override public void bindViewHolder(final NotesViewHolder holder, int position) { final Note note = mModel.getNote(position); holder.text.setText( note.getText() ); holder.date.setText( note.getDate() ); holder.btnDelete.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { clickDeleteNote(note, holder.getAdapterPosition(), holder.getLayoutPosition()); } }); } /** * @return Application context */ @Override public Context getAppContext() { try { return getView().getAppContext(); } catch (NullPointerException e) { return null; } } /** * @return Activity context */ @Override public Context getActivityContext() { try { return getView().getActivityContext(); } catch (NullPointerException e) { return null; } } /** * Called by View when user clicks on new Note button. * Creates a Note with text typed by the user and asks * Model to insert it in DB. * @param editText EditText with text typed by user */ @Override public void clickNewNote(final EditText editText) { getView().showProgress(); final String noteText = editText.getText().toString(); if ( !noteText.isEmpty() ) { new AsyncTask<Void, Void, Integer>() { @Override protected Integer doInBackground(Void... params) { // Inserts note in Model, returning adapter position return mModel.insertNote(makeNote(noteText)); } @Override protected void onPostExecute(Integer adapterPosition) { try { if (adapterPosition > -1) { // Note inserted getView().clearEditText(); getView().notifyItemInserted(adapterPosition + 1); getView().notifyItemRangeChanged(adapterPosition, mModel.getNotesCount()); } else { // Informs about error getView().hideProgress(); getView().showToast(makeToast("Error creating note [" + noteText + "]")); } } catch (NullPointerException e) { e.printStackTrace(); } } }.execute(); } else { try { getView().showToast(makeToast("Cannot add a blank note!")); } catch (NullPointerException e) { e.printStackTrace(); } } } /** * Creates a Note object with given text * @param noteText String with Note text * @return A Note object */ public Note makeNote(String noteText) { Note note = new Note(); note.setText( noteText ); note.setDate(getDate()); return note; }}
接下来 就是Model层了,这个层跟我们使用的有较大差别,在这里Model拥有Presenter层的引用,并且在这里会处理一些数据的处理。
public class MainModel implements MVP_Main.ProvidedModelOps { // Presenter reference private MVP_Main.RequiredPresenterOps mPresenter; private DAO mDAO; // Recycler data public ArrayList<Note> mNotes; /** * Main constructor, called by Activity during MVP setup * @param presenter Presenter instance */ public MainModel(MVP_Main.RequiredPresenterOps presenter) { this.mPresenter = presenter; mDAO = new DAO( mPresenter.getAppContext() ); } /** * Inserts a note on DB * @param note Note to insert * @return Note's position on ArrayList */ @Override public int insertNote(Note note) { Note insertedNote = mDAO.insertNote(note); if ( insertedNote != null ) { loadData(); return getNotePosition(insertedNote); } return -1; } /** * Loads all Data, getting notes from DB * @return true with success */ @Override public boolean loadData() { mNotes = mDAO.getAllNotes(); return mNotes != null; } /** * Gets a specific note from notes list using its array position * @param position Array position * @return Note from list */ @Override public Note getNote(int position) { return mNotes.get(position); } /** * Get ArrayList size * @return ArrayList size */ @Override public int getNotesCount() { if ( mNotes != null ) return mNotes.size(); return 0; }}
最后我们还要处理一些配置导致的Activity重新创建的问题
public class MainActivity extends AppCompatActivity implements View.OnClickListener, MVP_Main.RequiredViewOps{ // … private void setupMVP() { // Check if StateMaintainer has been created if (mStateMaintainer.firstTimeIn()) { // Create the Presenter MainPresenter presenter = new MainPresenter(this); // Create the Model MainModel model = new MainModel(presenter); // Set Presenter model presenter.setModel(model); // Add Presenter and Model to StateMaintainer mStateMaintainer.put(presenter); mStateMaintainer.put(model); // Set the Presenter as a interface // To limit the communication with it mPresenter = presenter; } // get the Presenter from StateMaintainer else { // Get the Presenter mPresenter = mStateMaintainer.get(MainPresenter.class.getName()); // Updated the View in Presenter mPresenter.setView(this); } } // …}
总结起来,后面这种方式更加符合MVP的定义和使用。也更值得推荐。
另外附上项目源码:https://github.com/tutsplus/Android-ModelViewPresenter-Example
- Android MVP(Model View Presenter)
- Model-View-Presenter(MVP)概述
- MVP (Model View Presenter ) 简介
- 介绍MVP Model-View-Presenter在Android中的应用
- MVP(model,view Presenter) Pattern(模式) 学习
- Android MVP 架构二 Presenter与Model
- Android MVP 架构一 View与Presenter
- 在ASP.NET中实现Model-View-Presenter(MVP)
- NBearV3教程——MVP(Model/View/Presenter)
- NBearV3教程——MVP(Model/View/Presenter)篇
- 在ASP.NET中实现Model-View-Presenter(MVP)
- Model View Presenter (MVP) design pattern and data binding
- ASP.NET MVP( Model View Presenter )微软官方介绍
- 浅析MVP(Model-View-Presenter)架构及开发模式
- .NET平台上的Model-View-Presenter(MVP)模式实践
- Introduction to Model View Presenter on Android
- Introduction to Model View Presenter on Android
- Model View Presenter
- 数据结构:单源最短路径--Dijkstra算法
- JSP内置对象和EL内置对象及区别解析
- 高级数据结构及算法分类
- CreateProcess 创建带命令行参数的进程时,报错或者提示内存位置无效的可能的一个原因
- 抽象类
- Android MVP(Model View Presenter)
- Java1.8源码阅读-DualPivotQuicksort
- delphi 身份证号码相关处理单元
- 四叉树-2D平面的渲染剔除、碰撞次数优化
- ssh整合 出现Could not obtain transaction-synchronized Session for current thread
- Java高级之异常
- form表单验证input标签内容
- CodeForces
- 环信JAVA服务端调用各接口完整实例