深入理解 Android DataBinding 与 ViewModel(17)

369 阅读31分钟

深入理解 Android DataBinding 与 ViewModel

一、Android DataBinding 概述

1.1 DataBinding 简介

Android DataBinding 是 Android 官方提供的一个支持库,允许开发者将布局文件(XML)与应用程序逻辑(通常是 ViewModel)直接绑定。这种绑定机制能够减少传统的 findViewById() 调用,使代码更加简洁、可维护。

DataBinding 的核心优势包括:

  • 减少样板代码,提高开发效率
  • 实现视图与数据的解耦,遵循 MVVM 架构模式
  • 提高代码的可读性和可维护性
  • 减少因空指针异常导致的崩溃

1.2 DataBinding 基本用法

使用 DataBinding 首先需要在项目的 build.gradle 文件中启用该功能:

android {
    ...
    dataBinding {
        enabled = true
    }
}

然后在布局文件中使用 <layout> 标签包裹根元素:

<layout xmlns:android="http://schemas.android.com/apk/res/android">
    <data>
        <variable
            name="user"
            type="com.example.User" />
    </data>
    
    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="@{user.name}" />
</layout>

在 Activity 或 Fragment 中,可以通过生成的 Binding 类来设置数据:

// 加载布局并获取 Binding 实例
ActivityMainBinding binding = ActivityMainBinding.inflate(getLayoutInflater());
// 设置变量值
binding.setUser(user);
// 设置生命周期所有者,使 LiveData 能够自动更新
binding.setLifecycleOwner(this);
// 设置ContentView
setContentView(binding.getRoot());

1.3 DataBinding 与 MVVM 架构

DataBinding 是实现 MVVM(Model-View-ViewModel)架构的关键组件之一。MVVM 架构模式将视图(View)与业务逻辑(ViewModel)分离,通过 DataBinding 实现两者之间的数据流动。

  • Model:负责数据的获取和存储,通常包括网络请求、数据库操作等
  • View:对应于 Activity、Fragment 或自定义 View,负责展示 UI
  • ViewModel:作为 View 和 Model 之间的桥梁,处理业务逻辑并提供数据给 View

DataBinding 使得 View 可以直接绑定到 ViewModel 中的数据,而不需要在 Activity 或 Fragment 中编写大量的胶水代码。这种分离使得代码更加模块化,易于测试和维护。

二、ViewModel 基础

2.1 ViewModel 简介

ViewModel 是 Android Architecture Components 中的一个组件,旨在存储和管理与 UI 相关的数据,并确保数据在配置更改(如屏幕旋转)后仍然存在。ViewModel 的设计遵循以下原则:

  • 分离 UI 逻辑与业务逻辑
  • 保持数据在配置更改期间的持久性
  • 简化单元测试
  • 促进代码复用

2.2 ViewModel 基本用法

使用 ViewModel 通常需要继承 ViewModel 类,并在其中定义与 UI 相关的数据:

import androidx.lifecycle.ViewModel;
import androidx.lifecycle.MutableLiveData;
import androidx.lifecycle.LiveData;

public class MyViewModel extends ViewModel {
    // 使用 MutableLiveData 存储可修改的数据
    private MutableLiveData<String> mUserName = new MutableLiveData<>();
    
    // 提供只读的 LiveData 给外部观察
    public LiveData<String> getUserName() {
        return mUserName;
    }
    
    // 更新用户名的方法
    public void setUserName(String userName) {
        mUserName.setValue(userName);
    }
}

在 Activity 或 Fragment 中获取 ViewModel 实例:

import androidx.lifecycle.ViewModelProvider;

public class MainActivity extends AppCompatActivity {
    private MyViewModel mViewModel;
    
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        
        // 通过 ViewModelProvider 获取 ViewModel 实例
        mViewModel = new ViewModelProvider(this).get(MyViewModel.class);
        
        // 观察 LiveData 的变化
        mViewModel.getUserName().observe(this, userName -> {
            // 更新 UI
        });
    }
}

2.3 ViewModel 的生命周期

ViewModel 的生命周期与 Activity 或 Fragment 的生命周期不同。ViewModel 的生命周期从它被创建开始,直到与之关联的 Activity 被完全销毁(非配置更改导致的销毁)或 Fragment 被移除。

下面是 ViewModel 生命周期的关键特点:

  • ViewModel 在 Activity 因配置更改(如旋转)而重新创建时保持不变
  • ViewModel 在 Activity 真正销毁时才会被清除
  • ViewModel 的生命周期比 Activity 或 Fragment 的生命周期更长

这种生命周期特性使得 ViewModel 非常适合存储和管理与 UI 相关的数据,特别是在处理异步操作和配置更改时。

三、DataBinding 与 ViewModel 的集成

3.1 集成方式

DataBinding 与 ViewModel 的集成主要通过以下步骤实现:

  1. 在布局文件中声明 ViewModel 变量
  2. 在 Activity 或 Fragment 中设置 ViewModel 实例
  3. 在布局文件中使用表达式绑定 ViewModel 中的数据和方法

下面是一个完整的示例:

<!-- activity_main.xml -->
<layout xmlns:android="http://schemas.android.com/apk/res/android">
    <data>
        <variable
            name="viewModel"
            type="com.example.MyViewModel" />
    </data>
    
    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical">
        
        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="@{viewModel.userName}" />
            
        <Button
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="更新名称"
            android:onClick="@{() -> viewModel.updateUserName()}" />
    </LinearLayout>
</layout>
// MyViewModel.java
public class MyViewModel extends ViewModel {
    // 使用 LiveData 存储用户名
    private MutableLiveData<String> userName = new MutableLiveData<>("初始名称");
    
    // 获取用户名的 LiveData
    public LiveData<String> getUserName() {
        return userName;
    }
    
    // 更新用户名的方法
    public void updateUserName() {
        userName.setValue("新名称");
    }
}
// MainActivity.java
public class MainActivity extends AppCompatActivity {
    private ActivityMainBinding binding;
    private MyViewModel viewModel;
    
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        
        // 初始化 DataBinding
        binding = ActivityMainBinding.inflate(getLayoutInflater());
        setContentView(binding.getRoot());
        
        // 获取 ViewModel 实例
        viewModel = new ViewModelProvider(this).get(MyViewModel.class);
        
        // 设置 ViewModel 到 DataBinding
        binding.setViewModel(viewModel);
        
        // 设置生命周期所有者,使 LiveData 能够自动更新
        binding.setLifecycleOwner(this);
    }
}

3.2 数据绑定机制

DataBinding 与 ViewModel 的集成基于以下核心机制:

  1. 表达式语言:DataBinding 使用一种简单的表达式语言来绑定数据和方法。例如:android:text="@{viewModel.userName}"

  2. LiveData 支持:DataBinding 能够自动观察 LiveData 的变化,并在数据更新时自动更新 UI。

  3. 双向绑定:除了单向绑定,DataBinding 还支持双向绑定,例如在 EditText 中:android:text="@={viewModel.inputText}"

  4. 事件绑定:可以直接在布局文件中绑定 ViewModel 中的方法作为事件处理程序,例如:android:onClick="@{() -> viewModel.doSomething()}"

3.3 优势分析

将 DataBinding 与 ViewModel 集成带来了以下优势:

  1. 代码简洁:减少了 Activity 和 Fragment 中的样板代码,使代码更加清晰。

  2. 视图与逻辑分离:遵循 MVVM 架构模式,使视图和业务逻辑完全分离。

  3. 自动数据更新:通过 LiveData 和 DataBinding 的结合,实现了数据到 UI 的自动更新。

  4. 配置更改处理:ViewModel 在配置更改时保持数据,避免了数据丢失和重复加载。

  5. 易于测试:ViewModel 可以独立测试,不依赖于 Activity 或 Fragment。

四、ViewModel 源码分析

4.1 ViewModel 类结构

ViewModel 是一个抽象类,定义在 androidx.lifecycle 包中:

package androidx.lifecycle;

import androidx.annotation.NonNull;
import androidx.annotation.Nullable;

/**
 * ViewModel 类是一个抽象类,用于存储和管理与 UI 相关的数据
 * 设计目的是在配置更改(如屏幕旋转)期间保留数据
 */
public abstract class ViewModel {
    // 标记 ViewModel 是否已经被清理
    private boolean mCleared = false;
    
    /**
     * 当 ViewModel 不再被使用且将被销毁时调用
     * 子类可以重写此方法来清理资源,如取消网络请求或数据库操作
     */
    @SuppressWarnings("WeakerAccess")
    protected void onCleared() {
    }
    
    /**
     * 由 ViewModelStore 调用,用于清理 ViewModel
     * 此方法会设置 mCleared 标志并调用 onCleared() 方法
     */
    final void clear() {
        mCleared = true;
        // 调用 onCleared() 方法,由子类实现具体的清理逻辑
        onCleared();
    }
    
    /**
     * 检查 ViewModel 是否已被清理
     * @return 如果 ViewModel 已被清理返回 true,否则返回 false
     */
    public final boolean isCleared() {
        return mCleared;
    }
}

ViewModel 类的核心功能非常简单,主要提供了一个生命周期回调方法 onCleared(),用于在 ViewModel 不再被使用时进行资源清理。

