本文章已授权微信公众号郭霖(guolin_blog)转载。

本文章主要是对ViewModel进行源码分析,建议对着示例代码阅读文章,示例代码如下:

ViewModelDemo

本文章使用的是Android SDK 29的源码分析。

定义

Android框架管理UI控制器的生命周期(例如:ActivityFragment),Framework可能决定销毁或者重新创建一个UI控制器,以响应某些用户操作或者设备事件,这些操作或者事件完全超出你的控制。

如果系统销毁或者重新创建一个UI控制器,那么你存储在其中的任何与UI相关的临时数据丢失,例如:你的应用程序在某个Activity中包含一个用户列表,当配置信息更改重新创建Activity时,新的Activity必须重新获取用户列表。对于简单数据,Activity可以使用onSaveInstanceState()方法,并且在onCreate()方法中从Bundle中恢复数据,但是这种方法只适用于少量的、可以序列化和反序列化的数据,而不是潜在的大量数据的用户列表或者是很多的Bitmap

另外一个问题是UI控制器经常需要进行异步调用,这可能需要一些时间才能返回,UI控制器需要管理这些调用,并确保系统在销毁后对其进行清理,以避免潜在的内存泄露,这种管理需要大量的维护,并且为了配置更改重新创建对象的情况下,这是对资源的浪费,因为对象可能不得不重新发出它已经发出的调用。

UI控制器(例如:ActivityFragment)主要用于显示UI数据响应用户操作或者处理操作系统通信(例如:权限请求),要求UI控制器也负责从数据库或者网络加载数据会使类膨胀,将过多的责任分配给UI控制器会导致单个类视图自己处理应用程序的所有工作,而不是将工作委托给其他类,这样也会使测试变得更加困难。

视图数据所有权UI控制器的逻辑中分离出来会更加简单、更有效,所以官方推出这样一个组件:ViewModel

ViewModel是一个负责准备和管理Activity或者Fragment的类,它还可以处理ActivityFragment与应用程序其余部分的通信(例如:调用业务逻辑类)。

ViewModel总是在一个Activity或者一个Fragment创建的,并且只要对应的Activity或者Fragment处于活动状态的话,它就会被保留(例如:如果它是个Activity,就会直到它finished)。

换句话说,这意味着一个ViewModel不会因为配置的更改(例如:旋转)而被销毁,所有的新实例将被重新连接到现有的ViewModel

ViewModel的目的是获取保存Activity或者Fragment所需的信息,Activity或者Fragment应该能够观察到ViewModel中的变化,通常通过LiveData或者Android Data Binding公开这些信息。

注意的是,ViewModel的唯一职责是管理UI的数据,它不应该访问你的视图层次结构或者保留对Activity或者Fragment的引用

以下这张图片表示Activity经历屏幕旋转而后结束的过程中所处的各种生命周期状态,还在关联的Activity生命周期的旁边显示了ViewModel的生命周期:

示例代码

项目加上如下依赖:

implementation 'androidx.lifecycle:lifecycle-extensions:2.2.0-alpha02'

由于我这边用到了DataBinding,所以加上如下代码:

dataBinding {    enabled = true}

项目结构如下图:

我这边定义了一个继承了ViewModel,并且实现了ObservableObservableViewModel类,来通知控件数据的变化,也可以使用LiveData来实现这样的功能,代码如下:

package com.tanjiajun.viewmodeldemo.viewmodelimport androidx.databinding.Observableimport androidx.databinding.PropertyChangeRegistryimport androidx.lifecycle.ViewModel/** * Created by TanJiaJun on 2019-11-24. */open class ObservableViewModel : ViewModel(), Observable {    private val callbacks = PropertyChangeRegistry()    override fun addOnPropertyChangedCallback(callback: Observable.OnPropertyChangedCallback?) =        callbacks.add(callback)    override fun removeOnPropertyChangedCallback(callback: Observable.OnPropertyChangedCallback?) =        callbacks.remove(callback)    fun notifyChange() =        callbacks.notifyCallbacks(this, 0, null)    fun notifyPropertyChanged(fieldId: Int) =        callbacks.notifyCallbacks(this, fieldId, null)}

