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

原创粉丝点击