4.2 ViewModelProvider 源码分析

ViewModelProvider 是用于获取 ViewModel 实例的核心类,它负责创建和管理 ViewModel 实例。

package androidx.lifecycle;

import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.RestrictTo;
import androidx.arch.core.util.Function;

import java.util.HashMap;
import java.util.Map;

/**
 * ViewModelProvider 用于创建和管理 ViewModel 实例
 */
public class ViewModelProvider {
    // 存储 ViewModel 实例的 ViewModelStore
    private final ViewModelStore mViewModelStore;
    // 创建 ViewModel 的工厂
    private final Factory mFactory;
    
    /**
     * 使用给定的工厂和 ViewModelStore 创建 ViewModelProvider
     * @param store 存储 ViewModel 的 ViewModelStore
     * @param factory 创建 ViewModel 的工厂
     */
    public ViewModelProvider(@NonNull ViewModelStore store, @NonNull Factory factory) {
        mFactory = factory;
        mViewModelStore = store;
    }
    
    /**
     * 获取 ViewModel 实例的通用方法
     * @param modelClass ViewModel 的类
     * @param <T> ViewModel 的类型
     * @return ViewModel 实例
     */
    @NonNull
    @MainThread
    public <T extends ViewModel> T get(@NonNull Class<T> modelClass) {
        String canonicalName = modelClass.getCanonicalName();
        if (canonicalName == null) {
            throw new IllegalArgumentException("Local and anonymous classes can not be ViewModels");
        }
        return get(DEFAULT_KEY + ":" + canonicalName, modelClass);
    }
    
    /**
     * 使用指定的键获取 ViewModel 实例
     * @param key 用于标识 ViewModel 的键
     * @param modelClass ViewModel 的类
     * @param <T> ViewModel 的类型
     * @return ViewModel 实例
     */
    @NonNull
    @MainThread
    public <T extends ViewModel> T get(@NonNull String key, @NonNull Class<T> modelClass) {
        // 从 ViewModelStore 中获取 ViewModel
        ViewModel viewModel = mViewModelStore.get(key);
        
        if (modelClass.isInstance(viewModel)) {
            // 如果已经存在且类型匹配,直接返回
            if (mFactory instanceof OnRequeryFactory) {
                ((OnRequeryFactory) mFactory).onRequery(viewModel);
            }
            return (T) viewModel;
        } else {
            // 类型不匹配时,如果存在旧的 ViewModel 则清除
            if (viewModel != null) {
                // TODO: log a warning.
            }
        }
        // 使用工厂创建新的 ViewModel
        if (mFactory instanceof KeyedFactory) {
            viewModel = ((KeyedFactory) mFactory).create(key, modelClass);
        } else {
            viewModel = mFactory.create(modelClass);
        }
        // 将新创建的 ViewModel 存入 ViewModelStore
        mViewModelStore.put(key, viewModel);
        return (T) viewModel;
    }
    
    // 其他内部类和方法...
}

ViewModelProvider 的核心功能是通过工厂模式创建 ViewModel 实例,并将其存储在 ViewModelStore 中。这样可以确保在配置更改时,同一个 ViewModel 实例会被重复使用。

4.3 ViewModelStore 源码分析

ViewModelStore 是一个简单的存储类,用于存储 ViewModel 实例:

package androidx.lifecycle;

import androidx.annotation.NonNull;
import androidx.annotation.Nullable;

import java.util.HashMap;
import java.util.Map;

/**
 * ViewModelStore 用于存储 ViewModel 实例
 * 通常与 Activity 或 Fragment 关联
 */
public class ViewModelStore {
    // 使用 HashMap 存储 ViewModel 实例
    private final HashMap<String, ViewModel> mMap = new HashMap<>();
    
    /**
     * 向存储中添加 ViewModel
     * @param key 用于标识 ViewModel 的键
     * @param viewModel ViewModel 实例
     */
    final void put(String key, ViewModel viewModel) {
        // 检查是否已存在相同键的 ViewModel,如果存在则清除
        ViewModel oldViewModel = mMap.put(key, viewModel);
        if (oldViewModel != null) {
            oldViewModel.clear();
        }
    }
    
    /**
     * 从存储中获取 ViewModel
     * @param key 用于标识 ViewModel 的键
     * @return ViewModel 实例,如果不存在则返回 null
     */
    final ViewModel get(String key) {
        return mMap.get(key);
    }
    
    /**
     * 清理存储中的所有 ViewModel
     * 调用每个 ViewModel 的 clear() 方法进行资源清理
     */
    public final void clear() {
        for (ViewModel vm : mMap.values()) {
            vm.clear();
        }
        mMap.clear();
    }
}

ViewModelStore 的实现非常简单,主要使用一个 HashMap 来存储 ViewModel 实例,并提供了添加、获取和清理 ViewModel 的方法。

4.4 ViewModelProvider.Factory 源码分析

ViewModelProvider.Factory 是一个接口,定义了创建 ViewModel 的工厂方法:

package androidx.lifecycle;

import androidx.annotation.NonNull;

/**
 * 用于创建 ViewModel 的工厂接口
 */
public interface Factory {
    /**
     * 创建给定类的 ViewModel 实例
     * @param modelClass ViewModel 的类
     * @param <T> ViewModel 的类型
     * @return ViewModel 实例
     */
    @NonNull
    <T extends ViewModel> T create(@NonNull Class<T> modelClass);
}

ViewModelProvider 提供了几种默认的工厂实现:

  1. NewInstanceFactory:使用反射通过无参构造函数创建 ViewModel
/**
 * 默认的工厂实现,使用反射通过无参构造函数创建 ViewModel
 */
public static class NewInstanceFactory implements Factory {
    @NonNull
    @Override
    public <T extends ViewModel> T create(@NonNull Class<T> modelClass) {
        // 检查 ViewModel 类是否是 ViewModel 的子类
        try {
            return modelClass.newInstance();
        } catch (InstantiationException e) {
            throw new RuntimeException("Cannot create an instance of " + modelClass, e);
        } catch (IllegalAccessException e) {
            throw new RuntimeException("Cannot create an instance of " + modelClass, e);
        }
    }
}
  1. AndroidViewModelFactory:专门用于创建 AndroidViewModel 子类的工厂
/**
 * 用于创建 AndroidViewModel 子类的工厂
 * AndroidViewModel 需要 Application 作为参数
 */
public static class AndroidViewModelFactory extends ViewModelProvider.NewInstanceFactory {
    // 单例实例
    private static AndroidViewModelFactory sInstance;
    
    // 应用程序实例
    private final Application mApplication;
    
    /**
     * 获取 AndroidViewModelFactory 的单例实例
     * @param application 应用程序实例
     * @return AndroidViewModelFactory 实例
     */
    @NonNull
    public static AndroidViewModelFactory getInstance(@NonNull Application application) {
        if (sInstance == null) {
            sInstance = new AndroidViewModelFactory(application);
        }
        return sInstance;
    }
    
    /**
     * 构造函数
     * @param application 应用程序实例
     */
    public AndroidViewModelFactory(@NonNull Application application) {
        mApplication = application;
    }
    
    @NonNull
    @Override
    public <T extends ViewModel> T create(@NonNull Class<T> modelClass) {
        // 检查 ViewModel 是否是 AndroidViewModel 的子类
        if (AndroidViewModel.class.isAssignableFrom(modelClass)) {
            // 尝试使用 Application 参数创建实例
            try {
                return modelClass.getConstructor(Application.class).newInstance(mApplication);
            } catch (NoSuchMethodException e) {
                throw new RuntimeException("Cannot create an instance of " + modelClass, e);
            } catch (IllegalAccessException e) {
                throw new RuntimeException("Cannot create an instance of " + modelClass, e);
            } catch (InstantiationException e) {
                throw new RuntimeException("Cannot create an instance of " + modelClass, e);
            } catch (InvocationTargetException e) {
                throw new RuntimeException("Cannot create an instance of " + modelClass, e);
            }
        }
        // 如果不是 AndroidViewModel 的子类,使用默认的工厂方法
        return super.create(modelClass);
    }
}

五、DataBinding 源码分析

5.1 DataBinding 生成的代码

当启用 DataBinding 后,Android 构建系统会为每个布局文件生成对应的 Binding 类。例如,对于布局文件 activity_main.xml,会生成 ActivityMainBinding 类。

下面是一个简化的生成代码示例:

// ActivityMainBinding.java (简化版)
public abstract class ActivityMainBinding extends ViewDataBinding {
    @NonNull
    public final TextView textView;
    
    @Bindable
    protected User mUser;
    
    protected ActivityMainBinding(Object _bindingComponent, View _root,
            int _localFieldCount,
            TextView textView) {
        super(_bindingComponent, _root, _localFieldCount);
        this.textView = textView;
    }
    
    // 设置 User 对象的方法
    public abstract void setUser(@Nullable User user);
    
    // 获取 User 对象的方法
    @Nullable
    public abstract User getUser();
}

实际生成的代码会更加复杂,包含了数据绑定的具体实现逻辑。

5.2 DataBinding 初始化过程

DataBinding 的初始化过程主要发生在 Activity 或 Fragment 中:

