当前位置: 首页 > news >正文

Android 之 MVVM架构

以下是一个基于 ​​Java 语言​​ 的 Android MVVM 登录模块详细实现,结合 ViewModelLiveDataDataBinding 和 Repository 模式,确保代码解耦、可维护性强。

一个完整的MVVM登录模块方案,会包括以下核心组件:

Model层使用Repository模式管理数据源,包括网络数据源(使用Retrofit)和本地数据源(使用Room)。

ViewModel层处理业务逻辑,使用LiveData暴露UI状态。

View层(Activity)使用DataBinding实现数据绑定,观察ViewModel的LiveData状态变化。使用单向和双向数据绑定技术简化UI更新。


​项目结构概览

app/
├── src/
│   ├── main/
│   │   ├── java/com/example/login/
│   │   │   ├── data/
│   │   │   │   ├── UserRepository.java       # 数据仓库
│   │   │   │   ├── local/UserDao.java        # Room数据库操作
│   │   │   │   ├── remote/ApiService.java    # 网络请求接口
│   │   │   ├── model/
│   │   │   │   ├── User.java                 # 用户实体类
│   │   │   ├── ui/
│   │   │   │   ├── LoginActivity.java        # View层
│   │   │   │   ├── LoginViewModel.java       # ViewModel层
│   │   │   ├── utils/
│   │   │   │   ├── BindingAdapters.java      # 自定义绑定适配器
│   │   ├── res/
│   │   │   ├── layout/activity_login.xml     # 使用DataBinding的布局

1. 添加依赖(build.gradle)

dependencies {// MVVM核心组件implementation 'androidx.lifecycle:lifecycle-viewmodel:2.6.0'implementation 'androidx.lifecycle:lifecycle-livedata:2.6.0'implementation 'androidx.databinding:databinding-runtime:7.0.0'// Room数据库(可选,用于本地缓存)implementation 'androidx.room:room-runtime:2.5.0'annotationProcessor 'androidx.room:room-compiler:2.5.0'// 网络请求(Retrofit + Gson)implementation 'com.squareup.retrofit2:retrofit:2.9.0'implementation 'com.squareup.retrofit2:converter-gson:2.9.0'
}

2. Model层实现​

