Android中的MVP模型

Android中的MVP模型

作为开发人员,我们不应该仅仅满足于实现功能需求,还应该实现易于维护的代码。在这篇文章里,我们将简单地介绍一下Model-View-Presenter结构,并用一个简单的成绩登记系统来作为例子。

  • MVP和MVC的区别
  • MVP的优势
  • MVP的实现
  • Sample

MVP和MVC的区别

MVC模式(Model–view–controller)是软件工程中的一种软件架构模式,把软件系统分为三个基本部分:模型(Model)、视图(View)和控制器(Controller)。

  • 控制器(Controller)- 负责转发请求,对请求进行处理。
  • 视图(View) - 界面设计人员进行图形界面设计。
  • 模型(Model) - 程序员编写程序应有的功能(实现算法等等)、数据库专家进行数据管理和数据库设计(可以实现具体的功能)。

Model-view-presenter (MVP) 是使用者界面设计模式的一种,被广范用于便捷自动化单元测试和在呈现逻辑中改良分离关注点(separation of concerns)。

  • Model 定义使用者界面所需要被显示的资料模型,一个模型包含着相关的业务逻辑。
  • View 视图为呈现使用者界面的终端,用以表现来自 Model 的资料,和使用者命令路由再经过 Presenter 对事件处理后的资料。
  • Presenter 包含着元件的事件处理,负责检索 Model 取得资料,和将取得的资料经过格式转换与 View 进行沟通。 [ 维基百科 ]

MVP是建立在MVC的基础上的。Presenter和Controller功能差不多,它们都是负责Model和View之间的交流的。如下图所示, Controller没有像Presenter那样如此严格地管理Model和View.
MVC和MVP架构
在MVC模型里,View层可以直接从Model取数据。在MVP模型里,View是完全被动的,数据是Presenter提供的。MVC中,Controller可以被多个View共享。在MVP中,View和Presenter有着一对一的关系。


MVP的优势

正是如上的区别,使MVP能更好地分离关注点。使对象更小,Model和View之间的依赖更少。把三个核心层区分开,也提高了可测试性。

MVP的实现

  • Model:数据层
  • View:UI层,展示从Presenter接收的数据,接受用户输入。在安卓里,Activities,Fragments和android.view.View作为MVP 中的View。
  • Presenter:对UI层上的操作作出响应,在Model上执行业务逻辑,并将执行的结果传递给View。

MVP类图

Sample

下面来举个例子说明一下吧。老师可以登录该系统登记学生成绩。以下代码主要体现成绩登记部分。

基本接口和实现

Interface RequiredViewOps

    /**
     * View mandatory methods. Available to Presenter
     *      Presenter -> View
     */
    interface RequiredViewOps {
        void showToast(String msg);
        void showAlert(String msg);
        // any other ops
    }

Interface PresenterOps

    /**
     * Operations offered from Presenter to View
     *      View -> Presenter
     */
    interface PresenterOps{
        void onConfigurationChanged(RequiredViewOps view);
        void onDestroy(boolean isChangingConfig);
        void addGrade(Grade grade);
        // any other ops to be called from View
    }

Interface RequiredPresenterOps

   /**
     * Operations offered from Presenter to Model
     *      Model -> Presenter
     */
    interface RequiredPresenterOps {
        void onAddGrade(Grade grade);
        void onError(String errorMsg);
        // Any other returning operation Model -> Presenter
    }

Interface ModelOps

  /**
     * Model operations offered to Presenter
     *      Presenter -> Model
     */
    interface ModelOps {
        void insertGrade(Grade grade);
        void onDestroy();
        // Any other data operation
    }

