关键字Configuration,屏幕旋转,语言切换,字体切换, 源码

前段时间做了关于系统字体切换的功能,其中涉及到较多ConfigurationChanged的流程。屏幕旋转、mcc、系统语言切换等均是通过该流程来实现的。
网上少有这方面的描述,故将该部分总结提炼出来做个小结。
以下以屏幕旋转为例。关于屏幕旋转对于Activity的生命周期的影响也在以下可以体现。

ConfigurationChange Flow

讲太多也不如一个图来得实在,下图也是整个ConfigurationChange通用流程的概括,该流程是基于Android 5.1 和6.0画出,kk版本应该也是差不多的。图片请放大来看。

流程跟踪

下面是对屏幕旋转的事件跟踪,尽量干货.
注:有序列表标号代表对应上图中的时序节点

1.屏幕旋转事件上传

G-Sensor将旋转事件由底层上传到FW处理,改变Configuration中orientation的值并将事件继续上传:
Configuration中对屏幕方向的定义:

    /*** Overall orientation of the screen.  May be one of* {@link #ORIENTATION_LANDSCAPE}, {@link #ORIENTATION_PORTRAIT}.*/public int orientation;

调用ActivityManagerNative.getDefault().updatePersistentConfiguration(newConfig);将事件上传

2.ActivityManagerNative中使用远程代理通过Binder调用AMS的同名方法updatePersistentConfiguration

(远程代理这块不作深入了解)

   public void updatePersistentConfiguration(Configuration values) throws RemoteException{Parcel data = Parcel.obtain();Parcel reply = Parcel.obtain();data.writeInterfaceToken(IActivityManager.descriptor);values.writeToParcel(data, 0);mRemote.transact(UPDATE_PERSISTENT_CONFIGURATION_TRANSACTION, data, reply, 0);reply.readException();data.recycle();reply.recycle();}

然后在AMS中,遍历每一个最近运行的程序,同步顺序执行以下方法

    /*** Do either or both things: (1) change the current configuration, and (2)* make sure the given activity is running with the (now) current* configuration.  Returns true if the activity has been left running, or* false if <var>starting</var> is being destroyed to match the new* configuration.* @param persistent TODO*/
boolean updateConfigurationLocked(Configuration values, ActivityRecord starting, boolean persistent, boolean initLocale) {...mSystemThread.applyConfigurationToResources(configCopy);for (int i=mLruProcesses.size()-1; i>=0; i--) {ProcessRecord app = mLruProcesses.get(i);try {if (app.thread != null) {if (DEBUG_CONFIGURATION) Slog.v(TAG, "Sending to proc " + app.processName + " new config " + mConfiguration);app.thread.scheduleConfigurationChanged(configCopy);}} catch (Exception e) {}}...
}

3.通过代理对每个进程上传事件

得到远程服务对象在ActivityManagerService在本地的代理,最终调用了AMS的updateConfiguration()来更新系统Configuration。
其中scheduleConfigurationChanged()实际是通过Binder远程调用(该过程同步)ActivityThread中的同名方法//咳咳:

public final void scheduleConfigurationChanged(Configuration config)throws RemoteException {Parcel data = Parcel.obtain();data.writeInterfaceToken(IApplicationThread.descriptor);config.writeToParcel(data, 0);mRemote.transact(SCHEDULE_CONFIGURATION_CHANGED_TRANSACTION, data, null,IBinder.FLAG_ONEWAY);data.recycle();
}public void scheduleConfigurationChanged(Configuration config) {updatePendingConfiguration(config);sendMessage(H.CONFIGURATION_CHANGED, config);
}

4.每个进程进行事件响应

将该进程下执行对应CONFIGURATION_CHANGED处理:

final void handleConfigurationChanged(Configuration config, CompatibilityInfo compat) {...mResourcesManager.applyConfigurationToResourcesLocked(config, compat);         ...configDiff = mConfiguration.diff(config);mConfiguration.updateFrom(config);config = applyCompatConfiguration(mCurDefaultDisplayDpi);ArrayList<ComponentCallbacks2> callbacks = collectComponentCallbacks(false, config);freeTextLayoutCachesIfNeeded(configDiff);if (callbacks != null) {final int N = callbacks.size();for (int i=0; i<N; i++) {performConfigurationChanged(callbacks.get(i), config);}}
}

5.更新资源

Reload New Resources: 将Config应用到Resource的一系列操作。

public final boolean applyConfigurationToResourcesLocked(Configuration config,CompatibilityInfo compat) {... //对Configuration的比较以及更新到Resource      Resources.updateSystemConfiguration(config, defaultDisplayMetrics, compat);ApplicationPackageManager.configurationChanged();//清空Icon和String缓存Configuration tmpConfig = null;for (int i=mActiveResources.size()-1; i>=0; i--) {ResourcesKey key = mActiveResources.keyAt(i);Resources r = mActiveResources.valueAt(i).get();if (r != null) {...//更新Resource的Config} else {//Slog.i(TAG, "Removing old resources " + v.getKey());mActiveResources.removeAt(i);}}return changes != 0;
}

7.8.更新资源Res

在Resource中更新资源,在重新加载时就会使用新的资源