// 在 Activity 中使用 DataBinding
ActivityMainBinding binding = ActivityMainBinding.inflate(getLayoutInflater());
setContentView(binding.getRoot());

ActivityMainBinding.inflate() 方法最终会调用生成的 Binding 类的静态方法:

// ActivityMainBinding.java (简化版)
public static ActivityMainBinding inflate(LayoutInflater inflater, ViewGroup root, boolean attachToRoot) {
    // 解析布局文件
    View rootView = inflater.inflate(R.layout.activity_main, root, false);
    if (attachToRoot) {
        root.addView(rootView);
    }
    return bind(rootView);
}

public static ActivityMainBinding bind(View rootView) {
    // 获取布局中的各个视图
    TextView textView = rootView.findViewById(R.id.textView);
    // 创建并返回 Binding 实例
    return new ActivityMainBindingImpl(rootView, textView);
}

5.3 数据绑定表达式处理

DataBinding 使用表达式语言来处理布局文件中的绑定表达式。例如:

android:text="@{user.name}"

这种表达式会在生成的代码中被转换为具体的实现:

// ActivityMainBindingImpl.java (简化版)
private void executeBindings() {
    synchronized(this) {
        // 获取变量值
        User user = mUser;
        String userContentDescription = null;
        String userText = null;
        
        // 计算表达式的值
        if (user != null) {
            userContentDescription = user.getContentDescription();
            userText = user.getText();
        }
        
        // 更新视图
        updateRegistration(0, user);
        textView.setText(userText);
        textView.setContentDescription(userContentDescription);
    }
}

5.4 LiveData 绑定处理

当绑定 LiveData 对象时,DataBinding 会自动注册一个观察者:

// 简化版的 LiveData 绑定实现
private void registerLiveDataBindings(int localFieldId) {
    if (localFieldId == 0) {
        // 注册对 LiveData 的观察
        if (mUserLiveData != null) {
            if (mUserLiveDataObserver == null) {
                mUserLiveDataObserver = new Observer<User>() {
                    @Override
                    public void onChanged(@Nullable User user) {
                        mUser = user;
                        // 数据变化时重新执行绑定
                        requestRebind();
                    }
                };
            }
            // 观察 LiveData 的变化
            mUserLiveData.observeForever(mUserLiveDataObserver);
        }
    }
}

当 LiveData 的值发生变化时,观察者会被触发,然后调用 requestRebind() 方法重新执行数据绑定。

5.5 双向绑定实现

双向绑定使用特殊的语法 @={} 实现,例如:

android:text="@={viewModel.inputText}"

双向绑定的实现涉及两个方向的数据流动:

  1. 从 ViewModel 到 View:与单向绑定相同
  2. 从 View 到 ViewModel:通过设置 View 的监听器实现

下面是一个简化的 EditText 双向绑定实现:

// 简化版的双向绑定实现
private void setupEditTextBinding(final EditText editText, final ObservableField<String> text) {
    // 设置初始值
    editText.setText(text.get());
    
    // 添加文本变化监听器
    editText.addTextChangedListener(new TextWatcher() {
        @Override
        public void beforeTextChanged(CharSequence s, int start, int count, int after) {}
        
        @Override
        public void onTextChanged(CharSequence s, int start, int before, int count) {}
        
        @Override
        public void afterTextChanged(Editable s) {
            // 当文本变化时,更新 ViewModel 中的值
            if (!s.toString().equals(text.get())) {
                text.set(s.toString());
            }
        }
    });
    
    // 添加对 ObservableField 的观察
    text.addOnPropertyChangedCallback(new Observable.OnPropertyChangedCallback() {
        @Override
        public void onPropertyChanged(Observable sender, int propertyId) {
            // 当 ViewModel 中的值变化时,更新 EditText
            String newValue = text.get();
            if (!newValue.equals(editText.getText().toString())) {
                editText.setText(newValue);
            }
        }
    });
}

六、ViewModel 生命周期管理

6.1 ViewModel 与 Activity/Fragment 生命周期的关系

ViewModel 的生命周期与 Activity 或 Fragment 的生命周期密切相关,但又有所不同。下面是它们之间的关系图:

Activity/Fragment 生命周期              ViewModel 生命周期

onCreate()                             创建 ViewModel
    |                                     |
    |                                     |
onStart()                              存在
    |                                     |
    |                                     |
onResume()                             存在
    |                                     |
    |                                     |
onPause()                              存在
    |                                     |
    |                                     |
onStop()                               存在
    |                                     |
    |                                     |
onDestroy() (非配置更改)               销毁 ViewModel

当 Activity 或 Fragment 因配置更改(如屏幕旋转)而重新创建时,ViewModel 不会被销毁,而是继续存在并被新的 Activity 或 Fragment 实例复用。

6.2 ViewModel 生命周期管理源码分析

ViewModel 的生命周期管理主要由 ViewModelStoreOwner 和 ViewModelStore 完成。

6.2.1 ViewModelStoreOwner 接口

ViewModelStoreOwner 是一个接口,定义了获取 ViewModelStore 的方法:

package androidx.lifecycle;

import androidx.annotation.NonNull;

/**
 * 实现此接口的类可以提供 ViewModelStore
 */
public interface ViewModelStoreOwner {
    /**
     * 返回存储 ViewModel 的 ViewModelStore
     * @return ViewModelStore 实例
     */
    @NonNull
    ViewModelStore getViewModelStore();
}

Activity 和 Fragment 都实现了这个接口,因此它们可以提供 ViewModelStore。

6.2.2 ComponentActivity 中的实现

ComponentActivity 是 AndroidX 中所有 Activity 的基类,它实现了 ViewModelStoreOwner 接口:

public class ComponentActivity extends androidx.core.app.ComponentActivity implements
        LifecycleOwner,
        ViewModelStoreOwner,
        SavedStateRegistryOwner,
        OnBackPressedDispatcherOwner {
    
    // ViewModelStore 实例
    private ViewModelStore mViewModelStore;
    
    // 其他代码...
    
    @NonNull
    @Override
    public ViewModelStore getViewModelStore() {
        if (getApplication() == null) {
            throw new IllegalStateException("Your activity is not yet attached to the "
                    + "Application instance. You can't request ViewModel before onCreate call.");
        }
        if (mViewModelStore == null) {
            NonConfigurationInstances nc =
                    (NonConfigurationInstances) getLastNonConfigurationInstance();
            if (nc != null) {
                // 如果存在非配置实例,从其中获取 ViewModelStore
                mViewModelStore = nc.viewModelStore;
            }
            if (mViewModelStore == null) {
                // 如果没有非配置实例或其中没有 ViewModelStore,则创建新的
                mViewModelStore = new ViewModelStore();
            }
        }
        return mViewModelStore;
    }
    
    @Override
    public final Object onRetainNonConfigurationInstance() {
        // 保留非配置实例
        Object custom = onRetainCustomNonConfigurationInstance();
        
        ViewModelStore viewModelStore = mViewModelStore;
        if (viewModelStore == null) {
            // 检查是否有之前保留的非配置实例
            NonConfigurationInstances nc =
                    (NonConfigurationInstances) getLastNonConfigurationInstance();
            if (nc != null) {
                viewModelStore = nc.viewModelStore;
            }
        }
        
        if (viewModelStore == null && custom == null) {
            return null;
        }
        
        NonConfigurationInstances nci = new NonConfigurationInstances();
        nci.custom = custom;
        nci.viewModelStore = viewModelStore;
        return nci;
    }
    
    static final class NonConfigurationInstances {
        Object custom;
        ViewModelStore viewModelStore;
    }
    
    // 其他代码...
}
6.2.3 Fragment 中的实现

Fragment 也实现了 ViewModelStoreOwner 接口:

public class Fragment implements ComponentCallbacks2, OnCreateContextMenuListener,
        LifecycleOwner, ViewModelStoreOwner, SavedStateRegistryOwner {
    
    // ViewModelStore 实例
    private ViewModelStore mViewModelStore;
    
    // 其他代码...
    
    @NonNull
    @Override
    public ViewModelStore getViewModelStore() {
        if (mViewModelStore == null) {
            mViewModelStore = new ViewModelStore();
        }
        return mViewModelStore;
    }
    
    // 其他代码...
    
    void performDestroy() {
        mCalled = true;
        Lifecycle lifecycle = getLifecycle();
        lifecycle.handleLifecycleEvent(Lifecycle.Event.ON_DESTROY);
        
        // 如果 Fragment 不是被保留,清理 ViewModelStore
        if (!mRetaining) {
            if (mViewModelStore != null && !getActivity().isChangingConfigurations()) {
                mViewModelStore.clear();
            }
            // 其他清理操作...
        }
    }
}

6.3 配置更改时的 ViewModel 保留机制

当 Activity 因配置更改而重新创建时,ViewModel 的保留机制主要依赖于 Activity 的 onRetainNonConfigurationInstance() 方法。

