ConfigurationChanged流程梳理(屏幕旋转、语言及字体切换)
关键字: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流程梳理(屏幕旋转、语言及字体切换)相关推荐
- [html] 如何阻止屏幕旋转时自动调整字体的大小?
[html] 如何阻止屏幕旋转时自动调整字体的大小? html, body, form, fieldset, p, div, h1, h2, h3, h4, h5, h6 { -webkit-text ...
- android 屏幕旋转流程,android自动屏幕旋转流程分析.doc
android自动屏幕旋转流程分析.doc android自动屏幕旋转流程分析 在android设置(Settings)中我们可以看到显示(display)下有一个自动屏幕旋转的checkbox, 如 ...
- Android 7.1 屏幕旋转流程分析
Android 7.1 屏幕旋转流程分析 一.概述 Android屏幕的旋转在framework主要涉及到三个类,结构如图 PhoneWindowManager:为屏幕的横竖屏转换的管理类. Wi ...
- android wifi连接流程,(九十三) Android O 连接WiFi AP流程梳理续——保存网络-Go语言中文社区...
前言: 之前在(五十五)Android O 连接WiFi AP流程梳理 梳理连接流程梳理到SupplicantStaNetworkHal 然后没梳理的下去,现在继续梳理下. 之前梳理的时序图 1.流程 ...
- 嵌入式 linux 屏 翻转,linux嵌入式qt的屏幕旋转与字体大小问题
[1.与qt程序显示文字大小有关的两项] export QWS_DISPLAY=LinuxFB:mmWidth200:mmHeight100:0 //输出设备为Linux framebuffer, ...
- 嵌入式 linux 屏 翻转,linux嵌入式qt的屏幕旋转与字体大小疑点
QT界面必须旋转,这需要在编译QTE库的时候,加入参数: -qt-gfx-transformed -qt-gfx-linuxfb [1.与qt程序显示文字大小有关的两项] export QWS_DIS ...
- 屏幕旋转导致Activity销毁重建,ViewModel是如何恢复数据的
前言 当屏幕旋转或者切换系统语言时,Activity 生命周期从销毁再重建,但是ViewModel里面的变量值不受到影响,说明ViewModel中的变量在屏幕旋转前进行了存储,在屏幕旋转后又进行了恢复 ...
- Android App屏幕旋转要点
文章目录 前言 屏幕旋转"预热" android:screenOrientation android:configChanges Acitvity onConfigurationC ...
- 仰邦LED(BX-Y08)二次开发的java版流程梳理记录
仰邦LED-bx_y08二次开发的java版流程梳理记录 首次开发的项目是一个停车场主要显示剩余多少车辆 遇到很多难题;主要还要搞清楚二次开发LED的发送流程,在这记录下 //初始化API环境 仅一次 ...
最新文章
- h5做的app和原生app的区别
- ISME: 北大吴晓磊组发现囊泡为细菌利用环境血红素提供全新途径
- 解决VMware虚拟机时间同步问题
- python的用途实例-python中类方法,实例方法,静态方法的作用和区别
- Leetcode46全排列DFS
- 查看Linux系统基本信息CPU内存系统负载
- 恢复Ext3下被删除的文件(转)
- JS:封装函数判断数据类型
- 程序员愤怒了:我们不仅要当管理者还要当CEO!
- php 强制输出数组,php怎么将对象强制转数组
- 关于K8s技术架构的几个问题
- windows8 系统语言由中文修改为英文系统
- mcinabox运行库下载_mcinabox下载-mcinabox运行库(附安装教程)最新官网版下载v0.1.3-1y2y游戏...
- JavaScript学习之初识JS
- Richard Hamming:You and your research
- ramda 函数 Object
- 大厂P5、P6、P7级程序员的简历长什么样?
- 国产十大手表品牌有哪些?值得买的中国手表品牌推荐
- 汇佳学校新推“未来艺术大师”项目 实现学术艺术双赢
- 如何使用eclipse开发单片机程序
热门文章
- 后台缓存收回进程无法释放上下文[/BUSINESS的缓存的[10]%-请考虑增加缓存的最大大小
- 计算机高中竞赛自主招生,自主招生必备常识:高校认可的各类竞赛
- 对 emwin 窗口创建的认识
- 将AIR-CAP2702I-H-K9升级成胖AP
- springboot停车场车辆定位管理可视化分析系统的设计与实现毕业设计源码101702
- 仿简书登录框,可删除内容或显示密码框的内容
- jQuery文件导入
- 简单有趣的互动小游戏介绍:好玩的密室脱逃H5互动小游戏
- opencv 大图中找小图,并点击小图
- 二手交易网站,校园二手交易系统,校园二手交易系统毕业设计