Android MVP

本文详细介绍了Model-View-Presenter (MVP) 设计模式的概念、原理及其在Android开发中的应用。MVP模式通过将应用程序分为Model、View和Presenter三个部分,实现了更好的代码组织和模块解耦,有助于提高软件的可测试性和维护性。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

原文地址
https://github.com/konmik/konmik.github.io/wiki/Introduction-to-Model-View-Presenter-on-Android

1、MVP是什么

  1. View 视图是一个显示数据并对用户行为做出反应的层。在Android系统上,这可能是一个Activity,Fragment,或者是一个view的对话框。
  2. Model 模型是一个数据访问层,如数据库应用程序接口或远程服务器应用程序接口。
  3. Presenter 主持者(暂且这样翻译)是一个从Model(模型)层中提供数据给View(视图)的层。Presenter还处理后台任务。

2、使用MVP的原因

1、简单明了,使用代码分层结构明显(解耦)

如果仅仅用MV的话,view与data之间显示的关系如下,我们称之为everything is connected with everything(一切与一切连接)
这里写图片描述

如果这个图表看起来不复杂,那么想想每个视图都可以消失,并在随机出现。不要忘记保存/恢复视图。有时候还会将一些后台任务附加到临时视图上。

这样就产生了everything is connected with everything的对象,它就是activity
这里写图片描述

(这样产生的activity,它不仅控件,view,而且还要从后台或者数据库获取数据,从而使用activity不仅代码量庞大,而且臃肿,不容易维护和扩展,如果view层变得,甚至只改一点点,我们activity可能都需要改很多代码)。

因此,为了解决上述问题
这里写图片描述

  1. 复杂的任务被分解成更简单的任务,更容易解决。
  2. 较小的对象,较少的错误,更容易调试。
  3. 可测试。

MVP视图层变得如此简单,它甚至不需要有回调要求的数据。视图逻辑变得非常线性。

2、后台任务

当我们写一个Activity、Fragment或一个自定义View时,可以将所有与后台任务连接的方法都放在一个不同的外部或静态类上。这样,你的背景任务将不会被连接到一个活动,不会泄漏内存,不会依赖于Activity。我们称这样的对象“主持者”。

有几个不同的方法来处理后台任务,正确使用的MVP 库是一个最可靠的选择。

这是如何工作的呢?

下面是一个图表,显示在配置更改或内存事件期间,在不同应用程序部分发生了什么。每一个安卓开发人员都应该知道这个数据,但是这个数据是令人惊讶的很难找到。

这里写图片描述

情况1:一个配置变化通常发生在用户翻转屏幕,更改语言设置,连接外部显示器,等更多关于这个事件你可以看这里:configchanges。

情况2:活动启动时,用户已设置“不要在开发商设置另一个活动”复选框成为最高。

情况3:如果没有足够的内存和应用程序是在后台,一个进程重新启动。

结论
可以看出,一个片段setretaininstance(真正的)没有帮助,在这里我们需要保存/恢复这样的片段的状态。因此,(为了使问题更明了,暂时忽略这个问题)
这里写图片描述
现在看起来好多了。我们只需要写两个代码来完全恢复在任何可能的情况下的应用程序:

  1. save/restore 给Activity,View,Fragment,DialogFragment
  2. 在进程重新启动的情况下重新启动后台请求。

第一部分的工作通过Android API的相关调用就可以完成,第二部分的就是我们Presenter的工作,Presenter仅仅记住它应该执行的请求,如果一个进程重新启动,Presenter将再次执行。

3、MVP的优缺点

优点:
1、模型与视图完全分离,我们可以修改视图而不影响模型
2、可以更高效地使用模型,因为所有的交互都发生在一个地方——Presenter内部
3、我们可以将一个Presenter用于多个视图,而不需要改变Presenter的逻辑。这个特性非常的有用,因为视图的变化总是比模型的变化频繁。
4、如果我们把逻辑放在Presenter中,那么我们就可以脱离用户接口来测试这些逻辑(单元测试)
缺点:
由于对视图的渲染放在了Presenter中,所以视图和Presenter的交互会过于频繁。还有一点需要明白,如果Presenter过多地渲染了视图,往往会使得它与特定的视图的联系过于紧密。一旦视图需要变更,那么Presenter也需要变更了。比如说,原本用来呈现Html的Presenter现在也需要用于呈现Pdf了,那么视图很有可能也需要变更。

4、MVC与MVP的区别

更加解耦,model与view之间不存在直接通信。
这里写图片描述

5、Demo Listview显示

github比较经典的demo
地址https://github.com/antoniolg/androidmvp
这里写图片描述
运行效果
这里写图片描述

