MVVM架构下如何向Repository类传递Context?
嘿,我来帮你搞定这个问题!在MVVM架构里,Repository确实不应该直接持有Activity/Fragment的Context(容易引发内存泄漏),但我们可以用Application Context来满足需求,同时还要调整你代码里权限请求的逻辑——因为requestPermissions是UI层(Activity/Fragment)的专属方法,Repository里可不能直接调用哦。
一、给Repository传递Context的正确姿势
1. 构造函数注入Application Context
这是最直接且安全的方式,创建Repository时传入Context,但内部要保存Application级别的Context,避免和UI组件绑定导致泄漏。
修改你的LocationRepository:
public class LocationRepository { private final Context appContext; // 通过构造函数传入Context,内部留存Application Context public LocationRepository(Context context) { this.appContext = context.getApplicationContext(); } public void getLastLocation(LocationCallback callback) { FusedLocationProviderClient client = LocationServices.getFusedLocationProviderClient(appContext); // 用App Context做权限检查 if (ContextCompat.checkSelfPermission(appContext, Manifest.permission.INTERNET) == PackageManager.PERMISSION_GRANTED && ContextCompat.checkSelfPermission(appContext, Manifest.permission.ACCESS_FINE_LOCATION) == PackageManager.PERMISSION_GRANTED && ContextCompat.checkSelfPermission(appContext, Manifest.permission.ACCESS_COARSE_LOCATION) == PackageManager.PERMISSION_GRANTED) { client.getLastLocation().addOnSuccessListener(location -> { if (location != null) { callback.onLocationReceived(location); } else { callback.onLocationFailed("无法获取有效位置"); } }).addOnFailureListener(e -> { callback.onLocationFailed(e.getMessage()); }); } else { // 权限不足,通知UI层去发起请求 callback.onPermissionRequired(); } } // 定义回调接口,让Repository和UI层解耦通信 public interface LocationCallback { void onLocationReceived(Location location); void onPermissionRequired(); void onLocationFailed(String error); } }
接着在ViewModel里初始化Repository(推荐用AndroidViewModel直接拿到Application Context):
public class LocationViewModel extends AndroidViewModel { private LocationRepository locationRepository; public LocationViewModel(@NonNull Application application) { super(application); // 用Application Context初始化Repository locationRepository = new LocationRepository(application); } public void fetchLocation(LocationRepository.LocationCallback callback) { locationRepository.getLastLocation(callback); } }
最后在Activity里处理回调和权限请求:
public class MainActivity extends AppCompatActivity { private LocationViewModel viewModel; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); viewModel = new ViewModelProvider(this).get(LocationViewModel.class); viewModel.fetchLocation(new LocationRepository.LocationCallback() { @Override public void onLocationReceived(Location location) { // 处理获取到的位置数据 } @Override public void onPermissionRequired() { // 在这里调用权限请求,因为这是Activity的专属方法 if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { requestPermissions(new String[]{ Manifest.permission.INTERNET, Manifest.permission.ACCESS_FINE_LOCATION, Manifest.permission.ACCESS_COARSE_LOCATION }, 1); } } @Override public void onLocationFailed(String error) { // 处理位置获取失败的情况 } }); } @Override public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) { super.onRequestPermissionsResult(requestCode, permissions, grantResults); if (requestCode == 1) { // 权限请求结果处理完后,重新触发位置获取 viewModel.fetchLocation(...); } } }
2. 用依赖注入框架(比如Hilt/Dagger)
如果你的项目用了依赖注入框架,直接注入Application Context会更规范,不用手动传递。比如用Hilt的话,只需要在Repository的构造函数里标记@Inject:
public class LocationRepository { private final Context appContext; @Inject public LocationRepository(@ApplicationContext Context context) { this.appContext = context; } // 其余逻辑和上面一致 }
这样在ViewModel里直接注入Repository即可,不用手动创建实例,更符合MVVM的解耦思想。
二、为什么不能在Repository里调用requestPermissions?
requestPermissions是Activity或Fragment的方法,属于UI层的交互操作,而Repository的核心职责是数据获取与处理,不应该和UI层耦合。把权限请求放到Activity/Fragment里,能让各层职责更清晰,也完全符合MVVM的架构原则。
内容的提问来源于stack exchange,提问作者BackToDream

