Android MVP模式:如何实现从Model到Presenter的数据回传
兄弟,我太懂你这种Activity越写越臃肿的痛苦了——本来想清爽写个功能,结果逻辑、UI、网络请求全堆一块,改个小需求都得翻半天代码。用MVP来拆分确实是个好路子,关于Model怎么把数据回传给Presenter,我给你捋几个常用且靠谱的方案:
方案一:接口回调(MVP经典实现,首推)
这是MVP模式里最基础也最稳妥的方式,完全不需要额外依赖,还能避免你担心的静态引用带来的内存泄漏问题。核心思路是给Model定义一个回调接口,Presenter实现这个接口,Model拿到数据后调用接口方法通知Presenter。
步骤示例:
- 定义回调接口
// 定义数据加载完成的回调接口 public interface OnDataLoadedListener { void onDataLoaded(UserData data); // 数据加载成功的回调 void onLoadFailed(String errorMsg); // 加载失败的回调 }
- 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(); } }
- 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
相关产品推荐
相关产品推荐

