从github上的优秀实例看MVP模式

本文通过分析GitHub上的一个MVP模式实例,揭示了MVP模式的精髓。在该实例中,视图层仅处理UI操作,如显示进度对话框和渲染列表;presenter作为桥梁协调视图和数据层;数据源负责获取数据。通过定义接口和代理层,实现了组件间的解耦和通信,使得代码结构清晰,易于维护。

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

github上有一个关于MVP模式学习的实例https://github.com/antoniolg/androidmvp,虽然只有简单的几个类,却收获了几千个星。这个例子确实通俗易懂,直观的体现出了MVP模式的特点:


考虑这样一个需求,页面显示一个列表,数据源需要特别获取。

分析一下这个过程,页面首先显示出进度提示框表示正在加载,然后数据源获取数据并传给页面的列表进行渲染。

MVP模式,model ,view,presenter。。视图层只处理UI操作,包括显示dialog,拿到数据后的处理(关闭dialog并渲染列表),这里可以细分为关闭dialog和为列表的适配器设置数据列表。     presenter是视图层和数据层沟通的桥梁,控制着双方的行为。  数据源则负责获取数据并返回。


首先对三个类进行抽象:

1.视图层:

package com.antonioleiva.mvpexample.app.main;

import java.util.List;

public interface MainView {

    void showProgress();

    void hideProgress();

    void setItems(List<String> items);

    void showMessage(String message);
}


2.数据层:

package com.antonioleiva.mvpexample.app.main;

import java.util.List;

public interface FindItemsInteractor {

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

    void findItems(OnFinishedListener listener);
}

这里一个接口用来返回拿到的数据,接口中的方法将数据传入。在获取数据的方法里将这个接口传入,接口的实例化是在代理中实现的。


3.代理层:

package com.antonioleiva.mvpexample.app.main;

public interface MainPresenter {

    void onResume();

    void onItemClicked(int position);

    void onDestroy();
}
视图层在调用onResume和onDestroy时的逻辑也要通过代理的方法实现。



下面是创建的实例,首先是数据实例:

1.数据层:

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.视图层:
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, new FindItemsInteractorImpl());
    }

    @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);
    }
}

视图层拥有一个代理实例,将几个生命阶段的逻辑以及列表项点击全部托管给代理进行处理,自己只负责实现接口中的几个ui操作方法。


3.代理层:

public class MainPresenterImpl implements MainPresenter, FindItemsInteractor.OnFinishedListener {

    private MainView mainView;
    private FindItemsInteractor findItemsInteractor;

    public MainPresenterImpl(MainView mainView, FindItemsInteractor findItemsInteractor) {
        this.mainView = mainView;
        this.findItemsInteractor = findItemsInteractor;
    }

    @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();
        }
    }

    public MainView getMainView() {
        return mainView;
    }
}

首先代理的构造器要传入视图和数据两个实例,然后通过调用这两个实例的方法来完成控制。这里代理的几个任务有,在视图销毁时设置解绑,对列表项点击事件的响应,在视图刚开始时控制视图显示等待中并开始控制数据源获取数据,在数据获取完成后通过实现的回调接口将数据传给视图层。

需要注意的是代理实现了数据层的接口,以此来接收并处理数据。


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值