揭秘ViewModel怎样在屏幕旋转时保存数据

本文深入解析Android中的ViewModel,阐述其在MVVM架构中的作用,特别是如何结合LiveData实现视图和逻辑的解耦。ViewModel的主要职责是管理界面数据,确保在配置变化(如屏幕旋转)时数据不丢失。文章详细介绍了ViewModel的创建、使用过程,以及Activity如何通过ViewModelStore保存和恢复ViewModel,确保数据持久性。此外,还探讨了onSaveInstanceState方法和ViewModelProvider的内部机制。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

ViewModel的作用及怎样使用

ViewModel使用在MVVM框架中,结合LiveData实现View层和控制层解耦。ViewModel类旨在以注重生命周期的方式存储和管理界面相关的数据,ViewModel作为一个类保存着LiveData类型的数据,这样做的好处在于当Activity因为配置的改变(如屏幕旋转)而重新创建时,界面中所持有的数据能够不被销毁。  对于简单的数据,Activity可以从onCreate()中利用Bundle对象恢复数据,但此方法仅适合可以序列化再反序列化的少量数据, 而不适合数量可能较大的数据,如列表或位图。

先回顾一下onSaveInstanceState方法怎样使用的:

Android中的activity通过onSaveInstanceState()方法保存activity的状态,在onCreate或者onRestoreInstanceState方法恢复状态。

public class SaveStateActivityTest extends Activity {
	public static final String TAG = "SaveStateActivity";
 
    @Override
    public void onCreate(Bundle savedInstanceState) {
    	if (savedInstanceState!=null) {
    		Log.v(TAG, savedInstanceState.getString("data"));
		}
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
        Log.v(TAG, "onCreate");
    }
    
	@Override
	protected void onRestoreInstanceState(Bundle savedInstanceState) {
		Log.v(TAG, savedInstanceState.getString("data"));
		super.onRestoreInstanceState(savedInstanceState);
		Log.v(TAG, "onRestoreInstanceState");
	}
	
	@Override
	protected void onSaveInstanceState(Bundle outState) {
		outState.putString("data", "mzq");
		super.onSaveInstanceState(outState);
		Log.v(TAG, "onSaveInstanceState");
	}
}

ViewModel是怎样使用的

架构组件为界面控制器提供了 ViewModel 辅助程序类,该类负责为界面准备数据。在配置更改期间会自动保留 ViewModel 对象,以便它们存储的数据立即可供下一个 activity 或 fragment 实例使用。例如,如果您需要在应用中显示用户列表,请确保将获取和保留该用户列表的责任分配给 ViewModel,而不是 activity 或 fragment。 

class MyViewModel : ViewModel() {
    private val users: MutableLiveData<List<User>> by lazy {
        MutableLiveData<List<User>>().also {
            loadUsers()
        }
    }

    fun getUsers(): LiveData<List<User>> {
        return users
    }

    private fun loadUsers() {
        // Do an asynchronous operation to fetch users.
    }
}

 然后,您可以从 Activity 访问该列表,如下所示:

class MyActivity : AppCompatActivity() {

    override fun onCreate(savedInstanceState: Bundle?) {
        // Create a ViewModel the first time the system calls an activity's onCreate() method.
        // Re-created activities receive the same MyViewModel instance created by the first activity.

        // Use the 'by viewModels()' Kotlin property delegate
        // from the activity-ktx artifact
        val model: MyViewModel by viewModels()
        model.getUsers().observe(this, Observer<List<User>>{ users ->
            // update UI
        })
    }
}

进入主题 

我们常用的创建ViewModel的方法: 

SearchViewModel viewModel = new ViewModelProvider(this).get(MzqViewModel.class);

我们看一下ViewModelProvider类的构造方法:

public ViewModelProvider(@NonNull ViewModelStoreOwner owner) {
        this(owner.getViewModelStore(), owner instanceof HasDefaultViewModelProviderFactory
                ? ((HasDefaultViewModelProviderFactory)owner).getDefaultViewModelProviderFactory()
                : NewInstanceFactory.getInstance());
}

我们看一下ViewModelStoreOwner是何方神圣:

它是Android.lifecycle包下的一个接口:

