Android MVVM中如何安全向ViewModel传递Activity引用?
解决MVVM中ViewModel无法传递Activity引用给Model的方案
在MVVM架构里,ViewModel确实不能持有Activity、View或生命周期相关引用,否则会引发内存泄漏风险。我们可以通过事件驱动+View层代理的方式解决这个问题——让ViewModel只负责通知View需要执行权限请求,由View(本身就是Activity)来传递自身引用给Model,同时保证架构的职责清晰。
下面结合你的代码给出具体实现步骤:
1. 定义单次事件通知类(避免重复触发)
普通MutableLiveData在配置变化(比如屏幕旋转)后可能重复触发事件,我们用SingleLiveEvent来确保事件只被消费一次:
public class SingleLiveEvent<T> extends MutableLiveData<T> { private final AtomicBoolean mPending = new AtomicBoolean(false); @Override public void observe(@NonNull LifecycleOwner owner, @NonNull Observer<? super T> observer) { super.observe(owner, t -> { if (mPending.compareAndSet(true, false)) { observer.onChanged(t); } }); } @Override public void setValue(T value) { mPending.set(true); super.setValue(value); } // 无参数的事件触发方法 public void call() { setValue(null); } }
2. 修改ViewModel:通过事件通知View请求权限
ViewModel只负责判断权限状态,然后发送事件通知View执行操作,不直接接触Activity或Model的权限请求逻辑:
public class MainActivityVM extends AndroidViewModel { // 权限请求事件 private final SingleLiveEvent<Void> requestPermissionEvent = new SingleLiveEvent<>(); private MyService model; public MainActivityVM(@NonNull Application application) { super(application); } // 暴露事件给View观察 public SingleLiveEvent<Void> getRequestPermissionEvent() { return requestPermissionEvent; } @Override public void onCreate() { model = new MyService(); // 先判断是否已有权限 if (!model.permissionAcquired()) { // 发送事件,通知View发起权限请求 requestPermissionEvent.call(); } else { // 已有权限,直接获取数据 model.getData(); } } // 权限成功后的回调,继续业务逻辑 public void onPermissionGranted() { if (model != null) { model.getData(); } } // 权限失败后的回调,处理异常逻辑 public void onPermissionDenied() { // 可以在这里通知View显示提示,或者记录日志 } // 其他生命周期方法保持不变 @Override public void onPause() {} @Override public void onResume() {} @Override public void onDestroy() {} public static class Factory extends ViewModelProvider.NewInstanceFactory { @NonNull private final Application mApplication; public Factory(@NonNull Application application) { mApplication = application; } @Override public <T extends ViewModel> T create(Class<T> modelClass) { return (T) new MainActivityVM(mApplication); } } }
3. 修改Model:接收Activity引用和结果回调
调整Model的权限请求方法,让它接收Activity参数和回调接口,以便把权限结果通知回ViewModel:
public class MyService { private HealthDataStore mStore; // 假设已初始化 private static final String APP_TAG = "MyService"; public void getData() { if (permissionAcquired()) { fetchData(); // 实际获取数据的逻辑 } } // 检查权限是否已获取 private boolean permissionAcquired() { HealthPermissionManager pmsManager = new HealthPermissionManager(mStore); try { Set<PermissionKey> grantedPermissions = pmsManager.getGrantedPermissions(); PermissionKey permKey = new PermissionKey(HealthConstants.StepCount.HEALTH_DATA_TYPE, PermissionType.READ); return grantedPermissions.contains(permKey); } catch (Exception e) { Log.e(APP_TAG, "权限检查失败", e); return false; } } // 修改权限请求方法,接收Activity和回调 public void requestPermission(Activity activity, PermissionResultCallback callback) { PermissionKey permKey = new PermissionKey(HealthConstants.StepCount.HEALTH_DATA_TYPE, PermissionType.READ); HealthPermissionManager pmsManager = new HealthPermissionManager(mStore); try { pmsManager.requestPermissions(Collections.singleton(permKey), activity).setResultListener(result -> { Log.d(APP_TAG, "权限回调已接收"); Map<PermissionKey, Boolean> resultMap = result.getResultMap(); if (resultMap.containsValue(Boolean.FALSE)) { callback.onPermissionDenied(); } else { callback.onPermissionGranted(); fetchData(); } }); } catch (Exception e) { Log.e(APP_TAG, "权限请求失败", e); callback.onPermissionDenied(); } } // 实际获取数据的逻辑 private void fetchData() { // 这里写原来的mReporter.start(mStepCountObserver)等数据获取代码 } // 权限结果回调接口 public interface PermissionResultCallback { void onPermissionGranted(); void onPermissionDenied(); } }
4. 修改View(MainActivity):监听事件并传递Activity引用
View作为Activity,持有自身引用是合法的,它负责监听ViewModel的事件,调用Model的权限请求方法,并把结果回传给ViewModel:
private MyApp app; private MainActivityVM viewModel; private MyService myService; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); app = (MyApp) this.getApplication(); ActivityMainBinding binding = DataBindingUtil.setContentView(this, R.layout.activity_main); MainActivityVM.Factory factory = new MainActivityVM.Factory(app); viewModel = ViewModelProviders.of(this, factory).get(MainActivityVM.class); binding.setVm(viewModel); // 初始化MyService(可替换为依赖注入方式) myService = new MyService(); // 监听ViewModel的权限请求事件 viewModel.getRequestPermissionEvent().observe(this, unused -> { // 传递Activity自身引用给Model myService.requestPermission(MainActivity.this, new MyService.PermissionResultCallback() { @Override public void onPermissionGranted() { viewModel.onPermissionGranted(); } @Override public void onPermissionDenied() { viewModel.onPermissionDenied(); showPermissionAlarmDialog(); // 显示权限拒绝提示 } }); }); viewModel.onCreate(); } // 权限拒绝提示弹窗(原代码保留) private void showPermissionAlarmDialog() { // 实现弹窗逻辑 }
方案优势
- 遵守MVVM规范:ViewModel完全不持有View/Activity引用,避免内存泄漏;
- 职责清晰:ViewModel负责业务逻辑判断,View负责UI/生命周期相关操作,Model负责数据和权限实现;
- 生命周期安全:LiveData是生命周期感知的,Activity销毁时会自动移除观察者,无内存泄漏风险。
内容的提问来源于stack exchange,提问作者AnD