// ComponentActivity.java
@Override
public final Object onRetainNonConfigurationInstance() {
    Object custom = onRetainCustomNonConfigurationInstance();
    
    ViewModelStore viewModelStore = mViewModelStore;
    if (viewModelStore == null) {
        // 检查是否有之前保留的非配置实例
        NonConfigurationInstances nc =
                (NonConfigurationInstances) getLastNonConfigurationInstance();
        if (nc != null) {
            viewModelStore = nc.viewModelStore;
        }
    }
    
    if (viewModelStore == null && custom == null) {
        return null;
    }
    
    NonConfigurationInstances nci = new NonConfigurationInstances();
    nci.custom = custom;
    nci.viewModelStore = viewModelStore;
    return nci;
}

当 Activity 重新创建时,会调用 getLastNonConfigurationInstance() 方法获取之前保留的实例:

// ComponentActivity.java
@NonNull
@Override
public ViewModelStore getViewModelStore() {
    if (getApplication() == null) {
        throw new IllegalStateException("Your activity is not yet attached to the "
                + "Application instance. You can't request ViewModel before onCreate call.");
    }
    if (mViewModelStore == null) {
        NonConfigurationInstances nc =
                (NonConfigurationInstances) getLastNonConfigurationInstance();
        if (nc != null) {
            // 从保留的实例中获取 ViewModelStore
            mViewModelStore = nc.viewModelStore;
        }
        if (mViewModelStore == null) {
            mViewModelStore = new ViewModelStore();
        }
    }
    return mViewModelStore;
}

这种机制确保了在配置更改时,ViewModel 实例不会被销毁,而是被保留并复用。

6.4 ViewModel 的销毁时机

ViewModel 的销毁发生在与之关联的 Activity 或 Fragment 真正销毁时(非配置更改导致的销毁)。

对于 Activity:

// ComponentActivity.java
@Override
protected void onDestroy() {
    super.onDestroy();
    
    if (mViewModelStore != null && !isChangingConfigurations()) {
        // 如果 Activity 不是因为配置更改而销毁,清理 ViewModelStore
        mViewModelStore.clear();
    }
    
    // 其他清理操作...
}

对于 Fragment:

// Fragment.java
void performDestroy() {
    mCalled = true;
    Lifecycle lifecycle = getLifecycle();
    lifecycle.handleLifecycleEvent(Lifecycle.Event.ON_DESTROY);
    
    // 如果 Fragment 不是被保留,清理 ViewModelStore
    if (!mRetaining) {
        if (mViewModelStore != null && !getActivity().isChangingConfigurations()) {
            mViewModelStore.clear();
        }
        // 其他清理操作...
    }
}

当 ViewModelStore 的 clear() 方法被调用时,会遍历所有存储的 ViewModel 并调用它们的 clear() 方法:

// ViewModelStore.java
public final void clear() {
    for (ViewModel vm : mMap.values()) {
        vm.clear();
    }
    mMap.clear();
}

ViewModel 的 clear() 方法会触发 onCleared() 回调,允许子类进行资源清理:

// ViewModel.java
final void clear() {
    mCleared = true;
    // 调用 onCleared() 方法,由子类实现具体的清理逻辑
    onCleared();
}

七、DataBinding 与 ViewModel 集成的最佳实践

7.1 使用 LiveData 与 DataBinding 结合

LiveData 与 DataBinding 的结合是实现数据驱动 UI 的最佳方式。LiveData 能够感知生命周期,并在数据变化时自动更新 UI。

// ViewModel 中使用 LiveData
public class MyViewModel extends ViewModel {
    private MutableLiveData<String> mUserName = new MutableLiveData<>();
    
    public LiveData<String> getUserName() {
        return mUserName;
    }
    
    public void fetchUserName() {
        // 从网络或数据库获取用户名
        // ...
        mUserName.setValue("新用户名");
    }
}
<!-- 布局文件中直接绑定 LiveData -->
<TextView
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:text="@{viewModel.userName}" />

7.2 双向绑定的正确使用

双向绑定可以简化表单输入处理,但应谨慎使用,避免造成数据流向混乱。

// ViewModel 中使用 ObservableField 支持双向绑定
public class MyViewModel extends ViewModel {
    public final ObservableField<String> userName = new ObservableField<>("");
    public final ObservableField<String> password = new ObservableField<>("");
    
    public void login() {
        String username = userName.get();
        String pwd = password.get();
        // 执行登录逻辑
    }
}
<!-- 布局文件中使用双向绑定 -->
<EditText
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:text="@={viewModel.userName}" />
    
<EditText
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:text="@={viewModel.password}"
    android:inputType="textPassword" />
    
<Button
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:text="登录"
    android:onClick="@{() -> viewModel.login()}" />

7.3 避免在 ViewModel 中持有 Activity 或 View 的引用

ViewModel 的设计初衷是独立于 Activity 和 Fragment 的生命周期,因此不应该在 ViewModel 中持有 Activity 或 View 的引用,以免造成内存泄漏。

如果需要 Application 上下文,可以继承 AndroidViewModel:

public class MyViewModel extends AndroidViewModel {
    public MyViewModel(@NonNull Application application) {
        super(application);
        // 使用 application 上下文
    }
    
    // 其他代码...
}

7.4 使用自定义 BindingAdapter

自定义 BindingAdapter 可以扩展 DataBinding 的功能,实现更复杂的绑定逻辑。

// 自定义 BindingAdapter 示例
public class BindingAdapters {
    @BindingAdapter("imageUrl")
    public static void loadImage(ImageView view, String url) {
        Glide.with(view.getContext())
                .load(url)
                .into(view);
    }
    
    @BindingAdapter("android:onScrollChanged")
    public static void setOnScrollChangedListener(NestedScrollView view, 
            NestedScrollView.OnScrollChangeListener listener) {
        view.setOnScrollChangeListener(listener);
    }
}
<!-- 在布局文件中使用自定义 BindingAdapter -->
<ImageView
    android:layout_width="100dp"
    android:layout_height="100dp"
    app:imageUrl="@{viewModel.imageUrl}" />
    
<NestedScrollView
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    app:onScrollChanged="@{(v, scrollX, scrollY, oldScrollX, oldScrollY) -> viewModel.onScrollChanged(scrollY)}">
    <!-- 内容 -->
</NestedScrollView>

7.5 合理处理 ViewModel 中的异步操作

ViewModel 中经常需要执行异步操作,如网络请求或数据库查询。应该使用适当的方式处理这些异步操作,确保在 ViewModel 销毁时能够取消未完成的操作。

public class MyViewModel extends ViewModel {
    private CompositeDisposable mDisposables = new CompositeDisposable();
    
    public void fetchData() {
        Disposable disposable = apiService.getData()
                .subscribeOn(Schedulers.io())
                .observeOn(AndroidSchedulers.mainThread())
                .subscribe(
                        data -> mData.setValue(data),
                        error -> mError.setValue(error.getMessage())
                );
        
        mDisposables.add(disposable);
    }
    
    @Override
    protected void onCleared() {
        super.onCleared();
        // 清理未完成的异步操作
        mDisposables.clear();
    }
}

7.6 使用 ViewModel 作用域管理

ViewModel 可以有不同的作用域,如 Activity 级别或 Fragment 级别。根据需求选择合适的作用域可以避免数据共享问题。

// Activity 级别 ViewModel
ViewModelProvider(this).get(MyViewModel.class);

// Fragment 级别 ViewModel
ViewModelProvider(requireActivity()).get(MyViewModel.class);

7.7 单元测试 ViewModel

ViewModel 应该是易于测试的,因为它不依赖于 Activity 或 Fragment。可以使用 JUnit 和 Mockito 等工具编写单元测试。

@RunWith(MockitoJUnitRunner.class)
public class MyViewModelTest {
    private MyViewModel mViewModel;
    @Mock private DataRepository mRepository;
    
    @Before
    public void setup() {
        mViewModel = new MyViewModel(mRepository);
    }
    
    @Test
    public void testFetchData() {
        // 设置模拟数据
        Data mockData = new Data("测试数据");
        when(mRepository.fetchData()).thenReturn(Observable.just(mockData));
        
        // 执行测试
        mViewModel.fetchData();
        
        // 验证结果
        verify(mRepository).fetchData();
        assertEquals(mockData, mViewModel.getData().getValue());
    }
}

7.8 使用 ViewModel 工厂提供参数

当 ViewModel 需要参数时,可以使用 ViewModel 工厂来提供这些参数。

public class MyViewModelFactory implements ViewModelProvider.Factory {
    private final DataRepository mRepository;
    private final String mUserId;
    
    public MyViewModelFactory(DataRepository repository, String userId) {
        mRepository = repository;
        mUserId = userId;
    }
    
    @NonNull
    @Override
    public <T extends ViewModel> T create(@NonNull Class<T> modelClass) {
        if (modelClass.isAssignableFrom(MyViewModel.class)) {
            return (T) new MyViewModel(mRepository, mUserId);
        }
        throw new IllegalArgumentException("Unknown ViewModel class");
    }
}
// 在 Activity 中使用工厂创建 ViewModel
MyViewModelFactory factory = new MyViewModelFactory(repository, userId);
MyViewModel viewModel = new ViewModelProvider(this, factory).get(MyViewModel.class);

八、常见问题与解决方案

8.1 内存泄漏问题

问题描述:如果在 ViewModel 中持有 Activity、Fragment 或 View 的引用,可能会导致内存泄漏。