package androidx.lifecycle;

import androidx.annotation.NonNull;

/**
 * A scope that owns {@link ViewModelStore}.
 * <p>
 * A responsibility of an implementation of this interface is to retain owned ViewModelStore
 * during the configuration changes and call {@link ViewModelStore#clear()}, when this scope is
 * going to be destroyed.
 *
 * @see ViewTreeViewModelStoreOwner
 */
@SuppressWarnings("WeakerAccess")
public interface ViewModelStoreOwner {
    /**
     * Returns owned {@link ViewModelStore}
     *
     * @return a {@code ViewModelStore}
     */
    @NonNull
    ViewModelStore getViewModelStore();
}

这个接口只有一个方法,getViewModelStore()方法,该方法返回ViewModelStore对象,从命名上可得知该对象上用于存储ViewModel的,一个Activity可能关联多个ViewModel,因此ViewModelStore应该持有集合的数据结构,我们继续验证一下:

public class ViewModelStore {

    private final HashMap<String, ViewModel> mMap = new HashMap<>();

    final void put(String key, ViewModel viewModel) {
        ViewModel oldViewModel = mMap.put(key, viewModel);
        if (oldViewModel != null) {
            oldViewModel.onCleared();
        }
    }

    final ViewModel get(String key) {
        return mMap.get(key);
    }

    Set<String> keys() {
        return new HashSet<>(mMap.keySet());
    }

    /**
     *  Clears internal storage and notifies ViewModels that they are no longer used.
     */
    public final void clear() {
        for (ViewModel vm : mMap.values()) {
            vm.clear();
        }
        mMap.clear();
    }
}

 果真如我们的猜想,它内部是一个HashMap的键值对集合,值即为我们的ViewModel对象。

Activity实现了ViewModelStoreOwner,并且重写了getVeiwModelStore()的方法。我们来看一下具体实现。

我们验证一下Activity实现了ViewModelStoreOwner:

public class AppCompatActivity extends FragmentActivity ....
public class FragmentActivity extends ComponentActivity ...
public class ComponentActivity extends androidx.core.app.ComponentActivity implements
        ContextAware,
        LifecycleOwner,
        ViewModelStoreOwner,
        HasDefaultViewModelProviderFactory,
        SavedStateRegistryOwner,
        OnBackPressedDispatcherOwner,
        ActivityResultRegistryOwner,
        ActivityResultCaller 

可以看到ComponentActivity类实现了ViewModelStoreOwner接口。

我们继续查看一下ComponentActivity是怎样实现getViewModelStore方法的:

 @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.");
        }
        ensureViewModelStore();
        return mViewModelStore;  //是ViewModelStore类型的对象
    }

    @SuppressWarnings("WeakerAccess") /* synthetic access */
    void ensureViewModelStore() {
        if (mViewModelStore == null) {
            NonConfigurationInstances nc =
                    (NonConfigurationInstances) getLastNonConfigurationInstance();
            if (nc != null) {
                // Restore the ViewModelStore from NonConfigurationInstances
                mViewModelStore = nc.viewModelStore;
            }
            if (mViewModelStore == null) {
                mViewModelStore = new ViewModelStore();
            }
        }
    }

这个方法当本地变量mViewModelStore为空时,会通过getLastNonConfigurationInstance()方法尝试获得ViewModeStore,如果没有的活就重新创建。而ViewModel不被销毁的玄机就在这里。

static final class NonConfigurationInstances {
    Object custom;
    ViewModelStore viewModelStore;
}

Activity类中: 

 public Object getLastNonConfigurationInstance() {
    return mLastNonConfigurationInstances != null
             ? mLastNonConfigurationInstances.activity : null;
 }

NonConfigurationInstaces是个很简单的类,包含了ViweModelStore,而getLastNonConfigurationInstace会尝试获取上次配置改变被保存的NonConfigurationInstaces对象

那么在哪里对NonConfigurationInstances中的ViewModel进行赋值存储呢?

