# AIAGENTS.md — Android MVVM 架构规范 > 本文件用于指导 AI Agent 在 Android 项目中正确生成 MVVM 架构代码。 > 所有新增/修改的 Activity、Fragment、ViewModel 必须遵循以下规范。 --- ## 一、架构总览 ``` ┌─────────────────────────────────────────────────────┐ │ View (UI) │ │ Activity / Fragment / ViewBinding │ │ 职责:渲染 UI、接收用户输入、订阅 ViewModel 状态 │ └────────────────────┬────────────────────────────────┘ │ 调用方法 / 观察 LiveData ▼ ┌─────────────────────────────────────────────────────┐ │ ViewModel │ │ 持有 UI 状态、处理业务逻辑、暴露数据 │ │ 职责:不持有 View 引用、不直接操作数据层 │ └────────────────────┬────────────────────────────────┘ │ 调用 ▼ ┌─────────────────────────────────────────────────────┐ │ Repository │ │ 统一数据源入口(网络 + 本地缓存) │ └────────────────────┬────────────────────────────────┘ │ ▼ ┌─────────────────────────────────────────────────────┐ │ Model (Data) │ │ Retrofit / Room / SharedPreferences │ └─────────────────────────────────────────────────────┘ ``` --- ## 二、核心原则 | 原则 | 说明 | |------|------| | **单向数据流** | View → 调用 ViewModel 方法 → ViewModel 更新 LiveData → View 被动刷新 | | **ViewModel 不持有 View 引用** | 禁止传入 Activity / Fragment / View / Context | | **View 不直接访问数据层** | 所有数据必须通过 ViewModel → Repository | | **生命周期安全** | ViewModel 中的数据操作需在 `viewModelScope` 或 `Lifecycle` 感知下执行 | | **可测试性** | ViewModel 不依赖 Android Framework,便于单元测试 | --- ## 三、基类代码 ### 3.1 BaseViewModel(无需 Context 时使用) > **适用场景:** ViewModel 不需要 `Context`(如纯内存计算、通过 Repository 访问数据等)。 > 若需要 `Context` / `Application`,请使用 **3.2 BaseAndroidViewModel**。 ```java import io.reactivex.disposables.CompositeDisposable; import io.reactivex.disposables.Disposable; /** * MVVM ViewModel 基类(无 Context 版本) * * 职责: * - 持有 UI 状态(LiveData) * - 处理业务逻辑 * - 暴露数据给 View 层 * - 通过 CompositeDisposable 统一管理 RxJava 异步任务 * * 禁止: * - 持有任何 View 引用(Activity / Fragment / View / Context) * - 直接进行网络请求或数据库操作(必须通过 Repository) * - 在 ViewModel 中弹 Toast / Dialog / Snackbar * - 手动创建 Thread / Executor 执行异步任务(必须使用 RxJava + CompositeDisposable) * - 忘记将 Disposable add 到 disposables 中 */ public abstract class BaseViewModel extends ViewModel { // ============================================================ // 通用 UI 状态 // ============================================================ /** 全局加载状态 */ private final MutableLiveData loadingLiveData = new MutableLiveData<>(false); /** 全局错误提示 */ private final MutableLiveData errorLiveData = new MutableLiveData<>(); /** 全局成功提示 */ private final MutableLiveData successLiveData = new MutableLiveData<>(); // ============================================================ // RxJava 异步管理(核心) // ============================================================ /** * CompositeDisposable 用于统一管理所有 RxJava 订阅。 * 在 onCleared() 中自动 dispose,防止内存泄漏。 * * 使用方式: * disposables.add( * repository.fetchUser(id) * .subscribeOn(Schedulers.io()) * .observeOn(AndroidSchedulers.mainThread()) * .subscribe(user -> { ... }, throwable -> { ... }) * ); */ protected final CompositeDisposable disposables = new CompositeDisposable(); // ============================================================ // 对外暴露(只读) // ============================================================ public LiveData getLoadingLiveData() { return loadingLiveData; } public LiveData getErrorLiveData() { return errorLiveData; } public LiveData getSuccessLiveData() { return successLiveData; } // ============================================================ // 受保护的方法(子类调用) // ============================================================ protected void setLoading(boolean loading) { loadingLiveData.postValue(loading); } protected void setError(String message) { errorLiveData.postValue(message); } protected void setSuccess(String message) { successLiveData.postValue(message); } /** * 便捷方法:将 Disposable 添加到 CompositeDisposable 中管理。 * 子类在发起 RxJava 请求时调用此方法。 */ protected void addDisposable(Disposable disposable) { disposables.add(disposable); } // ============================================================ // 生命周期 // ============================================================ @Override protected void onCleared() { super.onCleared(); // ViewModel 销毁时,自动取消所有正在执行的 RxJava 任务 if (!disposables.isDisposed()) { disposables.clear(); } } } ``` --- ### 3.2 BaseAndroidViewModel(需要 Context 时使用) > **适用场景:** ViewModel 需要 `Context` / `Application`(如读取 `SharedPreferences`、访问 `Resources`、使用 `SystemService` 等)。 > 继承 `AndroidViewModel`,通过 `getApplication()` 获取 `Application` 上下文(**不会泄漏**)。 > 其余代码结构与 `BaseViewModel` 完全一致。 ```java import android.app.Application; import io.reactivex.disposables.CompositeDisposable; import io.reactivex.disposables.Disposable; /** * MVVM ViewModel 基类(带 Context 版本) * * 继承 AndroidViewModel,可通过 getApplication() 获取 Application 级别的 Context。 * Application Context 生命周期与进程一致,不会造成内存泄漏。 * * 职责: * - 持有 UI 状态(LiveData) * - 处理业务逻辑 * - 暴露数据给 View 层 * - 通过 CompositeDisposable 统一管理 RxJava 异步任务 * - 可通过 getApplication() 获取 Application Context(安全) * * 禁止: * - 持有任何 View 引用(Activity / Fragment / View) * - 使用 Activity Context 或持有 View 的 Context(只能使用 Application) * - 直接进行网络请求或数据库操作(必须通过 Repository) * - 在 ViewModel 中弹 Toast / Dialog / Snackbar * - 手动创建 Thread / Executor 执行异步任务(必须使用 RxJava + CompositeDisposable) * - 忘记将 Disposable add 到 disposables 中 */ public abstract class BaseAndroidViewModel extends AndroidViewModel { // ============================================================ // 通用 UI 状态 // ============================================================ /** 全局加载状态 */ private final MutableLiveData loadingLiveData = new MutableLiveData<>(false); /** 全局错误提示 */ private final MutableLiveData errorLiveData = new MutableLiveData<>(); /** 全局成功提示 */ private final MutableLiveData successLiveData = new MutableLiveData<>(); // ============================================================ // RxJava 异步管理(核心) // ============================================================ /** * CompositeDisposable 用于统一管理所有 RxJava 订阅。 * 在 onCleared() 中自动 dispose,防止内存泄漏。 */ protected final CompositeDisposable disposables = new CompositeDisposable(); // ============================================================ // 构造方法 // ============================================================ public BaseAndroidViewModel(@NonNull Application application) { super(application); } // ============================================================ // 对外暴露(只读) // ============================================================ public LiveData getLoadingLiveData() { return loadingLiveData; } public LiveData getErrorLiveData() { return errorLiveData; } public LiveData getSuccessLiveData() { return successLiveData; } // ============================================================ // 受保护的方法(子类调用) // ============================================================ protected void setLoading(boolean loading) { loadingLiveData.postValue(loading); } protected void setError(String message) { errorLiveData.postValue(message); } protected void setSuccess(String message) { successLiveData.postValue(message); } /** * 便捷方法:将 Disposable 添加到 CompositeDisposable 中管理。 * 子类在发起 RxJava 请求时调用此方法。 */ protected void addDisposable(Disposable disposable) { disposables.add(disposable); } // ============================================================ // Context 安全访问 // ============================================================ /** * 获取 Application Context(安全,不会泄漏)。 * 子类可通过此方法访问: * - getApplication().getSharedPreferences(...) * - getApplication().getResources().getString(...) * - getApplication().getSystemService(...) */ @NonNull protected Application getAppContext() { return getApplication(); } // ============================================================ // 生命周期 // ============================================================ @Override protected void onCleared() { super.onCleared(); // ViewModel 销毁时,自动取消所有正在执行的 RxJava 任务 if (!disposables.isDisposed()) { disposables.clear(); } } } ``` > **选择指南:** > | 需求 | 继承 | > |------|------| > | 不需要 Context,纯数据驱动 | `BaseViewModel` | > | 需要 SharedPreferences / Resources / SystemService | `BaseAndroidViewModel` | > | 需要 Activity Context(弹 Dialog 等) | ❌ 不允许,应通过 View 层 LiveData 回调处理 | --- ### 3.3 BaseMvvmActivity ```java /** * MVVM Activity 基类 * * 职责: * - 绑定 ViewBinding * - 初始化 ViewModel * - 订阅 ViewModel 中的 LiveData * - 将用户操作转发给 ViewModel * * 禁止: * - 在 Activity 中直接进行网络请求或数据库操作 * - 持有可变 LiveData 的引用 * - 在 Activity 中写业务逻辑 * * @param ViewModel 类型,必须继承 BaseViewModel 或 BaseAndroidViewModel * @param ViewBinding 类型 */ public abstract class BaseMvvmActivity extends AppCompatActivity { // ============================================================ // 成员 // ============================================================ protected VM viewModel; protected VB binding; // ============================================================ // 抽象方法(子类必须实现) // ============================================================ /** 创建 ViewModel 实例 */ protected abstract VM createViewModel(); /** 创建 ViewBinding 实例 */ protected abstract VB createViewBinding(); /** 初始化界面(如设置 Adapter、Listener 等) */ protected abstract void initView(); /** 订阅 ViewModel 的 LiveData */ protected abstract void observeViewModel(); // ============================================================ // 生命周期 // ============================================================ @Override protected void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); // 1. 创建 ViewBinding binding = createViewBinding(); setContentView(binding.getRoot()); // 2. 创建 ViewModel viewModel = createViewModel(); // 3. 订阅 ViewModel observeViewModel(); // 4. 初始化界面 initView(); // 5. 加载数据 onLoadData(); } /** * 页面创建完成后加载数据 * 子类可重写此方法触发首次数据加载 */ protected void onLoadData() { // 默认空实现 } // ============================================================ // 通用订阅(可选重写) // ============================================================ /** * 订阅全局通用状态(loading / error / success) * 子类可在 observeViewModel() 中调用 super.observeCommonState() */ protected void observeCommonState() { viewModel.getLoadingLiveData().observe(this, loading -> { if (loading != null && loading) { showLoading(); } else { hideLoading(); } }); viewModel.getErrorLiveData().observe(this, error -> { if (error != null && !error.isEmpty()) { showError(error); } }); viewModel.getSuccessLiveData().observe(this, msg -> { if (msg != null && !msg.isEmpty()) { showSuccess(msg); } }); } // ============================================================ // UI 辅助方法(子类可重写) // ============================================================ protected void showLoading() { // 默认实现:可替换为自定义 LoadingDialog // LoadingDialog.show(this); } protected void hideLoading() { // LoadingDialog.dismiss(); } protected void showError(String message) { Toast.makeText(this, message, Toast.LENGTH_SHORT).show(); } protected void showSuccess(String message) { Toast.makeText(this, message, Toast.LENGTH_SHORT).show(); } // ============================================================ // 禁止使用的模式(编译期约束) // ============================================================ /** * @deprecated 禁止在 Activity 中直接创建 Handler 做延时操作 * → 应使用 ViewModel + LiveData 驱动 */ @Deprecated protected Handler getHandler() { throw new UnsupportedOperationException("禁止在 MVVM Activity 中使用 Handler"); } } ``` --- ### 3.4 BaseMvvmFragment ```java /** * MVVM Fragment 基类 * * 职责: * - 绑定 ViewBinding * - 初始化 ViewModel(默认与 Activity 共享 ViewModel) * - 订阅 ViewModel 中的 LiveData * - 将用户操作转发给 ViewModel * * 禁止: * - 在 Fragment 中直接进行网络请求或数据库操作 * - 持有可变 LiveData 的引用 * - 在 Fragment 中写业务逻辑 * - 使用 getActivity() 强转后直接操作 Activity(应通过 ViewModel 通信) * * @param ViewModel 类型,必须继承 BaseViewModel 或 BaseAndroidViewModel * @param ViewBinding 类型 */ public abstract class BaseMvvmFragment extends Fragment { // ============================================================ // 成员 // ============================================================ protected VM viewModel; protected VB binding; /** 是否与 Activity 共享 ViewModel(默认 true) */ protected boolean shareViewModelWithActivity() { return true; } // ============================================================ // 抽象方法(子类必须实现) // ============================================================ /** 创建 ViewModel 实例 */ protected abstract VM createViewModel(); /** 创建 ViewBinding 实例 */ protected abstract VB createViewBinding(@NonNull LayoutInflater inflater, @Nullable ViewGroup container); /** 初始化界面 */ protected abstract void initView(); /** 订阅 ViewModel 的 LiveData */ protected abstract void observeViewModel(); // ============================================================ // 生命周期 // ============================================================ @Nullable @Override public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { binding = createViewBinding(inflater, container); return binding.getRoot(); } @Override public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) { super.onViewCreated(view, savedInstanceState); // 1. 创建 ViewModel viewModel = createViewModel(); // 2. 订阅 ViewModel observeViewModel(); // 3. 初始化界面 initView(); // 4. 加载数据 onLoadData(); } @Override public void onDestroyView() { super.onDestroyView(); // 释放 ViewBinding 引用,防止内存泄漏 binding = null; } /** * 页面创建完成后加载数据 * 子类可重写此方法触发首次数据加载 */ protected void onLoadData() { // 默认空实现 } // ============================================================ // 通用订阅(可选调用) // ============================================================ /** * 订阅全局通用状态(loading / error / success) */ protected void observeCommonState() { if (getViewLifecycleOwner() == null) return; viewModel.getLoadingLiveData().observe(getViewLifecycleOwner(), loading -> { if (loading != null && loading) { showLoading(); } else { hideLoading(); } }); viewModel.getErrorLiveData().observe(getViewLifecycleOwner(), error -> { if (error != null && !error.isEmpty()) { showError(error); } }); viewModel.getSuccessLiveData().observe(getViewLifecycleOwner(), msg -> { if (msg != null && !msg.isEmpty()) { showSuccess(msg); } }); } // ============================================================ // UI 辅助方法(子类可重写) // ============================================================ protected void showLoading() { // 可替换为自定义 LoadingDialog } protected void hideLoading() { // LoadingDialog.dismiss(); } protected void showError(String message) { if (getContext() != null) { Toast.makeText(getContext(), message, Toast.LENGTH_SHORT).show(); } } protected void showSuccess(String message) { if (getContext() != null) { Toast.makeText(getContext(), message, Toast.LENGTH_SHORT).show(); } } // ============================================================ // 工具方法 // ============================================================ /** * 获取 Activity 级别的 ViewModel(用于 Fragment 间通信) */ protected T getActivityViewModel(Class vmClass) { return new ViewModelProvider(requireActivity()).get(vmClass); } // ============================================================ // 禁止使用的模式 // ============================================================ /** * @deprecated 禁止在 Fragment 中直接使用 getActivity() 强转 * → 应通过 ViewModel 或接口回调通信 */ @Deprecated protected T getActivityAs(Class clazz) { throw new UnsupportedOperationException("禁止强转 Activity,请使用 ViewModel 通信"); } } ``` --- ## 四、使用示例 ### 4.1 无需 Context 的 ViewModel(继承 BaseViewModel) ```java import io.reactivex.android.schedulers.AndroidSchedulers; import io.reactivex.schedulers.Schedulers; import io.reactivex.disposables.Disposable; public class UserViewModel extends BaseViewModel { private final MutableLiveData userLiveData = new MutableLiveData<>(); private final UserRepository repository; public UserViewModel(UserRepository repository) { this.repository = repository; } public LiveData getUser() { return userLiveData; } public void loadUser(String userId) { setLoading(true); Disposable disposable = repository.fetchUser(userId) .subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) .subscribe( user -> { userLiveData.setValue(user); setSuccess("加载成功"); setLoading(false); }, throwable -> { setError(throwable.getMessage()); setLoading(false); } ); // 必须添加到 disposables 中,确保 ViewModel 销毁时自动取消 addDisposable(disposable); } public void updateUserName(String userId, String newName) { setLoading(true); Disposable disposable = repository.updateName(userId, newName) .andThen(repository.fetchUser(userId)) // 更新后刷新数据 .subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) .subscribe( user -> { userLiveData.setValue(user); setSuccess("更新成功"); setLoading(false); }, throwable -> { setError(throwable.getMessage()); setLoading(false); } ); addDisposable(disposable); } } ``` > **说明:** `UserRepository` 的方法返回 `Single` / `Completable` 等 RxJava 类型, > 由 ViewModel 通过 `disposables` 统一管理订阅生命周期。 --- ### 4.2 需要 Context 的 ViewModel(继承 BaseAndroidViewModel) ```java import android.app.Application; import io.reactivex.android.schedulers.AndroidSchedulers; import io.reactivex.schedulers.Schedulers; import io.reactivex.disposables.Disposable; /** * 需要读取 SharedPreferences / Resources 等系统服务时使用 BaseAndroidViewModel */ public class SettingsViewModel extends BaseAndroidViewModel { private final MutableLiveData appVersionLiveData = new MutableLiveData<>(); private final MutableLiveData darkModeLiveData = new MutableLiveData<>(); private final SettingsRepository repository; public SettingsViewModel(Application application, SettingsRepository repository) { super(application); this.repository = repository; } public LiveData getAppVersion() { return appVersionLiveData; } public LiveData getDarkMode() { return darkModeLiveData; } /** * 读取 Application Context 中的 SharedPreferences */ public void loadSettings() { setLoading(true); // 通过 getAppContext() 安全获取 Application Context String version = getAppContext() .getPackageManager() .getPackageInfo(getAppContext().getPackageName(), 0) .versionName; appVersionLiveData.postValue(version); // 从 SharedPreferences 读取设置 boolean darkMode = getAppContext() .getSharedPreferences("settings", Application.MODE_PRIVATE) .getBoolean("dark_mode", false); darkModeLiveData.postValue(darkMode); setLoading(false); } /** * 保存设置到 SharedPreferences + 远程同步 */ public void saveDarkMode(boolean enabled) { setLoading(true); // 本地持久化(使用 Application Context,安全) getAppContext() .getSharedPreferences("settings", Application.MODE_PRIVATE) .edit() .putBoolean("dark_mode", enabled) .apply(); // 远程同步 Disposable disposable = repository.syncDarkMode(enabled) .subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) .subscribe( () -> { darkModeLiveData.setValue(enabled); setSuccess("设置已保存"); setLoading(false); }, throwable -> { setError(throwable.getMessage()); setLoading(false); } ); addDisposable(disposable); } } ``` > **关键点:** > - 构造函数必须接收 `Application` 并传给 `super(application)` > - 通过 `getAppContext()` 获取 Context,绝不使用 Activity Context > - 其余 RxJava 订阅管理与 `BaseViewModel` 完全一致 ### 4.3 具体 Activity(使用 BaseViewModel) ```java public class UserProfileActivity extends BaseMvvmActivity { @Override protected UserViewModel createViewModel() { UserRepository repository = new UserRepository(); return new ViewModelProvider(this, new ViewModelProvider.Factory() { @Override public T create(Class modelClass) { return (T) new UserViewModel(repository); } }).get(UserViewModel.class); } @Override protected ActivityUserProfileBinding createViewBinding() { return ActivityUserProfileBinding.inflate(getLayoutInflater()); } @Override protected void initView() { binding.btnRefresh.setOnClickListener(v -> { viewModel.loadUser("123"); }); binding.btnUpdate.setOnClickListener(v -> { viewModel.updateUserName("123", "新名字"); }); } @Override protected void observeViewModel() { observeCommonState(); viewModel.getUser().observe(this, user -> { if (user != null) { binding.tvName.setText(user.getName()); binding.tvAge.setText(String.valueOf(user.getAge())); } }); } } ``` ### 4.4 具体 Fragment ```java public class UserDetailFragment extends BaseMvvmFragment { @Override protected UserViewModel createViewModel() { UserRepository repository = new UserRepository(); return new ViewModelProvider(requireActivity(), new ViewModelProvider.Factory() { @Override public T create(Class modelClass) { return (T) new UserViewModel(repository); } }).get(UserViewModel.class); } @Override protected FragmentUserDetailBinding createViewBinding(LayoutInflater inflater, ViewGroup container) { return FragmentUserDetailBinding.inflate(inflater, container, false); } @Override protected void initView() { binding.tvTitle.setText("用户详情"); } @Override protected void observeViewModel() { observeCommonState(); viewModel.getUser().observe(getViewLifecycleOwner(), user -> { if (user != null) { binding.tvEmail.setText(user.getEmail()); } }); } } ``` ### 4.5 具体 Activity(使用 BaseAndroidViewModel) ```java public class SettingsActivity extends BaseMvvmActivity { @Override protected SettingsViewModel createViewModel() { SettingsRepository repository = new SettingsRepository(); // BaseAndroidViewModel 需要传入 Application return new ViewModelProvider(this, new ViewModelProvider.Factory() { @Override public T create(Class modelClass) { return (T) new SettingsViewModel(getApplication(), repository); } }).get(SettingsViewModel.class); } @Override protected ActivitySettingsBinding createViewBinding() { return ActivitySettingsBinding.inflate(getLayoutInflater()); } @Override protected void initView() { binding.switchDarkMode.setOnCheckedChangeListener((buttonView, isChecked) -> { viewModel.saveDarkMode(isChecked); }); } @Override protected void observeViewModel() { observeCommonState(); viewModel.getAppVersion().observe(this, version -> { binding.tvVersion.setText("版本:" + version); }); viewModel.getDarkMode().observe(this, enabled -> { binding.switchDarkMode.setChecked(enabled != null && enabled); }); } @Override protected void onLoadData() { viewModel.loadSettings(); } } ``` --- ## 五、禁止规范(AI Agent 必须遵守) ### 5.1 ❌ ViewModel 层禁止 | 禁止行为 | 原因 | |----------|------| | 持有 Activity / Fragment / View 引用 | 导致内存泄漏,ViewModel 生命周期长于 View | | 在 BaseViewModel 中持有任何 Context 引用 | BaseViewModel 不应依赖 Context,如需 Context 应改用 BaseAndroidViewModel | | 在 BaseAndroidViewModel 中使用 Activity Context | 只能使用 Application Context(通过 getAppContext()),Activity Context 会泄漏 | | 直接进行网络请求(如 new OkHttpCall()) | 应委托给 Repository | | 直接操作数据库(如 Room DAO 调用) | 应委托给 Repository | | 弹 Toast / Dialog / Snackbar | ViewModel 不应感知 UI 组件 | | 手动创建 Thread / Executor / AsyncTask | 必须使用 RxJava + CompositeDisposable | | 发起 RxJava 请求后不 add 到 disposables | 导致内存泄漏,ViewModel 销毁后任务仍在执行 | | 在 onCleared 之外手动调用 `disposables.clear()` | 应由基类统一管理,子类不应干预销毁时机 | | 暴露 MutableLiveData 给外部 | 应只暴露 LiveData(不可变) | | 在 ViewModel 中写死 Context 相关资源(R.string.xxx) | 降低可测试性,应通过 Repository 或资源 ID 传递 | | 创建独立的 Disposable 而不加入 CompositeDisposable | 必须统一通过 `addDisposable()` 管理 | ### 5.2 ❌ View 层(Activity / Fragment)禁止 | 禁止行为 | 原因 | |----------|------| | 直接调用 Retrofit / OkHttp / Room | 破坏分层架构 | | 持有 MutableLiveData 引用并直接 setValue | 破坏单向数据流 | | 在 Activity/Fragment 中写业务逻辑 | 降低可维护性和可测试性 | | 使用 findViewById 替代 ViewBinding | 低效且易出错 | | 在 onDestroyView 之后访问 binding | 导致空指针 | | 使用静态变量存储 View / Context | 严重内存泄漏 | | 在 Fragment 中通过 getActivity() 强转调用 Activity 方法 | 应使用 ViewModel 或接口回调 | ### 5.3 ❌ 数据层禁止 | 禁止行为 | 原因 | |----------|------| | Repository 直接持有 ViewModel 引用 | 数据层不应感知 UI 层 | | Model 中混杂业务逻辑 | Model 只做数据定义和存储 | | 在 Repository 中弹 Toast / Dialog | 数据层不应感知 UI | --- ## 六、代码生成检查清单 AI Agent 在生成 MVVM 相关代码时,必须逐项确认: - [ ] ViewModel 是否继承 `BaseViewModel` 或 `BaseAndroidViewModel`? - [ ] 需要 Context 的 ViewModel 是否改为继承 `BaseAndroidViewModel`(而非在 BaseViewModel 中持有 Context)? - [ ] Activity 是否继承 `BaseMvvmActivity`? - [ ] Fragment 是否继承 `BaseMvvmFragment`? - [ ] ViewModel 中是否没有任何 View / Activity Context 引用? - [ ] BaseAndroidViewModel 中是否只通过 `getAppContext()` 获取 Application Context? - [ ] 所有数据获取是否通过 Repository? - [ ] View 层是否只通过 LiveData 观察数据? - [ ] View 层是否只调用 ViewModel 方法,不直接操作数据? - [ ] MutableLiveData 是否声明为 `private`,对外只暴露 `LiveData`? - [ ] Fragment 的 `onDestroyView` 中是否置空了 binding? - [ ] 是否没有使用 `findViewById`? - [ ] 所有 RxJava 订阅是否通过 `addDisposable()` 添加到 `disposables` 中? - [ ] ViewModel 中是否没有手动创建 Thread / Executor / AsyncTask? - [ ] Repository 方法返回类型是否为 RxJava 类型(Single / Observable / Completable / Maybe)? --- ## 七、目录结构建议 ``` app/src/main/java/com/example/ ├── base/ │ ├── BaseViewModel.java (无需 Context 时继承) │ ├── BaseAndroidViewModel.java (需要 Context 时继承) │ ├── BaseMvvmActivity.java │ └── BaseMvvmFragment.java ├── model/ │ ├── User.java │ └── ApiResponse.java ├── repository/ │ ├── UserRepository.java │ ├── SettingsRepository.java │ └── RemoteDataSource.java ├── viewmodel/ │ ├── UserViewModel.java (extends BaseViewModel) │ ├── SettingsViewModel.java (extends BaseAndroidViewModel) │ └── HomeViewModel.java ├── view/ │ ├── activity/ │ │ ├── UserProfileActivity.java │ │ └── SettingsActivity.java │ └── fragment/ │ └── UserDetailFragment.java └── network/ ├── ApiService.java └── RetrofitClient.java ``` --- ## 八、所需依赖(build.gradle) ```groovy dependencies { // Jetpack ViewModel & LiveData implementation 'androidx.lifecycle:lifecycle-viewmodel:2.6.2' implementation 'androidx.lifecycle:lifecycle-livedata:2.6.2' // RxJava 3 implementation 'io.reactivex.rxjava3:rxjava:3.1.8' implementation 'io.reactivex.rxjava3:rxandroid:3.0.2' // Retrofit + RxJava adapter(如网络层使用 Retrofit) implementation 'com.squareup.retrofit2:adapter-rxjava3:2.9.0' // ViewBinding(在 android {} 块中启用) // buildFeatures { viewBinding true } } ``` --- > **最后更新:** 2026-08-21 > **维护者:** Android Team > **适用版本:** Android SDK 21+ / Jetpack ViewModel 2.5+ / RxJava 3.x / ViewBinding enabled