解决方案:

  1. 不要在 ViewModel 中持有 Activity、Fragment 或 View 的引用
  2. 如果需要上下文,可以继承 AndroidViewModel 并使用 Application 上下文
  3. 确保在 ViewModel 销毁时取消所有异步操作

8.2 LiveData 数据倒灌问题

问题描述:当 Activity 或 Fragment 因配置更改重建时,LiveData 可能会发送之前的值,导致数据倒灌。

解决方案:

  1. 使用 SingleLiveEvent 包装 LiveData,确保每个值只被消费一次
  2. 使用 EventWrapper 类包装数据,添加消费状态标志
  3. 使用 LiveData 的扩展方法,如 Transformations.map() 过滤旧数据

8.3 双向绑定导致的无限循环问题

问题描述:在某些情况下,双向绑定可能会导致无限循环更新。

解决方案:

  1. 在 ViewModel 中添加标志位,避免不必要的更新
  2. 使用 LiveData 的 distinctUntilChanged() 方法过滤重复数据
  3. 在数据处理逻辑中添加条件判断,避免循环更新

8.4 ViewModel 与协程结合的问题

问题描述:在 ViewModel 中使用协程时,如果不正确管理协程作用域,可能会导致内存泄漏或崩溃。

解决方案:

  1. 使用 viewModelScope 管理协程,确保在 ViewModel 销毁时取消协程
  2. 在协程中捕获异常,避免因未处理的异常导致崩溃
  3. 使用 flow 替代 LiveData,利用 flow 的背压机制处理数据流

8.5 DataBinding 表达式复杂问题

问题描述:布局文件中的 DataBinding 表达式过于复杂,导致代码难以维护。

解决方案:

  1. 将复杂的逻辑移到 ViewModel 或 BindingAdapter 中
  2. 使用计算属性或方法简化表达式
  3. 避免在布局文件中编写过多的业务逻辑

8.6 数据更新不及时问题

问题描述:DataBinding 有时不能及时更新 UI。

解决方案:

  1. 确保在主线程更新 LiveData 或 ObservableField 的值
  2. 使用 LiveData 的 postValue() 方法在后台线程更新值
  3. 检查是否正确设置了 LifecycleOwner
  4. 确保在布局文件中正确绑定了数据

8.7 多模块项目中的 DataBinding 问题

问题描述:在多模块项目中使用 DataBinding 时,可能会遇到找不到生成类或绑定失败的问题。

解决方案:

  1. 确保每个模块都启用了 DataBinding
  2. 检查布局文件的命名和位置是否正确
  3. 清理并重新构建项目
  4. 检查模块之间的依赖关系是否正确

8.8 ViewModel 作用域混淆问题

问题描述:在 Fragment 中使用 ViewModel 时,可能会混淆 Activity 级别和 Fragment 级别 ViewModel 的作用域。

解决方案:

  1. 明确需要的 ViewModel 作用域
  2. 使用 ViewModelProvider(this) 获取 Fragment 级别的 ViewModel
  3. 使用 ViewModelProvider(requireActivity()) 获取 Activity 级别的 ViewModel
  4. 避免在不同作用域的 ViewModel 中存储相同类型的数据

九、高级应用场景

9.1 与 Navigation 组件结合

ViewModel 可以与 Navigation 组件结合,实现页面间的数据共享和传递。

// 在 SharedViewModel 中定义要共享的数据
public class SharedViewModel extends ViewModel {
    private final MutableLiveData<String> selectedItem = new MutableLiveData<>();
    
    public void selectItem(String item) {
        selectedItem.setValue(item);
    }
    
    public LiveData<String> getSelectedItem() {
        return selectedItem;
    }
}
// 在发送数据的 Fragment 中
public class ListFragment extends Fragment {
    private SharedViewModel sharedViewModel;
    
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        sharedViewModel = new ViewModelProvider(requireActivity()).get(SharedViewModel.class);
    }
    
    private void onItemClick(String item) {
        sharedViewModel.selectItem(item);
        Navigation.findNavController(requireView()).navigate(R.id.action_list_to_detail);
    }
}
// 在接收数据的 Fragment 中
public class DetailFragment extends Fragment {
    private SharedViewModel sharedViewModel;
    
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        sharedViewModel = new ViewModelProvider(requireActivity()).get(SharedViewModel.class);
    }
    
    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
                             Bundle savedInstanceState) {
        View root = inflater.inflate(R.layout.fragment_detail, container, false);
        
        sharedViewModel.getSelectedItem().observe(getViewLifecycleOwner(), item -> {
            // 更新 UI
        });
        
        return root;
    }
}

9.2 与 Room 数据库结合

ViewModel 可以与 Room 数据库结合,实现数据的持久化存储和自动更新。

// 定义数据实体
@Entity(tableName = "users")
public class User {
    @PrimaryKey
    private int id;
    private String name;
    private String email;
    
    // getter 和 setter 方法
}
// 定义 DAO
@Dao
public interface UserDao {
    @Query("SELECT * FROM users")
    LiveData<List<User>> getAllUsers();
    
    @Insert
    void insert(User user);
    
    @Update
    void update(User user);
    
    @Delete
    void delete(User user);
}
// 定义 Repository
public class UserRepository {
    private UserDao mUserDao;
    private LiveData<List<User>> mAllUsers;
    
    public UserRepository(Application application) {
        AppDatabase db = AppDatabase.getDatabase(application);
        mUserDao = db.userDao();
        mAllUsers = mUserDao.getAllUsers();
    }
    
    public LiveData<List<User>> getAllUsers() {
        return mAllUsers;
    }
    
    public void insert(User user) {
        new insertAsyncTask(mUserDao).execute(user);
    }
    
    private static class insertAsyncTask extends AsyncTask<User, Void, Void> {
        private UserDao mAsyncTaskDao;
        
        insertAsyncTask(UserDao dao) {
            mAsyncTaskDao = dao;
        }
        
        @Override
        protected Void doInBackground(final User... params) {
            mAsyncTaskDao.insert(params[0]);
            return null;
        }
    }
}
// 定义 ViewModel
public class UserViewModel extends AndroidViewModel {
    private UserRepository mRepository;
    private LiveData<List<User>> mAllUsers;
    
    public UserViewModel(Application application) {
        super(application);
        mRepository = new UserRepository(application);
        mAllUsers = mRepository.getAllUsers();
    }
    
    public LiveData<List<User>> getAllUsers() {
        return mAllUsers;
    }
    
    public void insert(User user) {
        mRepository.insert(user);
    }
}
// 在 Activity 中使用
public class MainActivity extends AppCompatActivity {
    private UserViewModel mUserViewModel;
// 在 Activity 中使用
public class MainActivity extends AppCompatActivity {
    private UserViewModel mUserViewModel;
    
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        
        // 获取 ViewModel 实例
        mUserViewModel = new ViewModelProvider(this).get(UserViewModel.class);
        
        // 观察用户数据变化
        mUserViewModel.getAllUsers().observe(this, users -> {
            // 更新 UI
            updateUserList(users);
        });
        
        // 添加用户按钮点击事件
        findViewById(R.id.add_user_button).setOnClickListener(v -> {
            User user = new User();
            user.setName("新用户");
            user.setEmail("newuser@example.com");
            mUserViewModel.insert(user);
        });
    }
    
    private void updateUserList(List<User> users) {
        // 更新用户列表 UI
    }
}

9.3 与 WorkManager 结合

ViewModel 可以与 WorkManager 结合,实现后台任务的管理和状态跟踪。

// 定义 Worker 类
public class MyWorker extends Worker {
    public MyWorker(@NonNull Context context, @NonNull WorkerParameters params) {
        super(context, params);
    }
    
    @NonNull
    @Override
    public Result doWork() {
        // 执行后台任务
        try {
            // 模拟耗时操作
            Thread.sleep(5000);
            
            // 返回成功结果
            return Result.success();
        } catch (InterruptedException e) {
            e.printStackTrace();
            // 返回失败结果
            return Result.failure();
        }
    }
}
// 定义 ViewModel
public class MyViewModel extends ViewModel {
    private WorkManager mWorkManager;
    private LiveData<WorkInfo> mWorkInfo;
    
    public MyViewModel() {
        mWorkManager = WorkManager.getInstance(ApplicationProvider.getApplicationContext());
    }
    
    public void startWork() {
        // 创建一次性工作请求
        OneTimeWorkRequest workRequest = new OneTimeWorkRequest.Builder(MyWorker.class)
                .build();
        
        // 启动工作
        mWorkManager.enqueue(workRequest);
        
        // 观察工作状态
        mWorkInfo = mWorkManager.getWorkInfoByIdLiveData(workRequest.getId());
    }
    
    public LiveData<WorkInfo> getWorkInfo() {
        return mWorkInfo;
    }
}
// 在 Activity 中使用
public class MainActivity extends AppCompatActivity {
    private MyViewModel mViewModel;
    
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        
        mViewModel = new ViewModelProvider(this).get(MyViewModel.class);
        
        // 观察工作状态
        mViewModel.getWorkInfo().observe(this, workInfo -> {
            if (workInfo != null) {
                WorkInfo.State state = workInfo.getState();
                // 更新 UI 显示工作状态
                updateWorkStatus(state);
            }
        });
        
