当前位置: 首页 > 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.dtcms.com/a/312788.html

相关文章:

  • 【MySQL】MySQL中锁有哪些?
  • Flutter 函数的基本使用
  • day39 力扣198.打家劫舍 力扣213.打家劫舍II 力扣337.打家劫舍 III
  • 常见框架漏洞靶场攻略
  • Java 实现poi方式读取word文件内容
  • 力扣967:连续差相同的数字
  • Mysql1
  • Docker-03.快速入门-部署MySQL
  • python的蛋糕店管理系统
  • MySQL的创建管理表:
  • 求根到叶子节点数字之和
  • 【数据分享】南京诗歌文学地理数据集(获取方式看文末)
  • 电机结构设计与特性曲线分析:基于MATLAB和FEMM的仿真研究
  • 6. 平台总线
  • 机器学习第四课之决策树
  • Shell 脚本流程控制语句详解(四):while 循环详解
  • lua table常用函数汇总
  • Django 序列化详解:从 Model 到 JSON,全面掌握数据转换机制
  • 使用AndroidStudio调试Framework源码
  • 腾讯人脸识别
  • 数据治理:DQC(Data Quality Center,数据质量中心)概述
  • [嵌入式embed]C51单片机STC-ISP提示:正在检测目标单片机
  • 《前端无障碍设计的深层逻辑与实践路径》
  • MyBatis动态SQL精要:从<if>到<foreach>的灵活拼接之道
  • 高质量数据集|建设三大难点
  • [硬件电路-140]:模拟电路 - 信号处理电路 - 锁定放大器概述、工作原理、常见芯片、管脚定义
  • [硬件电路-133]:模拟电路 - 信号处理电路 - 电荷放大器概述、工作原理、常见芯片、管脚定义
  • 深度学习(鱼书)day10--与学习相关的技巧(后两节)
  • 仿TCmalloc内存分配器
  • 后端研发转型爬虫实战:Scrapy 二开爬虫框架的避坑指南