采用MVVM架构时如何在ViewModel中获取位置权限与LocationManager
问题解答
1、如何在ViewModel中获取LocationManager实例?
有两种可行方案,更推荐第二种解耦方案:
- 方案1:使用
AndroidViewModel替代普通ViewModel,通过其持有的Application Context获取LocationManager。该方案实现简单,Application Context不会造成内存泄漏,但会让ViewModel和Android平台API耦合,不利于纯JVM单元测试。
示例代码:
public class MAgencyViewModel extends AndroidViewModel { private LocationManager lm; public MAgencyViewModel(@NonNull Application application) { super(application); lm = (LocationManager) application.getSystemService(Context.LOCATION_SERVICE); } }
- 方案2(推荐):将位置相关能力封装为独立的
LocationRepository,通过依赖注入的方式传入ViewModel。ViewModel只和仓库层交互,完全不感知LocationManager的存在,做到和平台API解耦,更符合MVVM分层要求,也方便单元测试。
2、如何在ViewModel中使用onRequestPermissionsResult()处理权限回调?
onRequestPermissionsResult()是Activity/Fragment专属的生命周期回调方法,ViewModel中不能直接重写或调用该方法,正确处理流程如下:
- 权限申请逻辑完全放在View层(Activity/Fragment)实现,推荐使用官方Activity Result API替代旧的
onRequestPermissionsResult方法 - View层监听到权限授权结果后,将结果传递给ViewModel:
- 权限授权通过:调用ViewModel的自定义业务方法,触发位置获取、天气接口调用流程
- 权限被拒绝:将权限拒绝状态传递给ViewModel,由ViewModel更新对应的LiveData通知View层展示权限拒绝提示
3、在ViewModel中申请权限、处理权限回调的做法是否符合MVVM开发规范?
不符合。
MVVM架构的核心分层要求是:
- View层:负责处理和UI交互、平台生命周期相关的逻辑,包括权限申请、UI跳转、用户交互响应
- ViewModel层:负责业务逻辑封装,和View层、平台API解耦,不能持有任何View实例,也不能处理和View生命周期绑定的逻辑
- Model层:负责数据获取(接口、本地存储、传感器等)的封装
权限申请属于和Android平台强绑定、和View交互强相关的逻辑,放在ViewModel中会导致ViewModel和Android平台耦合,无法做纯JVM单元测试,也违背了分层解耦的设计原则。
代码优化建议
针对你现有代码,可以调整为如下结构:
- 封装位置仓库层,统一处理位置获取逻辑
public class LocationRepo { private LocationManager lm; private Application app; private static LocationRepo instance; private LocationRepo(Application app) { this.app = app; lm = (LocationManager) app.getSystemService(Context.LOCATION_SERVICE); } public static LocationRepo getInstance(Application app) { if (instance == null) { instance = new LocationRepo(app); } return instance; } // 封装获取GeoInfo的逻辑 public GeoInfo getGeoInfo() throws SecurityException { // 位置获取逻辑实现 } }
- 调整ViewModel逻辑,只和仓库层交互
public class MAgencyViewModel extends ViewModel{ private final String TAG = "MAgencyViewModel"; private MutableLiveData<ShortWeather> sw = new MutableLiveData<>(); private MutableLiveData<String> errorMsg = new MutableLiveData<>(); private MAgencyRepo maRepo = MAgencyRepo.getInStance(); private LocationRepo locationRepo; // 通过依赖注入传入LocationRepo public MAgencyViewModel(LocationRepo locationRepo) { this.locationRepo = locationRepo; } public void fetchWeatherWithLocation() { try { GeoInfo gi = locationRepo.getGeoInfo(); sw.postValue(maRepo.getWeather(gi).getValue()); Log.i(TAG,"API连接完成"); } catch (SecurityException e) { errorMsg.postValue("无位置权限,请先授权"); } } public LiveData<ShortWeather> getWeather(){ return sw; } public LiveData<String> getErrorMsg() { return errorMsg; } }
- View层处理权限逻辑,授权通过后调用ViewModel的
fetchWeatherWithLocation()方法即可
内容的提问来源于stack exchange,提问作者codingRookie
相关产品推荐
相关产品推荐