        // 启动工作按钮点击事件
        findViewById(R.id.start_work_button).setOnClickListener(v -> {
            mViewModel.startWork();
        });
    }
    
    private void updateWorkStatus(WorkInfo.State state) {
        // 更新工作状态 UI
    }
}

9.4 与 Hilt 依赖注入结合

ViewModel 可以与 Hilt 依赖注入框架结合,实现依赖的自动注入。

首先,在项目中添加 Hilt 依赖:

// 项目级 build.gradle
buildscript {
    ext.hilt_version = '2.44'
    dependencies {
        classpath "com.google.dagger:hilt-android-gradle-plugin:$hilt_version"
    }
}

// 应用级 build.gradle
plugins {
    id 'com.google.dagger.hilt.android'
}

android {
    // 其他配置...
}

dependencies {
    implementation "com.google.dagger:hilt-android:$hilt_version"
    kapt "com.google.dagger:hilt-android-compiler:$hilt_version"
}

创建应用类并添加 @HiltAndroidApp 注解:

import dagger.hilt.android.HiltAndroidApp;
import android.app.Application;

@HiltAndroidApp
public class MyApplication extends Application {
    // 应用类代码
}

创建需要注入的 Repository:

import javax.inject.Inject;

public class DataRepository {
    @Inject
    public DataRepository() {
        // 初始化 Repository
    }
    
    public LiveData<String> fetchData() {
        // 获取数据的方法
        MutableLiveData<String> data = new MutableLiveData<>();
        // 模拟数据加载
        data.setValue("从 Repository 获取的数据");
        return data;
    }
}

创建 ViewModel 并使用 @HiltViewModel 和 @Inject 注解:

import androidx.lifecycle.ViewModel;
import androidx.lifecycle.LiveData;
import androidx.lifecycle.MutableLiveData;
import dagger.hilt.android.lifecycle.HiltViewModel;
import javax.inject.Inject;

@HiltViewModel
public class MyViewModel extends ViewModel {
    private DataRepository repository;
    private MutableLiveData<String> data;
    
    @Inject
    public MyViewModel(DataRepository repository) {
        this.repository = repository;
        data = new MutableLiveData<>();
        loadData();
    }
    
    private void loadData() {
        // 从 Repository 获取数据
        repository.fetchData().observeForever(new Observer<String>() {
            @Override
            public void onChanged(String s) {
                data.setValue(s);
            }
        });
    }
    
    public LiveData<String> getData() {
        return data;
    }
}

在 Activity 中使用 Hilt 注入 ViewModel:

import androidx.appcompat.app.AppCompatActivity;
import android.os.Bundle;
import androidx.lifecycle.ViewModelProvider;
import javax.inject.Inject;
import dagger.hilt.android.AndroidEntryPoint;

@AndroidEntryPoint
public class MainActivity extends AppCompatActivity {
    private MyViewModel viewModel;
    
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        
        // 使用 Hilt 提供的 ViewModel 工厂
        viewModel = new ViewModelProvider(this).get(MyViewModel.class);
        
        // 观察数据变化
        viewModel.getData().observe(this, new Observer<String>() {
            @Override
            public void onChanged(String s) {
                // 更新 UI
            }
        });
    }
}

9.5 与 Kotlin Flow 结合

ViewModel 可以与 Kotlin Flow 结合,处理异步数据流。

首先,确保项目中添加了 Kotlin Flow 依赖:

implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:1.6.4"
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:1.6.4"

创建 ViewModel 并使用 Flow:

import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.flow
import kotlinx.coroutines.launch

class MyViewModel : ViewModel() {
    // 使用 StateFlow 作为数据流
    private val _uiState = MutableStateFlow<UiState>(UiState.Loading)
    val uiState: StateFlow<UiState> = _uiState
    
    // 定义 UI 状态的密封类
    sealed class UiState {
        object Loading : UiState()
        data class Success(val data: String) : UiState()
        data class Error(val message: String) : UiState()
    }
    
    init {
        // 初始化时加载数据
        loadData()
    }
    
    // 使用 Flow 模拟数据加载
    private fun loadData() {
        viewModelScope.launch {
            try {
                // 模拟耗时操作
                delay(2000)
                
                // 获取数据
                val data = fetchDataFromNetwork()
                
                // 更新 UI 状态为成功
                _uiState.value = UiState.Success(data)
            } catch (e: Exception) {
                // 更新 UI 状态为错误
                _uiState.value = UiState.Error(e.message ?: "未知错误")
            }
        }
    }
    
    // 模拟网络请求的挂起函数
    private suspend fun fetchDataFromNetwork(): String {
        // 实际应用中这里会进行网络请求
        return "从网络获取的数据"
    }
    
    // 提供刷新数据的方法
    fun refreshData() {
        loadData()
    }
}

在 Activity 中使用:

import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import androidx.lifecycle.ViewModelProvider
import kotlinx.coroutines.flow.collect
import kotlinx.coroutines.launch
import androidx.lifecycle.lifecycleScope

class MainActivity : AppCompatActivity() {
    private lateinit var viewModel: MyViewModel
    
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        
        // 获取 ViewModel 实例
        viewModel = ViewModelProvider(this).get(MyViewModel::class.java)
        
        // 观察 UI 状态变化
        lifecycleScope.launch {
            viewModel.uiState.collect { state ->
                when (state) {
                    is MyViewModel.UiState.Loading -> showLoading()
                    is MyViewModel.UiState.Success -> showData(state.data)
                    is MyViewModel.UiState.Error -> showError(state.message)
                }
            }
        }
        
        // 设置刷新按钮点击事件
        findViewById<Button>(R.id.refresh_button).setOnClickListener {
            viewModel.refreshData()
        }
    }
    
    private fun showLoading() {
        // 显示加载状态
    }
    
    private fun showData(data: String) {
        // 显示数据
    }
    
    private fun showError(message: String) {
        // 显示错误信息
    }
}

9.6 与 Paging 3 库结合

ViewModel 可以与 Paging 3 库结合,实现大数据集的分页加载。

首先,添加 Paging 3 依赖:

implementation "androidx.paging:paging-runtime:3.1.1"
// 如果使用 Kotlin Flow
implementation "androidx.paging:paging-runtime-ktx:3.1.1"

创建数据模型和数据源:

// 数据模型
data class Item(val id: Int, val name: String, val description: String)

// 数据源
class ItemDataSource(private val apiService: ApiService) : PagingSource<Int, Item>() {
    override suspend fun load(params: LoadParams<Int>): LoadResult<Int, Item> {
        return try {
            // 获取当前页码,默认为第一页
            val page = params.key ?: 1
            
            // 加载数据
            val response = apiService.getItems(page, params.loadSize)
            
            // 计算前一页和下一页的页码
            val prevKey = if (page == 1) null else page - 1
            val nextKey = if (response.isEmpty()) null else page + 1
            
            // 返回加载结果
            LoadResult.Page(
                data = response,
                prevKey = prevKey,
                nextKey = nextKey
            )
        } catch (e: Exception) {
            // 处理错误
            LoadResult.Error(e)
        }
    }
    
    // 用于重置 PagingSource 的方法
    override fun getRefreshKey(state: PagingState<Int, Item>): Int? {
        return state.anchorPosition?.let { anchorPosition ->
            val anchorPage = state.closestPageToPosition(anchorPosition)
            anchorPage?.prevKey?.plus(1) ?: anchorPage?.nextKey?.minus(1)
        }
    }
}

// 数据仓库
class ItemRepository(private val apiService: ApiService) {
    fun getItems(): Flow<PagingData<Item>> {
        return Pager(
            config = PagingConfig(
                pageSize = 20,       // 每页加载的数量
                enablePlaceholders = true,  // 是否启用占位符
                prefetchDistance = 5  // 距离最后一项多远时预加载下一页
            ),
            pagingSourceFactory = { ItemDataSource(apiService) }
        ).flow
    }
}

创建 ViewModel:

import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import androidx.paging.PagingData
import androidx.paging.cachedIn
import kotlinx.coroutines.flow.Flow

class ItemViewModel(private val repository: ItemRepository) : ViewModel() {
    // 获取分页数据流
    val items: Flow<PagingData<Item>> = repository.getItems()
        .cachedIn(viewModelScope)  // 缓存数据流,避免配置更改时重新加载
}

在 Activity 中使用:

import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import androidx.lifecycle.ViewModelProvider
import androidx.paging.ExperimentalPagingApi
import androidx.paging.LoadState
import androidx.recyclerview.widget.LinearLayoutManager
import kotlinx.coroutines.flow.collectLatest
import kotlinx.coroutines.launch

class MainActivity : AppCompatActivity() {
    private lateinit var viewModel: ItemViewModel
    private lateinit var adapter: ItemAdapter
    
    @OptIn(ExperimentalPagingApi::class)
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        
        // 初始化 RecyclerView 和 Adapter
        val recyclerView = findViewById<RecyclerView>(R.id.recyclerView)
        recyclerView.layoutManager = LinearLayoutManager(this)
        adapter = ItemAdapter()
        recyclerView.adapter = adapter.withLoadStateFooter(
            footer = ItemLoadStateAdapter { adapter.retry() }
        )
        
