You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Android MVP模式:如何实现从Model到Presenter的数据回传

兄弟,我太懂你这种Activity越写越臃肿的痛苦了——本来想清爽写个功能,结果逻辑、UI、网络请求全堆一块,改个小需求都得翻半天代码。用MVP来拆分确实是个好路子,关于Model怎么把数据回传给Presenter,我给你捋几个常用且靠谱的方案:

方案一:接口回调(MVP经典实现,首推)

这是MVP模式里最基础也最稳妥的方式,完全不需要额外依赖,还能避免你担心的静态引用带来的内存泄漏问题。核心思路是给Model定义一个回调接口,Presenter实现这个接口,Model拿到数据后调用接口方法通知Presenter。

步骤示例:

  1. 定义回调接口
// 定义数据加载完成的回调接口
public interface OnDataLoadedListener {
    void onDataLoaded(UserData data); // 数据加载成功的回调
    void onLoadFailed(String errorMsg); // 加载失败的回调
}
  1. Model层实现数据请求并触发回调
public class UserModel {
    // 模拟网络请求/本地数据库查询,接收回调接口作为参数
    public void fetchUserData(String userId, OnDataLoadedListener listener) {
        // 模拟耗时操作(实际项目里可能是Retrofit请求、Room查询等)
        new Thread(() -> {
            try {
                Thread.sleep(1000);
                // 假设拿到了用户数据
                UserData data = new UserData(userId, "张三", "zhangsan@example.com");
                // 切回主线程更新UI(Android里必须注意线程切换)
                new Handler(Looper.getMainLooper()).post(() -> {
                    listener.onDataLoaded(data);
                });
            } catch (Exception e) {
                new Handler(Looper.getMainLooper()).post(() -> {
                    listener.onLoadFailed(e.getMessage());
                });
            }
        }).start();
    }
}
  1. Presenter层实现回调接口,处理数据并通知View
public class UserPresenter implements OnDataLoadedListener {
    private UserView mView; // 持有View层接口引用
    private UserModel mModel;

    public UserPresenter(UserView view) {
        mView = view;
        mModel = new UserModel();
    }

    // 对外暴露的获取数据方法,供View层调用
    public void getUserData(String userId) {
        mView.showLoading(); // 通知View显示加载状态
        mModel.fetchUserData(userId, this); // 调用Model方法,把自己作为回调传进去
    }

    @Override
    public void onDataLoaded(UserData data) {
        mView.hideLoading();
        mView.showUserData(data); // 把数据传递给View层展示
    }

    @Override
    public void onLoadFailed(String errorMsg) {
        mView.hideLoading();
        mView.showError(errorMsg); // 通知View显示错误信息
    }

    // View销毁时释放引用,避免内存泄漏
    public void onDestroy() {
        mView = null;
    }
}
方案二:响应式编程(RxJava/Kotlin协程)

如果你的项目已经引入了RxJava或者用Kotlin开发,用响应式框架来处理数据回调会更简洁优雅,还能自动处理线程切换和生命周期管理。

RxJava示例:

// Model层返回Observable
public class UserModel {
    public Observable<UserData> fetchUserData(String userId) {
        return Observable.create(emitter -> {
            // 模拟耗时操作
            UserData data = new UserData(userId, "张三", "zhangsan@example.com");
            emitter.onNext(data);
            emitter.onComplete();
        }).subscribeOn(Schedulers.io()) // 耗时操作在IO线程执行
          .observeOn(AndroidSchedulers.mainThread()); // 回调在主线程执行
    }
}

// Presenter层订阅Observable
public class UserPresenter {
    private UserView mView;
    private UserModel mModel;
    private Disposable mDisposable; // 用于管理订阅生命周期

    public UserPresenter(UserView view) {
        mView = view;
        mModel = new UserModel();
    }

    public void getUserData(String userId) {
        mView.showLoading();
        mDisposable = mModel.fetchUserData(userId)
                .subscribe(
                        data -> {
                            mView.hideLoading();
                            mView.showUserData(data);
                        },
                        throwable -> {
                            mView.hideLoading();
                            mView.showError(throwable.getMessage());
                        }
                );
    }

    // View销毁时取消订阅,避免内存泄漏
    public void onDestroy() {
        if (mDisposable != null && !mDisposable.isDisposed()) {
            mDisposable.dispose();
        }
        mView = null;
    }
}

Kotlin协程示例:

// Model层用挂起函数返回数据
class UserModel {
    suspend fun fetchUserData(userId: String): UserData {
        delay(1000) // 模拟耗时操作
        return UserData(userId, "张三", "zhangsan@example.com")
    }
}

// Presenter层用协程处理
class UserPresenter(private val mView: UserView) {
    private val mModel = UserModel()
    private val mScope = CoroutineScope(Dispatchers.Main + Job())

    fun getUserData(userId: String) {
        mView.showLoading()
        mScope.launch {
            try {
                // 切换到IO线程执行耗时操作
                val data = withContext(Dispatchers.IO) {
                    mModel.fetchUserData(userId)
                }
                mView.hideLoading()
                mView.showUserData(data)
            } catch (e: Exception) {
                mView.hideLoading()
                mView.showError(e.message ?: "加载失败")
            }
        }
    }

    // View销毁时取消协程
    fun onDestroy() {
        mScope.cancel()
    }
}
方案三:EventBus(不推荐,应急可用)

比如GreenRobot的EventBus,通过发送事件的方式让Model把数据传给Presenter。但这种方式会让代码耦合度变高,调试起来也麻烦,除非项目已经在用EventBus,不然不建议优先选择。

简单示例:

// 定义事件类
public class UserDataEvent {
    private UserData mData;

    public UserDataEvent(UserData data) {
        mData = data;
    }

    public UserData getData() {
        return mData;
    }
}

// Model层发送事件
public class UserModel {
    public void fetchUserData(String userId) {
        new Thread(() -> {
            UserData data = new UserData(userId, "张三", "zhangsan@example.com");
            EventBus.getDefault().post(new UserDataEvent(data));
        }).start();
    }
}

// Presenter层注册并接收事件
public class UserPresenter {
    private UserView mView;

    public UserPresenter(UserView view) {
        mView = view;
        EventBus.getDefault().register(this);
    }

    public void getUserData(String userId) {
        mView.showLoading();
        new UserModel().fetchUserData(userId);
    }

    @Subscribe(threadMode = ThreadMode.MAIN)
    public void onUserDataReceived(UserDataEvent event) {
        mView.hideLoading();
        mView.showUserData(event.getData());
    }

    public void onDestroy() {
        EventBus.getDefault().unregister(this);
        mView = null;
    }
}
最后提醒

不管用哪种方式,一定要注意内存泄漏问题:在View(比如Activity/Fragment)销毁时,Presenter要及时解除和View、Model的引用,或者取消订阅/协程,避免因为持有Context导致内存泄漏。

内容的提问来源于stack exchange,提问作者Aldridge1991

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.21 06:36:04