You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

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() {
    // 实现弹窗逻辑
}

方案优势

  1. 遵守MVVM规范:ViewModel完全不持有View/Activity引用,避免内存泄漏;
  2. 职责清晰:ViewModel负责业务逻辑判断,View负责UI/生命周期相关操作,Model负责数据和权限实现;
  3. 生命周期安全:LiveData是生命周期感知的,Activity销毁时会自动移除观察者,无内存泄漏风险。

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

火山引擎 最新活动