public final Object onRetainNonConfigurationInstance() {
        // Maintain backward compatibility.
        Object custom = onRetainCustomNonConfigurationInstance();

        ViewModelStore viewModelStore = mViewModelStore;
        if (viewModelStore == null) {
            // No one called getViewModelStore(), so see if there was an existing
            // ViewModelStore from our last NonConfigurationInstance
            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;
    }

viewModeStore不为null或者lastNonConfigurationInstacne不为null时,我们就把viewModelStore保存在NonConfigurationInstance对象中。当重新创建时,我们就能通过getLastNonConfigurationInstance()方法得到我们保存到的ViewModelStore了,而ViewModelStore里面就有我们的ViewMode。

总结


当Activity要销毁前回调onRetainNonConfigurationInstance()方法保存viewModelStore在lastNonConfigurationInstacne.activity(NonConfigurationInstance)中,然后在getViewModelStore方法中又会首先去获取lastNonConfigurationInstace.activity(NonConfigurationInstance)对象。

<think>首先,用户的问题是:“我想在Android的MVVM架构中确定RecyclerView数据的存储位置 请问Android MVVM架构中RecyclerView的数据应该保存在Activity还是ViewModel” 我需要根据系统级指令来构建回答。指令包括: - 所有行内数学表达式使用$...$格式。 - 独立公式使用$$...$$格式并单独成段。 - 使用中文回答。 - 生成相关问题。 - 回答中引用的段落末尾自然地添加引用标识。 用户提供了一些引用,这些引用是关于MVVM架构的,特别是RecyclerView在Android中的应用。引用包括: - [^1]: MVVM模式的基本描述。 - [^2]: MVVM的局限性。 - [^3]: MVVM在Android中的优势。 - [^4]: MVVM实现RecyclerView列表的例子。 - [^5]: BindingViewHolder类的代码。 这些引用是站内引用,我需要自然地引用它们。 用户的核心问题是:在Android MVVM架构中,RecyclerView的数据应该保存在Activity还是ViewModel? 根据MVVM架构的原则: - **Model**: 负责数据和业务逻辑。 - **View**: 负责UI显示,包括Activity和Fragment。 - **ViewModel**: 负责管理UI相关的数据,并处理View和Model之间的交互。 在MVVM中,数据应该保存ViewModel中,而不是Activity。这是因为ViewModel是设计来保存UI状态的数据,即使UI被销毁(如屏幕旋转),ViewModel中的数据也不会丢失。Activity是View的一部分,应该只负责UI操作。 具体到RecyclerView: - RecyclerView的数据通常是一个列表,比如从网络或数据库获取的数据。 - 这个数据应该由ViewModel持有和管理。ViewModel可以暴露LiveData或Observable字段,让View(Activity/Fragment)观察数据变化。 - 当数据更新ViewModel通知View,View更新RecyclerView的适配器。 引用[4]提到:“Android——MVVM架构实现RecyclerView列表”,并描述了建立VM层和数据与视图交互。这表明ViewModel是存储数据的地方。 引用[3]说:“ViewModel使得UI发生重建无需重新向后台请求数据”,这强调了ViewModel保存数据的重要性。 所以,回答应该是:数据应该保存ViewModel中。 现在,构建回答结构: 1. **清晰的结构**:逐步解释,帮助用户理解。 2. **引用相关段落**:自然地添加引用标识。 3. **生成相关问题**:在最后添加相关问题。 4. **使用中文**:整个回答用中文。 回答大纲: - 介绍MVVM架构的基本原则。 - 解释为什么数据应该保存ViewModel中。 - 引用相关引用支持。 - 简要说明如何在代码中实现。 - 最后,生成相关问题。 生成相关问题: - 基于主题,生成几个相关问题,如如何实现、优化等。 相关问题示例: 1. 如何在MVVM中实现RecyclerView的数据绑定? 2. ViewModel如何处理数据加载和错误处理? 3. MVVM中RecyclerView的适配器应该如何设计? 确保回答真实可靠:基于Android开发的最佳实践。 现在,写出回答。</think>### Android MVVM架构中RecyclerView数据的存储位置 在Android MVVM(Model-View-ViewModel)架构中,RecyclerView的数据存储位置是一个关键问题,它直接影响到应用的可维护性、性能和解耦程度。根据MVVM的设计原则,RecyclerView的数据应该**保存ViewModel中,而不是Activity**。下面我将逐步解释原因,并结合实际引用说明如何实现。 #### 1. **MVVM架构的基本原则** - MVVM架构将应用分为三个核心部分: - **Model**:负责数据源(如网络请求、数据库操作)和业务逻辑。 - **View**:负责UI显示和用户交互,包括Activity、Fragment和XML布局。 - **ViewModel**:作为View和Model之间的桥梁,负责管理UI相关的状态数据(如RecyclerView的数据列表),并处理数据变化通知。 - ViewModel的核心优势是它独立于View的生命周期。当UI被销毁或重建(例如屏幕旋转),ViewModel中的数据会保留,避免重复加载数据,从而提升性能和用户体验[^3]。 - 如果数据保存在Activity中,Activity作为View的一部分,其数据会在UI重建丢失,导致不必要的重新加载(如网络请求),增加开销并可能引入错误(如空指针)[^2]。 #### 2. **为什么RecyclerView数据保存ViewModel中?** - **数据驱动UI**:在MVVM中,ViewModel通过LiveData或Observable字段暴露数据,View(Activity/Fragment)观察这些数据的变化。当数据更新(例如从Model获取新数据),ViewModel自动通知View更新RecyclerView的适配器。这实现了数据与UI的解耦,确保RecyclerView的渲染高效且一致[^1]。 - **避免生命周期问题**:Activity是短生命周期的组件,频繁重建会导致数据丢失。ViewModel则与Activity的生命周期解耦,能持久保存数据状态。例如,在RecyclerView场景中,ViewModel可以缓存列表数据,避免每次重建都重新请求后端数据[^3]。 - **代码可维护性**:将数据逻辑集中在ViewModel中,使Activity专注于UI操作(如设置适配器、处理点击事件)。这符合关注点分离原则,减少Activity的膨胀,便于测试和扩展[^4]。如果数据放在Activity中,会导致逻辑混乱,增加维护成本。 - 引用支持:如引用[4]所示,在实现RecyclerView列表数据通过ViewModel层管理,确保“数据与视图交互”的流畅性。具体代码中,VM层负责持有数据列表,而Activity仅负责绑定数据到RecyclerView适配器[^4]。 #### 3. **如何在代码中实现** - 以下是基于MVVM的RecyclerView数据存储的简化实现步骤(使用Kotlin和Android Jetpack组件): - **ViewModel定义**:在ViewModel中,使用LiveData保存列表数据,并通过Repository(Model层)获取数据。 ```kotlin class MyViewModel : ViewModel() { private val repository = DataRepository() val itemList: LiveData<List<Item>> = repository.fetchItems() // 数据保存ViewModel中 } ``` - **View层(Activity/Fragment)**:观察ViewModel数据变化,并更新RecyclerView适配器。 ```kotlin class MyActivity : AppCompatActivity() { private lateinit var binding: ActivityMainBinding private val viewModel: MyViewModel by viewModels() override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) binding = DataBindingUtil.setContentView(this, R.layout.activity_main) // 观察ViewModel中的数据变化 viewModel.itemList.observe(this) { items -> // 更新RecyclerView适配器 binding.recyclerView.adapter = MyAdapter(items) } } } ``` - **适配器设计**:适配器应轻量级,仅处理UI渲染。数据绑定可通过Data Binding库简化,如引用[5]中的`BindingViewHolder`类,它直接绑定数据到布局[^5]。 - 注意事项: - 避免在ViewModel中直接处理UI逻辑(如RecyclerView的点击事件),应通过回调或Data Binding处理[^4]。 - 对于复杂数据,使用Paging Library等Jetpack组件优化性能,防止数据绑定带来的开销[^2]。 #### 4. **总结** 在Android MVVM架构中,RecyclerView的数据应始终保存ViewModel中,而不是Activity。这确保了数据持久性、解耦和高性能,同遵循MVVM的核心原则。Activity作为View层,只应负责UI更新和用户交互,而数据管理由ViewModel处理。通过这种方式,您可以构建更健壮且可维护的应用,特别是在处理列表数据[^1][^3][^4]。 如果您有具体代码示例或更多细节需要讨论,请随补充!
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值