public void updateConfiguration(Configuration config, DisplayMetrics metrics, CompatibilityInfo compat) {synchronized (mAccessLock) {... //更新Resource指向//{@kth add 20151127 start//像字体大小切换、语言切换等都会在此处开始更新资源的指向//kth add 20151127 end@}...//清空drawable资源clearDrawableCachesLocked(mDrawableCache, configChanges);clearDrawableCachesLocked(mColorDrawableCache, configChanges);mAnimatorCache.onConfigurationChange(configChanges);mStateListAnimatorCache.onConfigurationChange(configChanges);mColorStateListCache.clear();flushLayoutCache();}...
}

10.回调反馈

当Configuration的操作执行完后,实现了ComponentCallbacks2接口的组件如Activity、Services、Application等将会执行回调onConfigurationChanged()方法(接口回调),从而实现正在运行的app中所有组件对Config的更新响应。针对屏幕旋转更新前台显示,其他Configuration如字体、语言等需要通知所有。
该方法针对同一进程下Activity的状态进行甄别,将符合条件的Act放入list以方便后面操作.。

private static void performConfigurationChanged(ComponentCallbacks2 cb, Configuration config) {// Only for Activity objects, check that they actually call up to their// superclass implementation.ComponentCallbacks2 is an interface, so// we check the runtime type and act accordingly.Activity activity = (cb instanceof Activity) ? (Activity) cb : null;...boolean shouldChangeConfig = false;if ((activity == null) || (activity.mCurrentConfig == null)) {shouldChangeConfig = true;} else {// If the new config is the same as the config this Activity// is already running with then don't bother calling// onConfigurationChangedint diff = activity.mCurrentConfig.diff(config);if (diff != 0) {// If this activity doesn't handle any of the config changes// then don't bother calling onConfigurationChanged as we're// going to destroy it.if ((~activity.mActivityInfo.getRealConfigChanged() & diff) == 0) {shouldChangeConfig = true;}}}...if (shouldChangeConfig) {cb.onConfigurationChanged(config);if (activity != null) {...activity.mCurrentConfig = new Configuration(config);}}
}

Activity、Service、Application、Provider同样实现了ComponentCallbacks接口,从而实现四大组件全部更新状态和资源

   ArrayList<ComponentCallbacks2> collectComponentCallbacks(boolean allActivities, Configuration newConfig) {ArrayList<ComponentCallbacks2> callbacks= new ArrayList<ComponentCallbacks2>(); synchronized (mResourcesManager) {final int NAPP = mAllApplications.size();// Applicationfor (int i=0; i<NAPP; i++) {callbacks.add(mAllApplications.get(i));}final int NACT = mActivities.size();// Activityfor (int i=0; i<NACT; i++) {ActivityClientRecord ar = mActivities.valueAt(i);Activity a = ar.activity;if (a != null) {Configuration thisConfig = applyConfigCompatMainThread(mCurDefaultDisplayDpi, newConfig,ar.packageInfo.getCompatibilityInfo());if (!ar.activity.mFinished && (allActivities || !ar.paused)) {// If the activity is currently resumed, its configuration// needs to change right now.callbacks.add(a);} else if (thisConfig != null) {// Otherwise, we will tell it about the change// the next time it is resumed or shown.  Note that// the activity manager may, before then, decide the// activity needs to be destroyed to handle its new// configuration.if (DEBUG_CONFIGURATION) {Slog.v(TAG, "Setting activity " + ar.activityInfo.name + " newConfig=" + thisConfig);}ar.newConfig = thisConfig;}}}final int NSVC = mServices.size();// Servicefor (int i=0; i<NSVC; i++) {callbacks.add(mServices.valueAt(i));}}synchronized (mProviderMap) {final int NPRV = mLocalProviders.size();// Providerfor (int i=0; i<NPRV; i++) {callbacks.add(mLocalProviders.valueAt(i).mLocalProvider);}}return callbacks;}

11.四大组件事件响应

如果在manifest.xml中配置了configChnages属性则表示由app自己来处理configuration change,就会回调Activity等组件的onConfigurationChanged方法。否则就重启当前这个activity(这个重启步骤位于当activity回到前台时执行onDestroy->onStart->onResume),而重启之前,旧的resources已经被清空, 那么就会装载新的资源。对于未启动的应用则会在启动时加载新的资源。参考如下:

(引自Activity.java)

   /*** Called by the system when the device configuration changes while your* activity is running.  Note that this will <em>only</em> be called if* you have selected configurations you would like to handle with the* {@link android.R.attr#configChanges} attribute in your manifest.  If* any configuration change occurs that is not selected to be reported* by that attribute, then instead of reporting it the system will stop* and restart the activity (to have it launched with the new* configuration).** <p>At the time that this function has been called, your Resources* object will have been updated to return resource values matching the* new configuration.** @param newConfig The new device configuration.*/

顺道贴下ConfigurationChanged时Act的生命周期以便理解:

01-02 18:01:44.039 11305 11305 E MainActivity: onPause
01-02 18:01:44.040 11305 11305 E MainActivity: onSaveInstanceState
01-02 18:01:44.040 11305 11305 E MainActivity: onStop01-02 18:01:44.040 11305 11305 E MainActivity: onDestroy
01-02 18:01:44.063 11305 11305 E MainActivity: onStart
01-02 18:01:44.064 11305 11305 E MainActivity: onRestoreInstanceState
01-02 18:01:44.064 11305 11305 E MainActivity: onResume

小结

以上就是Configuration时间的流程梳理,好记性不如烂笔头。主要是流程时序图麻烦,从自己知道到写下来中间也来回经历好些个小时翻源码。
记录以供需要的人参考。

Fucking The Source Code
附上参考过的csdn链接
七号大蒜
android源码分析(一) - 语言切换机制
谢捧场
=。=

ConfigurationChanged流程梳理(屏幕旋转、语言及字体切换)相关推荐

  1. [html] 如何阻止屏幕旋转时自动调整字体的大小?

    [html] 如何阻止屏幕旋转时自动调整字体的大小? html, body, form, fieldset, p, div, h1, h2, h3, h4, h5, h6 { -webkit-text ...

  2. android 屏幕旋转流程,android自动屏幕旋转流程分析.doc

    android自动屏幕旋转流程分析.doc android自动屏幕旋转流程分析 在android设置(Settings)中我们可以看到显示(display)下有一个自动屏幕旋转的checkbox, 如 ...

  3. Android 7.1 屏幕旋转流程分析

    Android 7.1   屏幕旋转流程分析 一.概述 Android屏幕的旋转在framework主要涉及到三个类,结构如图 PhoneWindowManager:为屏幕的横竖屏转换的管理类. Wi ...

  4. android wifi连接流程,(九十三) Android O 连接WiFi AP流程梳理续——保存网络-Go语言中文社区...

    前言: 之前在(五十五)Android O 连接WiFi AP流程梳理 梳理连接流程梳理到SupplicantStaNetworkHal 然后没梳理的下去,现在继续梳理下. 之前梳理的时序图 1.流程 ...

  5. 嵌入式 linux 屏 翻转,linux嵌入式qt的屏幕旋转与字体大小问题

    [1.与qt程序显示文字大小有关的两项] export QWS_DISPLAY=LinuxFB:mmWidth200:mmHeight100:0   //输出设备为Linux framebuffer, ...

  6. 嵌入式 linux 屏 翻转,linux嵌入式qt的屏幕旋转与字体大小疑点

    QT界面必须旋转,这需要在编译QTE库的时候,加入参数: -qt-gfx-transformed -qt-gfx-linuxfb [1.与qt程序显示文字大小有关的两项] export QWS_DIS ...

  7. 屏幕旋转导致Activity销毁重建,ViewModel是如何恢复数据的

    前言 当屏幕旋转或者切换系统语言时,Activity 生命周期从销毁再重建,但是ViewModel里面的变量值不受到影响,说明ViewModel中的变量在屏幕旋转前进行了存储,在屏幕旋转后又进行了恢复 ...

  8. Android App屏幕旋转要点

    文章目录 前言 屏幕旋转"预热" android:screenOrientation android:configChanges Acitvity onConfigurationC ...

  9. 仰邦LED(BX-Y08)二次开发的java版流程梳理记录

    仰邦LED-bx_y08二次开发的java版流程梳理记录 首次开发的项目是一个停车场主要显示剩余多少车辆 遇到很多难题;主要还要搞清楚二次开发LED的发送流程,在这记录下 //初始化API环境 仅一次 ...

最新文章

  1. h5做的app和原生app的区别
  2. ISME: 北大吴晓磊组发现囊泡为细菌利用环境血红素提供全新途径
  3. 解决VMware虚拟机时间同步问题
  4. python的用途实例-python中类方法,实例方法,静态方法的作用和区别
  5. Leetcode46全排列DFS
  6. 查看Linux系统基本信息CPU内存系统负载
  7. 恢复Ext3下被删除的文件(转)
  8. JS:封装函数判断数据类型
  9. 程序员愤怒了:我们不仅要当管理者还要当CEO!
  10. php 强制输出数组,php怎么将对象强制转数组
  11. 关于K8s技术架构的几个问题
  12. windows8 系统语言由中文修改为英文系统
  13. mcinabox运行库下载_mcinabox下载-mcinabox运行库(附安装教程)最新官网版下载v0.1.3-1y2y游戏...
  14. JavaScript学习之初识JS
  15. Richard Hamming:You and your research
  16. ramda 函数 Object
  17. 大厂P5、P6、P7级程序员的简历长什么样?
  18. 国产十大手表品牌有哪些?值得买的中国手表品牌推荐
  19. 汇佳学校新推“未来艺术大师”项目 实现学术艺术双赢
  20. 如何使用eclipse开发单片机程序

热门文章

  1. 后台缓存收回进程无法释放上下文[/BUSINESS的缓存的[10]%-请考虑增加缓存的最大大小
  2. 计算机高中竞赛自主招生,自主招生必备常识:高校认可的各类竞赛
  3. 对 emwin 窗口创建的认识
  4. 将AIR-CAP2702I-H-K9升级成胖AP
  5. springboot停车场车辆定位管理可视化分析系统的设计与实现毕业设计源码101702
  6. 仿简书登录框,可删除内容或显示密码框的内容
  7. jQuery文件导入
  8. 简单有趣的互动小游戏介绍:好玩的密室脱逃H5互动小游戏
  9. opencv 大图中找小图,并点击小图
  10. 二手交易网站,校园二手交易系统,校园二手交易系统毕业设计