GradePresenter Class

  public class GradePresenter
        implements RequiredPresenterOps,PresenterOps {

    // Layer View reference
    private RequiredViewOps mView;
    // Layer Model reference
    private ModelOps mModel;

    // Configuration change state
    private boolean mIsChangingConfig;

    public GradePresenterRequiredViewOps mView) {
        this.mView = mView;
        this.mModel = new MainModel(this);
    }

    /**
     * Sent from Activity after a configuration changes
     * @param view  View reference
     */
    @Override
    public void onConfigurationChanged(RequiredViewOps view) {
        this.mView =view;
    }

    /**
     * Receives {@link MainActivity#onDestroy()} event
     * @param isChangingConfig  Config change state
     */
    @Override
    public void onDestroy(boolean isChangingConfig) {
        mView = null;
        mIsChangingConfig = isChangingConfig;
        if ( !isChangingConfig ) {
            mModel.onDestroy();
        }
    }

    /**
     * Called by user interaction from {@link MainActivity}
     * creates a new Note
     */
    @Override
    public void addGrade(Grade grade) {
        mModel.insertGrade(grade);
    }


    /**
     * Called from {@link MainModel}
     * when a Note is inserted successfully
     */
    @Override
    public void onAddGrade(Grade grade){
        mView.get().showToast("New grade added");
    }

    /**
     * receive errors
     */
    @Override
    public void onError(String errorMsg) {
        mView.get().showAlert(errorMsg);
    }
}

GradeModel Class


public class GradeModel implements ModelOps {

    // Presenter reference
    private RequiredPresenterOps mPresenter;

    public MainModel(RequiredPresenterOps mPresenter) {
        this.mPresenter = mPresenter;
    }

    /**
     * Sent from {@link MainPresenter#onDestroy(boolean)}
     * Should stop/kill operations that could be running
     * and aren't needed anymore
     */
    @Override
    public void onDestroy() {
        // destroying actions
    }

    // Insert grade in DB
    @Override
    public void insertGrade(Grade grade) {
        // data business logic
        // ...
        mPresenter.onAddGrade(grade);
    }
}

生命周期的管理

我们需要添加第四个元素StateMaintainer,用来管理在Activity生命周期变化的时候,Presenter和Model的状态变化。下图展示了简化版的MVP在Activity生命周期的状态。

lifecycle

StateMaintainer Class

public class StateMaintainer {
    protected final String TAG = getClass().getSimpleName();

    private final String mStateMaintenerTag;
    private final WeakReference<FragmentManager> mFragmentManager;
    private StateMngFragment mStateMaintainerFrag;

    /**
     * Constructor
     * @param fragmentManager       FragmentManager reference
     * @param stateMaintainerTAG    the TAG used to insert the state maintainer fragment
     */
    public StateMaintainer(FragmentManager fragmentManager, String stateMaintainerTAG) {
        mFragmentManager = new WeakReference<>(fragmentManager);
        mStateMaintenerTag = stateMaintainerTAG;
    }

    /**
     * Create the state maintainer fragment
     * @return  true: the frag was created for the first time
     *          false: recovering the object
     */
    public boolean firstTimeIn() {
        try {
            // Recovering the reference
            mStateMaintainerFrag = (StateMngFragment)
                    mFragmentManager.get().findFragmentByTag(mStateMaintenerTag);

            // Creating a new RetainedFragment
            if (mStateMaintainerFrag == null) {
                Log.d(TAG, "Creating a new RetainedFragment " + mStateMaintenerTag);
                mStateMaintainerFrag = new StateMngFragment();
                mFragmentManager.get().beginTransaction()
                        .add(mStateMaintainerFrag, mStateMaintenerTag).commit();
                return true;
            } else {
                Log.d(TAG, "Returns a existent retained fragment existente " + mStateMaintenerTag);
                return false;
            }
        } catch (NullPointerException e) {
            Log.w(TAG, "Error firstTimeIn()");
            return false;
        }
    }


    /**
     * Insert Object to be preserved during configuration change
     * @param key   Object's TAG reference
     * @param obj   Object to maintain
     */
    public void put(String key, Object obj) {
        mStateMaintainerFrag.put(key, obj);
    }