        // 获取 ViewModel
        viewModel = ViewModelProvider(this).get(ItemViewModel::class.java)
        
        // 观察分页数据
        lifecycleScope.launch {
            viewModel.items.collectLatest { pagingData ->
                adapter.submitData(pagingData)
            }
        }
        
        // 监听加载状态
        lifecycleScope.launch {
            adapter.loadStateFlow.collect { loadState ->
                // 处理加载状态,如显示加载动画、错误信息等
                when (loadState.refresh) {
                    is LoadState.Loading -> showLoading()
                    is LoadState.NotLoading -> hideLoading()
                    is LoadState.Error -> showError((loadState.refresh as LoadState.Error).error)
                }
            }
        }
    }
    
    private fun showLoading() {
        // 显示加载状态
    }
    
    private fun hideLoading() {
        // 隐藏加载状态
    }
    
    private fun showError(error: Throwable) {
        // 显示错误信息
    }
}

创建 Adapter:

import android.view.LayoutInflater
import android.view.ViewGroup
import androidx.paging.PagingDataAdapter
import androidx.recyclerview.widget.DiffUtil
import androidx.recyclerview.widget.RecyclerView
import com.example.databindingdemo.databinding.ItemLayoutBinding

class ItemAdapter : PagingDataAdapter<Item, ItemAdapter.ItemViewHolder>(ITEM_COMPARATOR) {
    companion object {
        private val ITEM_COMPARATOR = object : DiffUtil.ItemCallback<Item>() {
            override fun areItemsTheSame(oldItem: Item, newItem: Item): Boolean {
                return oldItem.id == newItem.id
            }
            
            override fun areContentsTheSame(oldItem: Item, newItem: Item): Boolean {
                return oldItem == newItem
            }
        }
    }
    
    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ItemViewHolder {
        val binding = ItemLayoutBinding.inflate(
            LayoutInflater.from(parent.context),
            parent,
            false
        )
        return ItemViewHolder(binding)
    }
    
    override fun onBindViewHolder(holder: ItemViewHolder, position: Int) {
        val item = getItem(position)
        if (item != null) {
            holder.bind(item)
        }
    }
    
    class ItemViewHolder(private val binding: ItemLayoutBinding) :
        RecyclerView.ViewHolder(binding.root) {
        
        fun bind(item: Item) {
            binding.apply {
                tvName.text = item.name
                tvDescription.text = item.description
                executePendingBindings()
            }
        }
    }
}

// 加载状态适配器
class ItemLoadStateAdapter(private val retry: () -> Unit) :
    LoadStateAdapter<ItemLoadStateAdapter.LoadStateViewHolder>() {
    
    override fun onCreateViewHolder(parent: ViewGroup, loadState: LoadState): LoadStateViewHolder {
        val binding = ItemLoadStateBinding.inflate(
            LayoutInflater.from(parent.context),
            parent,
            false
        )
        return LoadStateViewHolder(binding)
    }
    
    override fun onBindViewHolder(holder: LoadStateViewHolder, loadState: LoadState) {
        holder.bind(loadState)
    }
    
    inner class LoadStateViewHolder(private val binding: ItemLoadStateBinding) :
        RecyclerView.ViewHolder(binding.root) {
        
        init {
            binding.btnRetry.setOnClickListener { retry.invoke() }
        }
        
        fun bind(loadState: LoadState) {
            binding.apply {
                progressBar.isVisible = loadState is LoadState.Loading
                btnRetry.isVisible = loadState is LoadState.Error
                tvError.isVisible = loadState is LoadState.Error
                
                if (loadState is LoadState.Error) {
                    tvError.text = loadState.error.localizedMessage
                }
            }
        }
    }
}

9.7 与 RxJava 结合

ViewModel 可以与 RxJava 结合,处理异步数据流。

首先,添加 RxJava 依赖:

implementation 'io.reactivex.rxjava3:rxjava:3.1.5'
implementation 'io.reactivex.rxjava3:rxandroid:3.0.0'

创建 ViewModel:

import androidx.lifecycle.ViewModel;
import androidx.lifecycle.LiveData;
import androidx.lifecycle.MutableLiveData;
import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers;
import io.reactivex.rxjava3.core.Observable;
import io.reactivex.rxjava3.disposables.CompositeDisposable;
import io.reactivex.rxjava3.schedulers.Schedulers;
import java.util.concurrent.TimeUnit;

public class MyViewModel extends ViewModel {
    private MutableLiveData<String> mData = new MutableLiveData<>();
    private MutableLiveData<Boolean> mLoading = new MutableLiveData<>(false);
    private MutableLiveData<String> mError = new MutableLiveData<>();
    
    private CompositeDisposable mDisposables = new CompositeDisposable();
    
    public LiveData<String> getData() {
        return mData;
    }
    
    public LiveData<Boolean> getLoading() {
        return mLoading;
    }
    
    public LiveData<String> getError() {
        return mError;
    }
    
    public void fetchData() {
        mLoading.setValue(true);
        
        // 创建一个 Observable,模拟网络请求
        Observable.just("RxJava Data")
                .delay(2, TimeUnit.SECONDS)  // 模拟网络延迟
                .subscribeOn(Schedulers.io())
                .observeOn(AndroidSchedulers.mainThread())
                .subscribe(
                        data -> {
                            mLoading.setValue(false);
                            mData.setValue(data);
                        },
                        error -> {
                            mLoading.setValue(false);
                            mError.setValue(error.getMessage());
                        }
                )
                .dispose();
    }
    
    @Override
    protected void onCleared() {
        super.onCleared();
        // 清理所有订阅,防止内存泄漏
        mDisposables.clear();
    }
}

在 Activity 中使用:

import androidx.appcompat.app.AppCompatActivity;
import android.os.Bundle;
import androidx.lifecycle.ViewModelProvider;
import android.widget.TextView;
import android.widget.Button;
import android.widget.ProgressBar;

public class MainActivity extends AppCompatActivity {
    private MyViewModel mViewModel;
    private TextView mTextView;
    private ProgressBar mProgressBar;
    private Button mButton;
    
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        
        mTextView = findViewById(R.id.textView);
        mProgressBar = findViewById(R.id.progressBar);
        mButton = findViewById(R.id.button);
        
        mViewModel = new ViewModelProvider(this).get(MyViewModel.class);
        
        // 观察数据变化
        mViewModel.getData().observe(this, data -> {
            mTextView.setText(data);
        });
        
        // 观察加载状态
        mViewModel.getLoading().observe(this, isLoading -> {
            if (isLoading) {
                mProgressBar.setVisibility(ProgressBar.VISIBLE);
            } else {
                mProgressBar.setVisibility(ProgressBar.GONE);
            }
        });
        
        // 观察错误信息
        mViewModel.getError().observe(this, error -> {
            mTextView.setText(error);
        });
        
        // 设置按钮点击事件
        mButton.setOnClickListener(v -> {
            mViewModel.fetchData();
        });
    }
}

9.8 与 Koin 依赖注入结合

ViewModel 可以与 Koin 依赖注入框架结合,实现依赖的自动注入。

首先,添加 Koin 依赖:

implementation "io.insert-koin:koin-android:3.3.3"
implementation "io.insert-koin:koin-androidx-viewmodel:3.3.3"

创建 Koin 模块:

import org.koin.androidx.viewmodel.dsl.viewModel
import org.koin.dsl.module

val appModule = module {
    // 提供 Repository 实例
    single { DataRepository(get()) }
    
    // 提供 ViewModel 实例
    viewModel { MyViewModel(get()) }
}

初始化 Koin:

import android.app.Application
import org.koin.android.ext.koin.androidContext
import org.koin.android.ext.koin.androidLogger
import org.koin.core.context.startKoin

class MyApplication : Application() {
    override fun onCreate() {
        super.onCreate()
        
        // 启动 Koin
        startKoin {
            androidLogger()
            androidContext(this@MyApplication)
            modules(appModule)
        }
    }
}

创建 Repository:

class DataRepository(private val apiService: ApiService) {
    suspend fun fetchData(): String {
        // 模拟网络请求
        delay(2000)
        return "从网络获取的数据"
    }
}

创建 ViewModel:

import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import kotlinx.coroutines.launch

class MyViewModel(private val repository: DataRepository) : ViewModel() {
    private val _data = MutableLiveData<String>()
    val data: LiveData<String> = _data
    
    init {
        // 初始化时加载数据
        loadData()
    }
    
    private fun loadData() {
        viewModelScope.launch {
            try {
                // 获取数据
                val result = repository.fetchData()
                // 更新数据
                _data.value = result
            } catch (e: Exception) {
                // 处理错误
                e.printStackTrace()
            }
        }
    }
}

在 Activity 中使用:

import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import androidx.lifecycle.lifecycleScope
import org.koin.androidx.viewmodel.ext.android.viewModel

class MainActivity : AppCompatActivity() {
    // 使用 Koin 获取 ViewModel
    private val viewModel: MyViewModel by viewModel()
    
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        
        // 观察数据变化
        viewModel.data.observe(this) { data ->
            // 更新 UI
            findViewById<TextView>(R.id.textView).text = data
        }
    }
}