第一个例子:ViewModel不会因为配置更改而被销毁

MainActivity中创建MainViewModel,代码如下:

// MainActivity.ktoverride fun onCreate(savedInstanceState: Bundle?) {    super.onCreate(savedInstanceState)    DataBindingUtil.setContentView<ActivityMainBinding>(this, R.layout.activity_main).also {        it.viewModel = ViewModelProviders.of(this)[MainViewModel::class.java].apply {            name = "谭嘉俊"            age = 25            gender = "男"        }        it.handlers = this    }}

这个Activity所对应的界面可以跟随手机屏幕旋转,而且没有通过android:configChanges指定属性,让Activity在指定属性变化的时候,只会调用ActivityonConfigurationChanged()方法,而不会被销毁重建,代码如下:

android:configChanges="orientation|screenSize|keyboardHidden"

当我们旋转手机屏幕的时候,发现这个Activity的内容没有发生变化,符合我们的预期。

第二个例子是:Fragment使用Activity共享的ViewModel处理数据

定义NameViewModel,并且继承我在上面说的ObservableViewModel,代码如下:

package com.tanjiajun.viewmodeldemo.viewmodelimport androidx.databinding.Bindableimport androidx.databinding.library.baseAdapters.BR/** * Created by TanJiaJun on 2019-11-24. */class NameViewModel : ObservableViewModel() {    @get:Bindable    var name = ""        set(value) {            field = value            notifyPropertyChanged(BR.name)        }}

FirstNameFragment使用一个和NameActivity生命周期相同的NameViewModel,代码如下:

// FirstNameFragment.ktprivate var viewModel: NameViewModel? = nulloverride fun onCreate(savedInstanceState: Bundle?) {    super.onCreate(savedInstanceState)    // retainInstance方法下面会解析    retainInstance = true    viewModel = activity?.let {        ViewModelProviders.of(it)[NameViewModel::class.java].apply {            name = "谭嘉俊"        }    }}override fun onCreateView(    inflater: LayoutInflater,    container: ViewGroup?,    savedInstanceState: Bundle?): View? =    DataBindingUtil.inflate<FragmentFirstNameBinding>(        inflater,        R.layout.fragment_first_name,        container,        false    )        .also {            // 使用NameActivity共享的NameViewModel            it.viewModel = viewModel            it.handlers = this        }        .root

retainInstance方法控制在Activity重新创建(例如:配置更改)期间是否重新创建Fragment实例,如果设为true的话,在Activity重新创建的时候,Fragment的生命周期会有点不一样,onCreate(Bundle)方法将不会被调用,因为Fragment没有重新创建,onDestroy()不会被调用,但是onDetach()方法会被调用,因为Fragment只是从它附加的Activity分离而已,**onAttach(Activity)方法和onActivityCreated(Bundle)**方法仍然会被调用。

// SecondNameFragment.ktoverride fun onCreateView(    inflater: LayoutInflater,    container: ViewGroup?,    savedInstanceState: Bundle?): View? =    DataBindingUtil.inflate<FragmentSecondNameBinding>(        inflater,        R.layout.fragment_second_name,        container,        false    )        .also { it.handlers = this }        .root// 点击按钮后会改变NameViewModel中name的属性值override fun onChangeNameToAppleClick(view: View) {    activity?.let { ViewModelProviders.of(it)[NameViewModel::class.java].name = "苹果" }}

点击SecondFragment的按钮后,我们再按后退键,退回到上个Fragment,可以看到name已经已经从**”谭嘉俊“变成”苹果“了,这里的NameViewModel的生命周期是和NameActivity的生命周期一样,也就是这两个Fragment拿到的都是同一个ViewModel**,所以我们可以这样处理附加在同一个Activity多个Fragment之间的数据。

源码分析

我们看下ViewModelProviders这个类,代码如下:

// 创建一个ViewModelProvider,在Fragment处于活动状态时保留ViewModel@NonNull@MainThreadpublic static ViewModelProvider of(@NonNull Fragment fragment) {    return of(fragment, null);}// 创建一个ViewModelProvider,在Activity处于活动状态时保留ViewModel@NonNull@MainThreadpublic static ViewModelProvider of(@NonNull FragmentActivity activity) {    return of(activity, null);}@NonNull@MainThreadpublic static ViewModelProvider of(@NonNull Fragment fragment, @Nullable Factory factory) {    // 检查Fragment是否附加在Application    Application application = checkApplication(checkActivity(fragment));    // 在上面的方法中factory是传null    if (factory == null) {        // 创建一个单例的AndroidViewModelFactory        factory = ViewModelProvider.AndroidViewModelFactory.getInstance(application);    }    // 创建ViewModelProvider,这里会拿到Fragment的ViewModelStore,下面会分析    return new ViewModelProvider(fragment.getViewModelStore(), factory);}@NonNull@MainThreadpublic static ViewModelProvider of(@NonNull FragmentActivity activity,        @Nullable Factory factory) {    // 检查Activity是否附加在Application    Application application = checkApplication(activity);    // 在上面的方法中factory是传null    if (factory == null) {        // 创建一个单例的AndroidViewModelFactory        factory = ViewModelProvider.AndroidViewModelFactory.getInstance(application);    }    // 创建ViewModelProvider,这里会拿到Activity的ViewModelStore,下面会分析    return new ViewModelProvider(activity.getViewModelStore(), factory);}

我们看下Activity的**getViewModelStore()**方法,代码如下:

@NonNull@Overridepublic ViewModelStore getViewModelStore() {    // 检查Activity是否附加在Application    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) {        // 通过getLastNonConfigurationInstance()方法得到NonConfigurationInstances,下面会分析        NonConfigurationInstances nc =                (NonConfigurationInstances) getLastNonConfigurationInstance();        if (nc != null) {            // 从NonConfigurationInstances恢复ViewModelStore            mViewModelStore = nc.viewModelStore;        }        // 如果是空的话创建ViewModelStore对象        if (mViewModelStore == null) {            mViewModelStore = new ViewModelStore();        }    }    return mViewModelStore;}

在分析getLastNonConfigurationInstances()方法之前,我们看下onRetainNonConfigurationInstance()方法,它在Activity类中是一个返回null的方法,我们可以找到Activity的子类ComponentActivity,可以看到它重写了这个方法,代码如下:

// ComponentActivity.java// NonConfigurationInstances是一个final的静态类,里面有两个变量:custom和viewModelStorestatic final class NonConfigurationInstances {    Object custom;    ViewModelStore viewModelStore;}@Override@Nullablepublic final Object onRetainNonConfigurationInstance() {    // onRetainCustomNonConfigurationInstance()方法已弃用    Object custom = onRetainCustomNonConfigurationInstance();    ViewModelStore viewModelStore = mViewModelStore;    if (viewModelStore == null) {        // 如果viewModelStore是null的话,证明没人调用getViewModelStore(),所以看看我们最后一个NonConfigurationInstance是否存在ViewModelStore        NonConfigurationInstances nc =                (NonConfigurationInstances) getLastNonConfigurationInstance();        if (nc != null) {            // 如果有的话,就从NonConfigurationInstances取出ViewModelStore            viewModelStore = nc.viewModelStore;        }    }    if (viewModelStore == null && custom == null) {        // 如果ViewModelStore还是null而且custom也是null的话,证明没有NonConfigurationInstances        return null;    }    // 如果有ViewModelStore或者有custom的话,就创建NonConfigurationInstances对象,并且对其进行赋值    NonConfigurationInstances nci = new NonConfigurationInstances();    nci.custom = custom;    nci.viewModelStore = viewModelStore;    return nci;}

onRetainNonConfigurationInstance()方法是在一个Activity因为配置改变被销毁时被调用,这时就会创建一个新的实例,它会在onStop()方法和onDestroy()方法两者之间调用,我们可以在这里返回对象,甚至是Activity的实例也可以,之后我们可以在新的Activity的实例通过**getLastNonConfigurationInstance()**方法来检索,拿到我们想要的对象。

我们之前也用过onSaveInstanceState方法,调用这个方法可以在Activity被终止之前检索每个实例的状态,以便可以在onCreate方法或者onRestoreInstanceState方法中恢复状态,两个方法都会传入我们之前想要保留的Bundle对象,注意你还要给你的View设置id,因为它是通过id保存当前有焦点View,在Android P版本中,这个方法将在onStop()方法之后调用,在之前的版本中,这个方法将在onStop()方法之前调用,并不能保证它将在onPause()方法之前或之后调用,这个方法默认实现保存了关于Activity的视图层次状态的临时信息,例如:EditText中的文本和ListView或者RecyclerView中的滚动条位置。

onSaveInstanceState方法和onRetainNonConfigurationInstance()方法还有什么区别呢?其中一点是前者是保存到BundleBundle是有类型限制大小限制的,而且也要在主线程序列化和反序列化数据,而后者是保存到Object类型和大小都没有限制

我们继续看源码,从上面分析可知,会创建ViewModelStore对象,我们看下ViewModelStore的源码,代码如下:

public class ViewModelStore {    // 创建一个key为String,value为ViewModel的HashMap对象    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());    }    // 清除内部存储并且通知存储在这个HashMap中的所有的ViewModel不再被使用    public final void clear() {        for (ViewModel vm : mMap.values()) {            vm.clear();        }        mMap.clear();    }}

它是通过HashMap存放ViewModel的,然后我们回到上面ViewModelProvidersof方法,可以看到它创建了ViewModelProvider对象,看下它的构造方法,代码如下:

// ViewModelProvider.javapublic ViewModelProvider(@NonNull ViewModelStore store, @NonNull Factory factory) {    mFactory = factory;    mViewModelStore = store;}

构造方法就两个参数,第一个是ViewModelStore,用于存放ViewModel;第二个参数是Factory,用于实例化多个ViewModel工厂

在我们的示例代码中,我们调用了ViewModelProviderget方法,传入的是我们创建的ViewModelClass对象,看下相关的代码,代码如下:

// ViewModelProvider.javaprivate static final String DEFAULT_KEY =        "androidx.lifecycle.ViewModelProvider.DefaultKey";@NonNull@MainThreadpublic <T extends ViewModel> T get(@NonNull Class<T> modelClass) {    // 得到ViewModel的Class对象的Java语言规范定义的底层类的规范名称    String canonicalName = modelClass.getCanonicalName();    if (canonicalName == null) {        throw new IllegalArgumentException("Local and anonymous classes can not be ViewModels");    }    // 调用下面的get方法,传入“DEFAULT_KEY:modelClass的规范名称”字符串和ViewModel的Class对象    return get(DEFAULT_KEY + ":" + canonicalName, modelClass);}@SuppressWarnings("unchecked")@NonNull@MainThreadpublic <T extends ViewModel> T get(@NonNull String key, @NonNull Class<T> modelClass) {    // 通过key从ViewModelStore中的HashMap中得到ViewModel    ViewModel viewModel = mViewModelStore.get(key);    // 判断从ViewModelStore中得到的ViewModel是否是Class对象的一个实例,也就是说判断ViewModelStore中是否存在我们想要的ViewModel    if (modelClass.isInstance(viewModel)) {        // 如果有的话就返回对应的ViewModel        return (T) viewModel;    } else {        //noinspection StatementWithEmptyBody        if (viewModel != null) {            // TODO: log a warning.        }    }    // 如果没有的话就创建ViewModel    if (mFactory instanceof KeyedFactory) {        viewModel = ((KeyedFactory) (mFactory)).create(key, modelClass);    } else {        // 根据上面的代码可知,mFactory是Factory的实现类NewInstanceFactory的子类AndroidViewModelFactory,所以我们调用的是这段逻辑        viewModel = (mFactory).create(modelClass);    }    // 将创建好的ViewModel存放到ViewModelStore    mViewModelStore.put(key, viewModel);    // 返回ViewModel    return (T) viewModel;}

我们看下AndroidViewModelFactorycreate方法,代码如下:

// ViewModelProvider中的静态内部类AndroidViewModelFactory@NonNull@Overridepublic <T extends ViewModel> T create(@NonNull Class<T> modelClass) {    // 判断AndroidViewModel所表示的类或接口是否与modelClass所表示的类或接口相同,或者是否为其超类或超接口    if (AndroidViewModel.class.isAssignableFrom(modelClass)) {        //noinspection TryWithIdenticalCatches        try {            // 创建ViewModel对象            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);        }    }    // 根据我们的示例代码,我们传入的modelClass不是AndroidViewModel,而且也不是为其超类或者超接口,所以会执行以下逻辑    return super.create(modelClass);}

ViewModel是不能传入任何有Context引用的对象,这样导致内存泄露,如果需要使用的话,可以使用AndroidViewModel

然后会调用它的父类NewInstanceFactorycreate方法,代码如下:

@SuppressWarnings("ClassNewInstance")@NonNull@Overridepublic <T extends ViewModel> T create(@NonNull Class<T> modelClass) {    //noinspection TryWithIdenticalCatches    try {        // 创建由modelClass类对象表示的类的新实例        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);    }}

刚才我们看的是ActivitygetViewModelStore()方法,现在看下Fragment的**getViewModelStore()**方法,代码如下:

// Fragment.java@NonNull@Overridepublic ViewModelStore getViewModelStore() {    // 判断Fragment是否已经与Activity分离    if (mFragmentManager == null) {        throw new IllegalStateException("Can't access ViewModels from detached fragment");    }    return mFragmentManager.getViewModelStore(this);}

调用了FragmentManagerImplgetViewModelStore方法,代码如下:

// FragmentManagerImpl.java@NonNullViewModelStore getViewModelStore(@NonNull Fragment f) {    return mNonConfig.getViewModelStore(f);}

成员变量mNonConfigFragmentManagerVIewModel的引用,我们看下FragmentManagerViewModelgetViewModelStore方法,代码如下:

// FragmentManagerViewModel.java@NonNullViewModelStore getViewModelStore(@NonNull Fragment f) {    ViewModelStore viewModelStore = mViewModelStores.get(f.mWho);    if (viewModelStore == null) {        viewModelStore = new ViewModelStore();        mViewModelStores.put(f.mWho, viewModelStore);    }    return viewModelStore;}

成员变量mViewModelStoreskeyStringvalueViewModelStoreHashMap的引用,它是跟随Fragment的生命周期,根据Frament的内部唯一名称从这个HashMap中得到ViewModelStore,如果是空的话,就创建一个新的ViewModelStore对象,并且放入mViewModelStores,然后返回这个对象;如果不是空的话,就返回刚才从HashMap取得的ViewModelStore

到这里,ViewModel创建得到的源码就分析得差不多了,然后我们看下ViewModel什么时候被销毁,在上面分析ViewModelStore源码的时候,我们看到有个clear方法,这个方法用来清除内部存储并且通知存储在这个HashMap中的所有的ViewModel不再被使用,如果ViewModel跟随的Activity的生命周期的话,它会在如下代码调用这个方法:

public ComponentActivity() {    // 省略部分代码    getLifecycle().addObserver(new LifecycleEventObserver() {        @Override        public void onStateChanged(@NonNull LifecycleOwner source,                @NonNull Lifecycle.Event event) {            // 判断是否接收到Activity的destroy状态            if (event == Lifecycle.Event.ON_DESTROY) {                // 如果接收到,判断是否因为配置更改导致的destroy                if (!isChangingConfigurations()) {                    // 如果不是,调用ViewModelStore的clear方法                    getViewModelStore().clear();                }            }        }    });    // 省略部分代码}

如果ViewModel跟随的是Fragment的生命周期的话,它会在如下代码调用这个方法:

// FragmentManagerViewModel.javavoid clearNonConfigState(@NonNull Fragment f) {    if (FragmentManagerImpl.DEBUG) {        Log.d(FragmentManagerImpl.TAG, "Clearing non-config state for " + f);    }    // 清除并且删除Fragment的子配置状态    FragmentManagerViewModel childNonConfig = mChildNonConfigs.get(f.mWho);    if (childNonConfig != null) {        childNonConfig.onCleared();        mChildNonConfigs.remove(f.mWho);    }    // 清除并且删除Fragment的ViewModelStore    ViewModelStore viewModelStore = mViewModelStores.get(f.mWho);    if (viewModelStore != null) {        viewModelStore.clear();        mViewModelStores.remove(f.mWho);    }}

在如下代码调用clearNonConfigState这个方法:

// FragmentManagerImpl.java@SuppressWarnings("ReferenceEquality")void moveToState(Fragment f, int newState, int transit, int transitionStyle,                 boolean keepActive) {    // 省略部分代码    if (f.mState <= newState) {        // 省略部分代码    } else if (f.mState > newState) {        switch (f.mState) {            // 省略部分代码            case Fragment.CREATED:                if (newState < Fragment.CREATED) {                    // 省略部分代码                    if (f.getAnimatingAway() != null || f.getAnimator() != null) {                        // 省略部分代码                    } else {                        if (DEBUG) Log.v(TAG, "movefrom CREATED: " + f);                        boolean beingRemoved = f.mRemoving && !f.isInBackStack();                        // 判断Fragment是否正在remove,同时还没放入后退栈,或者判断是否FragmentManagerViewModel是否应该销毁                        if (beingRemoved || mNonConfig.shouldDestroy(f)) {                            boolean shouldClear;                            // 判断mHost是否是ViewModelStoreOwner的实例                            if (mHost instanceof ViewModelStoreOwner) {                                // 如果是,shouldClear的值就是FragmentManagerViewModel是否已经清除                                shouldClear = mNonConfig.isCleared();                            } else if (mHost.getContext() instanceof Activity) {                                Activity activity = (Activity) mHost.getContext();                                shouldClear = !activity.isChangingConfigurations();                            } else {                                shouldClear = true;                            }                            // 根据beingRemoved或者shouldClear的值来判断是否需要清除ViewModel                            if (beingRemoved || shouldClear) {                                // 如果是,调用clearNonConfigState方法                                mNonConfig.clearNonConfigState(f);                            }                            // 执行Fragment的onDestroy()方法                            f.performDestroy();                            dispatchOnFragmentDestroyed(f, false);                        } else {                            f.mState = Fragment.INITIALIZING;                        }                        // 省略部分代码                    }                }        }    }    // 省略部分代码}

到这里,ViewModel销毁的源码分析得差不多了。

onSaveInstanceState和ViewModel

onSaveInstanceState是生命周期的一个回调方法,用来保存以下两种状态下的少量UI相关的数据:

  • 应用的进程在后台的时候由于内存限制而被终止。
  • 配置更改。

onSaveInstanceState不是被设计用来储存类似Bitmap这样大的数据,而是储存小的与UI相关的能够被序列化和反序列化的数据,上面也提及过了,这里就不再赘述了。

ViewModel有以下好处:

  • ViewModel可以架构设计更加良好,UI代码数据分离,使代码更加遵循单一职责原则更加模块化更易于测试
  • ViewModel能储存更大更复杂的数据,而且数据类型也没有限制,甚至可以储存Activity实例

要注意的是,ViewModel只能在配置更改造成相关的销毁下得到保留,而不能在被终止的进程中得到保留,也就是说在应用的进程在后台的时候由于内存限制而被终止,ViewModel也会被销毁

因此我们最好两者结合来处理保存和恢复UI状态,如果要保证数据不丢失,就要对数据进行本地持久化

题外话

如果应用在特定配置更改期间无需更新资源,并且因性能限制你需要避免Activity重启,则可声明Activity自行处理配置更改,从而阻止系统重启Activity

我们可以通过在AndroidManifest文件中,找到相应**元素,添加android:configChanges属性,在声明多个配置值的时候,可以通过|**字符对其进行分隔。

Android官方不建议对大多数应用使用此方法,因为这样做可能会提高使用备用资源的难度。

有如下属性:

  • density:显示密度发生变更,例如:用户可能已指定不同的显示比例,或者有不同的显示现处于活跃状态。**请注意:**此项为 API 级别 24 中的新增配置。

  • fontScale:字体缩放系数发生变更,例如:用户已选择新的全局字号。

  • keyboard:键盘类型发生变更,例如:用户插入外置键盘。

  • keyboardHidden:键盘无障碍功能发生变更,例如:用户显示硬键盘。

  • layoutDirection:布局方向发生变更,例如:自从左至右 (LTR) 更改为从右至左 (RTL)。**请注意:**此项为 API 级别 17 中的新增配置。

  • locale:语言区域发生变更,例如:用户已为文本选择新的显示语言。

  • mcc:IMSI 移动设备国家/地区代码 (MCC) 发生变更,例如:检测到 SIM 并更新 MCC。

  • mnc:IMSI 移动设备网络代码 (MNC) 发生变更,例如:检测到 SIM 并更新 MNC。

  • navigation:导航类型(轨迹球/方向键)发生变更。(这种情况通常不会发生。)

  • orientation:屏幕方向发生变更,例如:用户旋转设备。请注意:如果应用面向 Android 3.2(API 级别 13)或更高版本的系统,则还应声明screenSize配置,因为当设备在横向与纵向之间切换时,该配置也会发生变更。

  • screenLayout:屏幕布局发生变更,例如:不同的显示现可能处于活跃状态。

  • screenSize:当前可用屏幕尺寸发生变更。该值表示当前可用尺寸相对于当前纵横比的变更,当用户在横向与纵向之间切换时,它便会发生变更。**请注意:**此项为 API 级别 13 中的新增配置。

  • smallestScreenSize:物理屏幕尺寸发生变更。该值表示与方向无关的尺寸变更,因此它只有在实际物理屏幕尺寸发生变更(如切换到外部显示器)时才会变化。对此配置所作变更对应smallestWidth配置的变化。**请注意:**此项为 API 级别 13 中的新增配置。

  • touchscreen:触摸屏发生变更。(这种情况通常不会发生。)

  • uiMode:界面模式发生变更,例如:用户已将设备置于桌面或车载基座,或者夜间模式发生变更。如需了解有关不同界面模式的更多信息,请参阅UiModeManager。**请注意:**此项为 API 级别 8 中的新增配置。

所有这些配置变更都可能影响到应用所看到资源值,因此调用**onConfigurationChanged()**方法时,通常有必要再次检索所有资源(包括视图布局、可绘制对象等),以正确处理变更。

我的GitHub:TanJiaJunBeyond

Android通用框架:Android通用框架

我的掘金:谭嘉俊

我的简书:谭嘉俊

我的CSDN:谭嘉俊

更多相关文章

  1. Android(安卓)资源获取
  2. Android应用程序启动过程——Launcher源码分析
  3. Android系统上实现应用程序的静默安装
  4. 如何在多个LinearLayout中添加分隔线?
  5. [Android]滑动刷新ListView——android-pulltorefresh使用方法解
  6. android adb push 与 adb install 区别(两种安装APK的方法)
  7. Android(安卓)的Bitmap的修改方法
  8. 深入探讨 Android(安卓)传感器随处监控您的环境
  9. Android(安卓)实现文件的下载

随机推荐

  1. Android读写XML(下)——创建XML文档
  2. 我的android第一课
  3. 如何在Android(安卓)Studio使用Java8 Lam
  4. Android(安卓)ANR
  5. Android(安卓)Layout XML属性
  6. 在PC端进行android截屏的多种方法
  7. 使用Android(安卓)studio开发Android(安
  8. Android教程之MediaStore
  9. android TextView属性
  10. Android发送短信