    /**
     * Insert Object to be preserved during configuration change
     * Uses the Object's class name as a TAG reference
     * Should only be used one time by type class
     * @param obj   Object to maintain
     */
    public void put(Object obj) {
        put(obj.getClass().getName(), obj);
    }


    /**
     * Recovers saved object
     * @param key   TAG reference
     * @param <T>   Class type
     * @return      Objects
     */
    @SuppressWarnings("unchecked")
    public <T> T get(String key)  {
        return mStateMaintainerFrag.get(key);

    }

    /**
     * Verify the object existence 
     * @param key   Obj TAG
     */
    public boolean hasKey(String key) {
        return mStateMaintainerFrag.get(key) != null;
    }


    /**
     * Save and manages objects that show be preserved
     * during configuration changes.
     */
    public static class StateMngFragment extends Fragment {
        private HashMap<String, Object> mData = new HashMap<>();

        @Override
        public void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            // Grants that the frag will be preserved
            setRetainInstance(true);
        }

        /**
         * Insert objects
         * @param key   reference TAG
         * @param obj   Object to save
         */
        public void put(String key, Object obj) {
            mData.put(key, obj);
        }

        /**
         * Insert obj using class name as TAG
         * @param object    obj to save
         */
        public void put(Object object) {
            put(object.getClass().getName(), object);
        }

        /**
         * Recover obj
         * @param key   reference TAG
         * @param <T>   Class
         * @return      Obj saved
         */
        @SuppressWarnings("unchecked")
        public <T> T get(String key) {
            return (T) mData.get(key);
        }
    }

}

MainActivity Activity (View layer)

public class MainActivity extends AppCompatActivity
        implements RequiredViewOps {

    protected final String TAG = getClass().getSimpleName();

    // Responsible to maintain the Objects state
    // during changing configuration
    private final StateMaintainer mStateMaintainer =
            new StateMaintainer( this.getFragmentManager(), TAG );

    // Presenter operations
    private PresenterOps mPresenter;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        startMVPOps();
        setContentView(R.layout.activity_main);
        Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
        setSupportActionBar(toolbar);
        FloatingActionButton fab = (FloatingActionButton) findViewById(R.id.fab);
    }


    /**
     * Initialize and restart the Presenter.
     * This method should be called after {@link Activity#onCreate(Bundle)}
     */
    public void startMVPOps() {
        try {
            if ( mStateMaintainer.firstTimeIn() ) {
                Log.d(TAG, "onCreate() called for the first time");
                initialize(this);
            } else {
                Log.d(TAG, "onCreate() called more than once");
                reinitialize(this);
            }
        } catch ( InstantiationException | IllegalAccessException e ) {
            Log.d(TAG, "onCreate() " + e );
            throw new RuntimeException( e );
        }
    }


    /**
     * Initialize relevant MVP Objects.
     * Creates a Presenter instance, saves the presenter in {@link StateMaintainer}
     */
    private void initialize( RequiredViewOps view )
            throws InstantiationException, IllegalAccessException{
        mPresenter = new MainPresenter(view);
        mStateMaintainer.put(PresenterOps.class.getSimpleName(), mPresenter);
    }

    /**
     * Recovers Presenter and informs Presenter that occurred a config change.
     * If Presenter has been lost, recreates a instance
     */
    private void reinitialize( RequiredViewOps view)
            throws InstantiationException, IllegalAccessException {
        mPresenter = mStateMaintainer.get(PresenterOps.class.getSimpleName() );

        if ( mPresenter == null ) {
            Log.w(TAG, "recreating Presenter");
            initialize( view );
        } else {
            mPresenter.onConfigurationChanged( view );
        }
    }


    // Show AlertDialog
    @Override
    public void showAlert(String msg) {
        // show alert Box
    }

    // Show Toast
    @Override
    public void showToast(String msg) {
        Toast.makeText(getApplicationContext(), msg, Toast.LENGTH_SHORT).show;
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值