9.9 与 Compose 结合

ViewModel 可以与 Jetpack Compose 结合,为 Compose UI 提供数据和状态管理。

首先,添加 Compose 依赖:

implementation "androidx.compose.ui:ui:1.4.3"
implementation "androidx.compose.material:material:1.4.3"
implementation "androidx.compose.ui:ui-tooling-preview:1.4.3"
implementation "androidx.lifecycle:lifecycle-viewmodel-compose:2.6.1"

创建 ViewModel:

import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.launch

class ComposeViewModel : ViewModel() {
    private val _uiState = MutableStateFlow(UiState())
    val uiState: StateFlow<UiState> = _uiState
    
    data class UiState(
        val isLoading: Boolean = false,
        val data: String = "",
        val error: String = ""
    )
    
    fun fetchData() {
        viewModelScope.launch {
            // 更新状态为加载中
            _uiState.value = uiState.value.copy(isLoading = true)
            
            try {
                // 模拟网络请求
                delay(2000)
                
                // 更新状态为成功
                _uiState.value = uiState.value.copy(
                    isLoading = false,
                    data = "从网络获取的数据",
                    error = ""
                )
            } catch (e: Exception) {
                // 更新状态为错误
                _uiState.value = uiState.value.copy(
                    isLoading = false,
                    error = e.message ?: "未知错误"
                )
            }
        }
    }
}

创建 Compose UI:

import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.material.Button
import androidx.compose.material.CircularProgressIndicator
import androidx.compose.material.MaterialTheme
import androidx.compose.material.Scaffold
import androidx.compose.material.Text
import androidx.compose.material.TopAppBar
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import androidx.lifecycle.compose.collectAsStateWithLifecycle

@Composable
fun MyScreen(viewModel: ComposeViewModel = androidx.lifecycle.viewmodel.compose.viewModel()) {
    val uiState by viewModel.uiState.collectAsStateWithLifecycle()
    
    Scaffold(
        topBar = {
            TopAppBar(
                title = { Text("Compose + ViewModel") }
            )
        }
    ) { paddingValues ->
        Column(
            modifier = Modifier
                .fillMaxSize()
                .padding(paddingValues)
                .padding(16.dp),
            horizontalAlignment = Alignment.CenterHorizontally
        ) {
            if (uiState.isLoading) {
                CircularProgressIndicator()
            } else if (uiState.error.isNotEmpty()) {
                Text(
                    text = uiState.error,
                    color = MaterialTheme.colors.error,
                    textAlign = TextAlign.Center
                )
            } else if (uiState.data.isNotEmpty()) {
                Text(
                    text = uiState.data,
                    style = MaterialTheme.typography.body1,
                    textAlign = TextAlign.Center
                )
            }
            
            Spacer(modifier = Modifier.height(16.dp))
            
            Button(
                onClick = { viewModel.fetchData() },
                enabled = !uiState.isLoading
            ) {
                Text("加载数据")
            }
        }
    }
}

@Preview(showBackground = true)
@Composable
fun DefaultPreview() {
    MyScreen()
}

在 Activity 中设置 Compose 内容:

import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.compose.runtime.getValue
import androidx.compose.ui.platform.LocalContext
import androidx.lifecycle.viewmodel.compose.viewModel
import android.os.Bundle

class ComposeActivity : ComponentActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        
        setContent {
            MyScreen()
        }
    }
}

9.10 与 MultiViewModel 结合

在某些复杂场景中,可能需要在同一个 Activity 或 Fragment 中使用多个 ViewModel。

创建多个 ViewModel:

// 第一个 ViewModel
public class UserViewModel extends ViewModel {
    private MutableLiveData<User> mUser = new MutableLiveData<>();
    
    public LiveData<User> getUser() {
        return mUser;
    }
    
    public void loadUser(String userId) {
        // 加载用户数据
        // ...
        mUser.setValue(new User(userId, "用户名", "用户邮箱"));
    }
}

// 第二个 ViewModel
public class OrderViewModel extends ViewModel {
    private MutableLiveData<List<Order>> mOrders = new MutableLiveData<>();
    
    public LiveData<List<Order>> getOrders() {
        return mOrders;
    }
    
    public void loadOrders(String userId) {
        // 加载订单数据
        // ...
        List<Order> orders = new ArrayList<>();
        orders.add(new Order("1", userId, "订单1"));
        orders.add(new Order("2", userId, "订单2"));
        mOrders.setValue(orders);
    }
}

在 Activity 中使用多个 ViewModel:

public class MainActivity extends AppCompatActivity {
    private UserViewModel mUserViewModel;
    private OrderViewModel mOrderViewModel;
    
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        
        // 获取 UserViewModel
        mUserViewModel = new ViewModelProvider(this).get(UserViewModel.class);
        
        // 获取 OrderViewModel
        mOrderViewModel = new ViewModelProvider(this).get(OrderViewModel.class);
        
        // 观察用户数据
        mUserViewModel.getUser().observe(this, user -> {
            // 更新用户信息 UI
            updateUserInfo(user);
            
            // 用户数据加载完成后,加载订单数据
            mOrderViewModel.loadOrders(user.getId());
        });
        
        // 观察订单数据
        mOrderViewModel.getOrders().observe(this, orders -> {
            // 更新订单列表 UI
            updateOrderList(orders);
        });
        
        // 加载用户数据
        mUserViewModel.loadUser("123");
    }
    
    private void updateUserInfo(User user) {
        // 更新用户信息 UI
    }
    
    private void updateOrderList(List<Order> orders) {
        // 更新订单列表 UI
    }
}

在布局文件中可以根据需要绑定不同 ViewModel 的数据:

<layout xmlns:android="http://schemas.android.com/apk/res/android">
    <data>
        <variable
            name="userViewModel"
            type="com.example.UserViewModel" />
        <variable
            name="orderViewModel"
            type="com.example.OrderViewModel" />
    </data>
    
    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical">
        
        <!-- 用户信息部分 -->
        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="@{userViewModel.user.name}" />
            
        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="@{userViewModel.user.email}" />
            
        <!-- 订单列表部分 -->
        <RecyclerView
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            app:items="@{orderViewModel.orders}" />
    </LinearLayout>
</layout>

十、性能优化与最佳实践

10.1 避免过度使用 DataBinding

虽然 DataBinding 可以简化代码,但过度使用可能会导致性能问题和代码难以维护。以下是一些建议:

  1. 避免复杂表达式:布局文件中的表达式应该尽量简单,复杂的逻辑应该移到 ViewModel 或 BindingAdapter 中。

  2. 限制双向绑定的使用:双向绑定虽然方便,但可能会导致性能问题和数据流向混乱,应谨慎使用。

  3. 避免在循环中使用复杂绑定:在 RecyclerView 的 item 布局中,避免使用复杂的绑定表达式,以免影响滚动性能。

10.2 优化 LiveData 更新频率

LiveData 的更新可能会触发 UI 重绘,过于频繁的更新会影响性能。以下是一些优化建议:

  1. 合并频繁更新:如果有多个相关的数据需要更新,可以考虑合并这些更新,减少 UI 重绘次数。

  2. 使用 distinctUntilChanged():对于 Flow 或 LiveData,可以使用 distinctUntilChanged() 过滤重复数据,避免不必要的 UI 更新。

  3. 避免在循环中更新 LiveData:在循环中更新 LiveData 会导致频繁的 UI 更新,应尽量避免。

10.3 合理管理 ViewModel 生命周期

ViewModel 的生命周期应该与对应的 UI 组件相匹配,避免不必要的内存占用。以下是一些建议:

  1. 及时清理资源:在 ViewModel 的 onCleared() 方法中清理资源,如取消网络请求、关闭数据库连接等。

  2. 避免持有大对象:ViewModel 中应避免持有大对象,如 Activity、Context 或大型数据集合,以免造成内存泄漏。

  3. 使用适当的作用域:根据需要选择合适的 ViewModel 作用域,如 Activity 级别或 Fragment 级别。

10.4 优化 BindingAdapter

BindingAdapter 可以提高代码的复用性,但不当使用可能会影响性能。以下是一些优化建议:

  1. 避免重复创建对象:在 BindingAdapter 中避免重复创建对象,特别是在循环中使用的 BindingAdapter。

  2. 使用 @JvmStatic 注解:对于 Java 中的 BindingAdapter 方法,使用 @JvmStatic 注解可以避免创建额外的对象。

  3. 检查参数变化:在 BindingAdapter 中检查参数是否变化,只有在参数变化时才执行更新操作。

@BindingAdapter("imageUrl")
public static void loadImage(ImageView view, String url) {
    // 检查 URL 是否变化,避免不必要的加载
    if (view.getTag(R.id.image_url_tag) == null || !view.getTag(R.id.image_url_tag).equals(url)) {
        view.setTag(R.id.image_url_tag, url);
        Glide.with(view.getContext())
                .load(url)
                .into(view);
    }
}

10.5 优化布局文件

布局文件的结构和复杂度会影响 DataBinding 的性能。以下是一些优化建议:

  1. 减少布局嵌套:过深的布局嵌套会增加视图渲染的时间,应尽量扁平化布局结构。