​2.1 用户实体类(User.java)
public class User {private String username;private String password;public User(String username, String password) {this.username = username;this.password = password;}// Getters & Setterspublic String getUsername() { return username; }public String getPassword() { return password; }
}
2.2 数据仓库(UserRepository.java)​
public class UserRepository {private final ApiService apiService;private final UserDao userDao;public UserRepository(ApiService apiService, UserDao userDao) {this.apiService = apiService;this.userDao = userDao;}public LiveData<Boolean> login(String username, String password) {MutableLiveData<Boolean> result = new MutableLiveData<>();// 优先检查本地数据库userDao.getUser(username, password).observeForever(localUser -> {if (localUser != null) {result.setValue(true); // 本地验证成功} else {// 本地无数据则发起网络请求apiService.login(new User(username, password)).enqueue(new Callback<Boolean>() {@Overridepublic void onResponse(Call<Boolean> call, Response<Boolean> response) {if (response.isSuccessful() && response.body()) {userDao.saveUser(new User(username, password)); // 缓存登录成功的用户}result.setValue(response.body());}@Overridepublic void onFailure(Call<Boolean> call, Throwable t) {result.setValue(false);}});}});return result;}
}
2.3. UserDao (本地数据库访问)​

位于 data/local/UserDao.java,使用 ​​Room​​ 持久化框架实现本地缓存

import androidx.room.Dao;
import androidx.room.Insert;
import androidx.room.Query;
import androidx.lifecycle.LiveData;@Dao
public interface UserDao {// 根据用户名和密码查询用户(用于登录验证)@Query("SELECT * FROM users WHERE username = :username AND password = :password")LiveData<User> getUser(String username, String password);// 插入或更新用户(用于缓存登录成功的用户数据)@Insert(onConflict = OnConflictStrategy.REPLACE)void saveUser(User user);// 删除用户(可选)@Query("DELETE FROM users WHERE id = :userId")void deleteUser(int userId);
}
2.4配套实体类 User.java
import androidx.room.Entity;
import androidx.room.PrimaryKey;@Entity(tableName = "users")
public class User {@PrimaryKey(autoGenerate = true)private int id;private String username;private String password;// 构造方法public User(String username, String password) {this.username = username;this.password = password;}// Getters & Setterspublic int getId() { return id; }public void setId(int id) { this.id = id; }public String getUsername() { return username; }public String getPassword() { return password; }
}
2.5. ApiService (网络请求接口)

位于 data/remote/ApiService.java,使用 ​​Retrofit​​ 定义网络API

import retrofit2.Call;
import retrofit2.http.Body;
import retrofit2.http.POST;public interface ApiService {// 登录API(POST请求,提交User对象)@POST("auth/login")Call<Boolean> login(@Body User user);// 获取用户信息(可选扩展)@POST("user/profile")Call<User> getUserProfile(@Body String userId);
}
2.6Retrofit实例化工具类 RetrofitClient.java
import retrofit2.Retrofit;
import retrofit2.converter.gson.GsonConverterFactory;public class RetrofitClient {private static final String BASE_URL = "https://your-api-domain.com/";private static Retrofit retrofit;public static Retrofit getInstance() {if (retrofit == null) {retrofit = new Retrofit.Builder().baseUrl(BASE_URL).addConverterFactory(GsonConverterFactory.create()).build();}return retrofit;}public static ApiService getApiService() {return getInstance().create(ApiService.class);}
}
2.7 BindingAdapters.java​

以下是基于 MVVM 架构的 BindingAdapters 工具类实现,包含图片加载、视图显隐控制等常用功能,解决 DataBinding 自定义属性绑定问题:

import android.graphics.drawable.Drawable;
import android.view.View;
import android.widget.ImageView;
import android.widget.ProgressBar;
import androidx.annotation.DrawableRes;
import androidx.databinding.BindingAdapter;
import com.bumptech.glide.Glide;
import com.bumptech.glide.request.RequestOptions;public class BindingAdapters {// 1. 图片加载(支持URL、错误占位图)[4,7](@ref)@BindingAdapter(value = {"imageUrl", "errorPlaceholder"}, requireAll = false)public static void loadImage(ImageView view, String imageUrl, Drawable errorPlaceholder) {if (imageUrl != null && !imageUrl.isEmpty()) {Glide.with(view.getContext()).load(imageUrl).apply(new RequestOptions().error(errorPlaceholder)).into(view);} else if (errorPlaceholder != null) {view.setImageDrawable(errorPlaceholder);}}// 2. 资源ID加载图片(避免直接传int导致资源混淆)[4](@ref)@BindingAdapter("android:src")public static void setImageResource(ImageView view, @DrawableRes int resId) {view.setImageResource(resId);}// 3. 控制视图显隐(支持布尔值)[5](@ref)@BindingAdapter("visibleIf")public static void setVisible(View view, boolean visible) {view.setVisibility(visible ? View.VISIBLE : View.GONE);}// 4. 进度条绑定(支持双向绑定)[5](@ref)@BindingAdapter("android:progress")public static void setProgress(ProgressBar progressBar, int progress) {if (progressBar.getProgress() != progress) {progressBar.setProgress(progress);}}
}

3. ViewModel层(LoginViewModel.java)​

public class LoginViewModel extends ViewModel {private UserRepository userRepository;private MutableLiveData<Boolean> loginResult = new MutableLiveData<>();private MutableLiveData<String> errorMessage = new MutableLiveData<>();private MutableLiveData<Boolean> isLoading = new MutableLiveData<>(false);// 双向绑定支持:用户名和密码public MutableLiveData<String> username = new MutableLiveData<>();public MutableLiveData<String> password = new MutableLiveData<>();public LoginViewModel(UserRepository userRepository) {this.userRepository = userRepository;}// 登录操作public void onLoginClick() {if (username.getValue() == null || password.getValue() == null) {errorMessage.setValue("用户名或密码不能为空");return;}isLoading.setValue(true);userRepository.login(username.getValue(), password.getValue()).observeForever(new Observer<Boolean>() {@Overridepublic void onChanged(Boolean success) {isLoading.setValue(false);if (success) loginResult.setValue(true);else errorMessage.setValue("登录失败,请重试");}});}// Getters for LiveDatapublic LiveData<Boolean> getLoginResult() { return loginResult; }public LiveData<String> getErrorMessage() { return errorMessage; }public LiveData<Boolean> getIsLoading() { return isLoading; }
}

4. View层实现​

​4.1 布局文件(activity_login.xml)使用DataBinding​
<layout xmlns:android="http://schemas.android.com/apk/res/android"><data><variable name="viewModel" type="com.example.login.ui.LoginViewModel" /></data><LinearLayoutandroid:layout_width="match_parent"android:layout_height="match_parent"android:orientation="vertical"android:padding="16dp"><!-- 双向绑定用户名 --><EditTextandroid:layout_width="match_parent"android:layout_height="wrap_content"android:text="@={viewModel.username}"android:hint="用户名" /><!-- 双向绑定密码 --><EditTextandroid:layout_width="match_parent"android:layout_height="wrap_content"android:text="@={viewModel.password}"android:inputType="textPassword"android:hint="密码" /><!-- 登录按钮绑定ViewModel方法 --><Buttonandroid:layout_width="match_parent"android:layout_height="wrap_content"android:onClick="@{() -> viewModel.onLoginClick()}"android:text="登录"android:enabled="@{!viewModel.isLoading}" /><!-- 加载状态提示 --><ProgressBarandroid:layout_width="wrap_content"android:layout_height="wrap_content"android:visibility="@{viewModel.isLoading ? View.VISIBLE : View.GONE}" /><!-- 错误信息提示 --><TextViewandroid:layout_width="wrap_content"android:layout_height="wrap_content"android:text="@{viewModel.errorMessage}"android:textColor="#FF0000" /></LinearLayout>
</layout>
4.2 Activity(LoginActivity.java)
public class LoginActivity extends AppCompatActivity {private ActivityLoginBinding binding;private LoginViewModel viewModel;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);binding = DataBindingUtil.setContentView(this, R.layout.activity_login);// 初始化ViewModel(需注入Repository)UserRepository repository = new UserRepository(RetrofitClient.getApiService(), AppDatabase.get(this).userDao());viewModel = new ViewModelProvider(this, new ViewModelProvider.Factory() {@NonNull@Overridepublic <T extends ViewModel> T create(@NonNull Class<T> modelClass) {return (T) new LoginViewModel(repository);}}).get(LoginViewModel.class);binding.setViewModel(viewModel);binding.setLifecycleOwner(this); // 支持LiveData自动更新UI// 监听登录结果viewModel.getLoginResult().observe(this, success -> {if (success) startActivity(new Intent(this, MainActivity.class));});}
}