1、首先View层

定义view接口MainView

public interface MainView {

    void showProgress();

    void hideProgress();

    void setItems(List<String> items);

    void showMessage(String message);
}

view中item事件接口:FindItemsInteractor

public interface FindItemsInteractor {

    interface OnFinishedListener {
        void onFinished(List<String> items);
    }

    void findItems(OnFinishedListener listener);
}

view接口的实现类

public class FindItemsInteractorImpl implements FindItemsInteractor {
    @Override
    public void findItems(final OnFinishedListener listener) {
        new Handler().postDelayed(new Runnable() {
            @Override
            public void run() {
                listener.onFinished(createArrayList());
            }
        }, 2000);
    }

    private List<String> createArrayList() {
        return Arrays.asList(
                "Item 1",
                "Item 2",
                "Item 3",
                "Item 4",
                "Item 5",
                "Item 6",
                "Item 7",
                "Item 8",
                "Item 9",
                "Item 10"
        );
    }
}

2、创建Presenter接口,并实现

public interface MainPresenter {

    void onResume();

    void onItemClicked(int position);

    void onDestroy();
}

实现类

public class MainPresenterImpl implements MainPresenter, FindItemsInteractor.OnFinishedListener {

    private MainView mainView;
    private FindItemsInteractor findItemsInteractor;

    public MainPresenterImpl(MainView mainView) {
        this.mainView = mainView;
        findItemsInteractor = new FindItemsInteractorImpl();
    }

    @Override
    public void onResume() {
        if (mainView != null) {
            mainView.showProgress();
        }

        findItemsInteractor.findItems(this);
    }

    @Override
    public void onItemClicked(int position) {
        if (mainView != null) {
            mainView.showMessage(String.format("Position %d clicked", position + 1));
        }
    }

    @Override
    public void onDestroy() {
        mainView = null;
    }

    @Override
    public void onFinished(List<String> items) {
        if (mainView != null) {
            mainView.setItems(items);
            mainView.hideProgress();
        }
    }
}

最后在activity中执行

/*
 *
 *  * Copyright (C) 2014 Antonio Leiva Gordillo.
 *  *
 *  * Licensed under the Apache License, Version 2.0 (the "License");
 *  * you may not use this file except in compliance with the License.
 *  * You may obtain a copy of the License at
 *  *
 *  *      http://www.apache.org/licenses/LICENSE-2.0
 *  *
 *  * Unless required by applicable law or agreed to in writing, software
 *  * distributed under the License is distributed on an "AS IS" BASIS,
 *  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  * See the License for the specific language governing permissions and
 *  * limitations under the License.
 *
 */

package com.antonioleiva.mvpexample.app.main;

import android.app.Activity;
import android.os.Bundle;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.widget.AdapterView;
import android.widget.ArrayAdapter;
import android.widget.ListView;
import android.widget.ProgressBar;
import android.widget.Toast;

import com.antonioleiva.mvpexample.app.R;

import java.util.List;

public class MainActivity extends Activity implements MainView, AdapterView.OnItemClickListener {

    private ListView listView;
    private ProgressBar progressBar;
    private MainPresenter presenter;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        listView = (ListView) findViewById(R.id.list);
        listView.setOnItemClickListener(this);
        progressBar = (ProgressBar) findViewById(R.id.progress);
        presenter = new MainPresenterImpl(this);

    }

    @Override
    protected void onResume() {
        super.onResume();
        presenter.onResume();
    }

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        getMenuInflater().inflate(R.menu.main, menu);
        return true;
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        switch (item.getItemId()) {
            case R.id.action_settings:
                return true;
            default:
                return super.onOptionsItemSelected(item);
        }
    }

    @Override
    protected void onDestroy() {
        presenter.onDestroy();
        super.onDestroy();
    }

    @Override
    public void showProgress() {
        progressBar.setVisibility(View.VISIBLE);
        listView.setVisibility(View.INVISIBLE);
    }

    @Override
    public void hideProgress() {
        progressBar.setVisibility(View.INVISIBLE);
        listView.setVisibility(View.VISIBLE);
    }

    @Override
    public void setItems(List<String> items) {
        listView.setAdapter(new ArrayAdapter<>(this, android.R.layout.simple_list_item_1, items));
    }

    @Override
    public void showMessage(String message) {
        Toast.makeText(this, message, Toast.LENGTH_LONG).show();
    }

    @Override
    public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
        presenter.onItemClicked(position);
    }
}

介绍比较全面的博客
https://segmentfault.com/a/1190000003927200

github比较经典的demo
地址https://github.com/antoniolg/androidmvp

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值