总结​

  • ​MVVM核心优势​​:
    通过 LiveData + DataBinding 实现​​数据驱动UI​​,ViewModel 管理业务逻辑,Repository 统一数据源。
  • ​关键实践​​:
    双向绑定简化输入处理、SingleLiveEvent 处理一次性事件、避免在 ViewModel 中持有 View 引用。
  • ​扩展性​​:
    可轻松集成 Room 持久化、Retrofit 网络请求、Dagger/Hilt 依赖注入。
http://www.lryc.cn/news/608766.html

相关文章:

  • 使用 Docker 部署 Golang 程序
  • 第四章:OSPF 协议
  • Dify中自定义工具类的类型
  • WebMvc自动配置流程讲解
  • MySQL 索引失效的场景与原因
  • 嵌入式开发学习———Linux环境下IO进程线程学习(二)
  • 04.Redis 的多实例
  • 笔试——Day27
  • 前端面试手撕题目全解析
  • 【数据迁移】Windows11 下将 Ubuntu 从 C 盘迁移到 D 盘
  • Redis——常用指令汇总指南(三)(哈希类型)
  • Odoo OWL前端框架全面学习指南 (后端开发者视角)
  • 三角洲行动ACE反作弊VT-d报错?CPU虚拟化如何开启!
  • GitOps:云原生时代的革命性基础设施管理范式
  • Ubuntu20.04 Carla安装与和Ros联合仿真
  • Ubuntu22.4部署大模型前置安装
  • AI + 云原生:正在引爆下一代应用的技术革命
  • LabVIEW小波变换检测信号断点
  • HCIP笔记(第四章)
  • 悬挂的绳子,它的函数方程是什么样子的?
  • Python Dash 全面讲解
  • 大屏项目展示
  • 基于Springboot+UniApp+Ai实现模拟面试小工具八:管理端基础功能实现
  • RAG与智能体技术全景解析:架构革新、场景落地与未来趋势
  • linux2.6 和 unix-v6 源码实验
  • uni-app学习笔记01-项目初始化及相关文件
  • Java小红书源码1:1还原uniapp_仿小红书源码
  • UniApp 实现顶部固定导航栏 Tab 及滚动变色效果
  • 7.13.B+树
  • io_setup系统调用及示例