Android输入法IME(三)之 管理端(IMMS)启动流程

2.2. IME管理端(IMMS)初始化流程

IMMS运行在system server进程中,属于系统服务的一部分,用于控制输入法的显示/隐藏、切换、绑定等操作。 涉及代码文件路径:

IMMS运行在system server进程中,属于系统服务的一部分,用于控制输入法的显示/隐藏、切换、绑定等操作。 涉及代码文件路径: frameworks/base/services/java/com/android/server/SystemServer.java frameworks/base/services/core/java/com/android/server/SystemServiceManager.java frameworks/base/core/java/android/os/SystemService.java frameworks/base/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java frameworks/base/packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java frameworks/base/packages/SettingsProvider/res/values/defaults.xml

2.2.1. 初始化函数流程梳理

# 我们从systemserver的startOtherServices函数开始梳理
# 此处需要注意,因为我梳理的是IMMS,而Google还提供了一个MultiClientInputMethodManagerService多客户端输入法服务进程,此处不梳理
# PS:从InputMethodManagerService代码文件中可以看到,Lifecycle是里面的一个内部类,继承systemservice
SystemServer.java -- startOtherServices,然后通过SystemServiceManager的startService启动IMMS,传入class name:"InputMethodManagerService.Lifecycle.class"---> SystemServiceManager.java -- startService有好几个重载的方法,说明下:(1)第一个startService方法,入参className即"InputMethodManagerService.Lifecycle.class",将其作为入参调用loadClassFromLoader(2)loadClassFromLoader会通过反射方法得到具体的Class类,返回Class<SystemService>类型的服务类,即继承SystemService的Lifecycle(3)调用第二个startService方法,入参即serviceClass(4)先通过"SystemService.class.isAssignableFrom(serviceClass)"判断该类是否是SysteService的子类(5)然后通过反射构造类的实例"service=constructor.newInstance(mContext)",即实例化Lifecycle类(重点)(6)调用第三个startService方法,入参该Lifecycle对象(2)先将该service添加到mServices列表中,然后调用SystemService.java的onStart函数---> InputMethodManagerService.java -- 通过上面的流程看到,此处会先调用Lifecycle类的构造函数,然后调用onStart函数(1)构造函数会创建IMMS实例,即"InputMethodManagerService mService=new InputMethodManagerService(context)"(2)onStart函数会将该mService通过publishBinderService方法发布到系统服务中,以便其他进行可以进行Binder获取到(即添加到dev/binder域管理)# 主要讲述IMMS对象被创建,从构造函数梳理---》 调用构造函数,主要用于注册一些监听事件, 获取必须的系统服务, UI相关的组件等

PS:

  1. SystemService启动输入法服务时,会有个判断启动IMMS还是MCIMMS。MULTI_CLIENT_IME_ENABLED(即persist.debug.multi_client_ime或ro.sys.multi_client_ime)开启,启动MultiClientInputMethodManagerService服务,否则启动InputMethodManagerService服务
  2. 关于MultiClientInputMethodManagerService就是多会话输入法,支持每屏幕焦点是启用此功能的前提。如果不支持,则无法启用此功能。由于安全限制,每屏幕焦点限制规定只有一小部分设备支持此功能。(详细参考Google官方文档和源码)

2.2.2. systemRunning函数流程梳理

# 我们从systemserver的startOtherServices函数开始梳理
# startBootPhase在服务startservice后执行,该函数将service分段处理,
# 例如此处IMMS在SystemService.PHASE_WAIT_FOR_SENSOR_SERVICE(200)和SystemService.PHASE_LOCK_SETTINGS_READY(480)之间
SystemServer.java -- startOtherServices,然后通过SystemServiceManager的startBootPhase---> SystemServiceManager.java -- startBootPhase遍历两个分段之间的服务,然后调用对应service的onBootPhase---> InputMethodManagerService.java -- 调用Lifecycle类的onBootPhase函数,然后调用InputMethodManagerService的systemRunning函数,主要内容:(1)MyPackageMonitor内部类register注册,监听安装包的变化,包含安装,卸载,更新等(2)SettingsObserver注册,监听当前用户的各种输入法相关的settingprovider变化,例如:默认输入法,输入法列表,输入法语言等(3)getSelectedInputMethod获取用户设置的输入法default_input_method,此处是查询settings数据库的默认输入法(frameworks/base/packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java)(4)buildInputMethodListLocked,如果没有默认输入法则入参false,该函数内容如下:---》 查询输入法服务信息,然后将信息储存到mMethodList,mMethodMap,mMyPackageMonitor中;---》 调用chooseNewDefaultIMELocked选择一个新的输入法;---》 updateInputMethodsFromSettingsLocked遍历所有输入法,如果输入法存在被禁用的组件,则重新启用调用setInputMethodLocked方法完成对输入法设置,和输入法发生变化的广播(ACTION_INPUT_METHOD_CHANGED)的发送(该函数中调用setInputMethodLocked)

一般我们修改默认输入法,packages/SettingsProvider/res/values/defaults.xml 数据库配置添加def_input_method和def_enable_input_methods,然后frameworks/base/packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java对应添加loadStringSetting加载引用DEFAULT_INPUT_METHOD和ENABLED_INPUT_METHODS

2.2.3. 代码详细说明

//SystemServer.java
private void startOtherServices(@NonNull TimingsTraceAndSlog t) {......// Bring up services needed for UI.if (mFactoryTestMode != FactoryTest.FACTORY_TEST_LOW_LEVEL) {t.traceBegin("StartInputMethodManagerLifecycle");if (InputMethodSystemProperty.MULTI_CLIENT_IME_ENABLED) {mSystemServiceManager.startService(MultiClientInputMethodManagerService.Lifecycle.class);} else {//启动IMMS服务mSystemServiceManager.startService(InputMethodManagerService.Lifecycle.class);}t.traceEnd();......}...
}//SystemServiceManager.java//第一个startService函数public SystemService startService(String className) {//调用loadClassFromLoaderfinal Class<SystemService> serviceClass = loadClassFromLoader(className,this.getClass().getClassLoader());return startService(serviceClass);}private static Class<SystemService> loadClassFromLoader(String className,ClassLoader classLoader) {try {//通过反射方法得到具体的Class类,返回Class<SystemService>类型的服务类,即继承SystemService的Lifecyclereturn (Class<SystemService>) Class.forName(className, true, classLoader);} catch (ClassNotFoundException ex) {.......}}//第二个startService函数public <T extends SystemService> T startService(Class<T> serviceClass) {try {final String name = serviceClass.getName();Slog.i(TAG, "Starting " + name);Trace.traceBegin(Trace.TRACE_TAG_SYSTEM_SERVER, "StartService " + name);// 判断该class该类是否是SysteService的子类if (!SystemService.class.isAssignableFrom(serviceClass)) {throw new RuntimeException("Failed to create " + name+ ": service must extend " + SystemService.class.getName());}final T service;try {//通过反射构造类的实例,即实例化Lifecycle类Constructor<T> constructor = serviceClass.getConstructor(Context.class);//newInstance实例化service = constructor.newInstance(mContext);} catch (InstantiationException ex) {throw new RuntimeException("Failed to create service " + name+ ": service could not be instantiated", ex);} ............//调用第三个startServicestartService(service);return service;} finally {Trace.traceEnd(Trace.TRACE_TAG_SYSTEM_SERVER);}}//第三个startService函数public void startService(@NonNull final SystemService service) {// Register it.将service注册到mServices列表中mServices.add(service);// Start it.long time = SystemClock.elapsedRealtime();try {//调用该service的onStart函数service.onStart();} catch (RuntimeException ex) {throw new RuntimeException("Failed to start service " + service.getClass().getName()+ ": onStart threw an exception", ex);}warnIfTooLong(SystemClock.elapsedRealtime() - time, service, "onStart");}//InputMethodManagerService.javapublic static final class Lifecycle extends SystemService {private InputMethodManagerService mService;//实例化时调用构造函数public Lifecycle(Context context) {super(context);//创建InputMethodManagerService IMMS对象,然后调用IMMS构造函数mService = new InputMethodManagerService(context);}//在startService中调用到此处@Overridepublic void onStart() {//将IMMS service添加到LocalServicesLocalServices.addService(InputMethodManagerInternal.class,new LocalServiceImpl(mService));//发布到系统服务中,以便其他进行可以进行Binder获取到(即添加到dev/binder域管理)publishBinderService(Context.INPUT_METHOD_SERVICE, mService, false /*allowIsolated*/,DUMP_FLAG_PRIORITY_CRITICAL | DUMP_FLAG_PRIORITY_NORMAL | DUMP_FLAG_PROTO);}.......}public class InputMethodManagerService extends IInputMethodManager.Stubimplements ServiceConnection, Handler.Callback {....//IMMS构造函数public InputMethodManagerService(Context context) {mIPackageManager = AppGlobals.getPackageManager();mContext = context;mRes = context.getResources();mHandler = new Handler(this);// Note: SettingsObserver doesn't register observers in its constructor.// SettingsObserver类型,用于监听来自设置的输入法配置, 比如默认输入法, 启用的输入法, 选择的输入法等mSettingsObserver = new SettingsObserver(mHandler);mIWindowManager = IWindowManager.Stub.asInterface(ServiceManager.getService(Context.WINDOW_SERVICE));mWindowManagerInternal = LocalServices.getService(WindowManagerInternal.class);mPackageManagerInternal = LocalServices.getService(PackageManagerInternal.class);mInputManagerInternal = LocalServices.getService(InputManagerInternal.class);mImeDisplayValidator = displayId -> mWindowManagerInternal.getDisplayImePolicy(displayId);.....// 状态栏输入法图标名称, 会根据这个名称设置输入法的图标显示mSlotIme = mContext.getString(com.android.internal.R.string.status_bar_ime);mIsLowRam = ActivityManager.isLowRamDeviceStatic();// 切换输入法时的通知Bundle extras = new Bundle();extras.putBoolean(Notification.EXTRA_ALLOW_DURING_SETUP, true);.....//获取UIDint userId = 0;try {userId = ActivityManager.getService().getCurrentUser().id;} catch (RemoteException e) {Slog.w(TAG, "Couldn't get current user ID; guessing it's 0", e);}// 最近切换的UIDmLastSwitchUserId = userId;//应在buildInputMethodListLocked之前创建mSettings//类型InputMethodSettings,输入法设置对象mSettings = new InputMethodSettings(mRes, context.getContentResolver(), mMethodMap, userId, !mSystemReady);updateCurrentProfileIds();AdditionalSubtypeUtils.load(mAdditionalSubtypeMap, userId);mSwitchingController = InputMethodSubtypeSwitchingController.createInstanceLocked(mSettings, context);mMenuController = new InputMethodMenuController(this);}......}

IMMS.java中几个重要的变量:

  1. String mCurMethodId:系统当前默认的输入法id, 可能为空, 与Settings.Secure#DEFAULT_INPUT_METHOD值保持一致,在setInputMethodLocked中赋值
  2. String mCurId:当前已经绑定的输入法id, 如果没有输入法绑定上的话, 值为null
  3. ClientState mCurClient:用于当前激活的IME, 只有持有这个令牌的IME才被系统认可
  4. IInputMethod mCurMethod:当前已经绑定的输入法接口, 如果为null, 说明没有任何输入法连接上

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/850315.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Linux云计算架构师涨薪班就业服务有哪些?

学员一站式就业服务:一次学习&#xff0c;薪资翻倍 简历制作与指导 学员在培训期间&#xff0c;人才顾问会提供简历制作和指导服务&#xff0c;帮助学员制作出一份专业、有吸引力的简历。简历是求职者给招聘单位的第一印象&#xff0c;因此非常重要 模拟面试与技巧指导 为了让…

WebGPU 引领前端未来,互动渲染如何驱动小红书业务增长?

在大前端时代&#xff0c;浏览器能力得到显著的增强&#xff0c;为前端开发带来了更多的可能性和挑战。作为一套全新的 Web API 标准&#xff0c;WebGPU 旨在提供高性能的 3D 图形和数据并行计算能力&#xff0c;其在游戏、虚拟现实、机器学习等多个行业和应用场景中展现出潜力…

嵌入式Linux系统编程 — 3.2 stat、fstat 和 lstat 函数查看文件属性

目录 1 文件有哪些属性 2 stat函数 2.1 stat函数简介 2.2 struct stat 结构体 2.3 struct timespec 结构体 2.4 示例程序 3 fstat 和 lstat 函数 3.1 fstat 函数 3.2 lstat 函数 1 文件有哪些属性 Linux文件属性是对文件和目录的元数据描述&#xff0c;包括文件类型…

30-unittest生成测试报告(HTMLTestRunner插件)

批量执行完测试用例后&#xff0c;为了更好的展示测试报告&#xff0c;最好是生成HTML格式的。本文使用第三方HTMLTestRunner插件生成测试报告。 一、导入HTMLTestRunner模块 这个模块下载不能通过pip安装&#xff0c;只能下载后手动导入&#xff0c;下载地址是&#xff1a;ht…

应用广义线性模型一|线性模型

文章目录 一、统计学及思维模式二、未知现象的数学描述三、线性模型&#xff08;一&#xff09;线性模型的定义&#xff08;二&#xff09;线性模型的参数估计&#xff08;三&#xff09;线性模型的应用&#xff08;四&#xff09;离散解释变量的设计向量构建方法 四、线性模型…

SpringAOP 常见应用场景

文章目录 SpringAOP1 概念2 常见应用场景3 AOP的几种通知类型分别有什么常见的应用场景4 AOP实现 性能监控4.1 首先&#xff0c;定义一个切面类&#xff0c;用于实现性能监控逻辑&#xff1a;4.2 定义自定义注解4.3 注解修饰监控的方法 5 AOP实现 API调用统计5.1 定义切面类&am…

mod_aws_transcribe

这里讲的很清楚&#xff1a; https://github.com/drachtio/drachtio-freeswitch-modules/blob/main/modules/mod_aws_transcribe/README.md sdk 到这里下载&#xff1a; https://github.com/aws/aws-sdk-cpp sdk 编译步骤&#xff1a; cd /usr/src git clone --recurse-subm…

深度图的方法实现加雾,Synscapes数据集以及D455相机拍摄为例

前言 在次之前&#xff0c;我们已经做了图像加雾的一些研究&#xff0c;这里我们将从深度图的方法实现加雾展开细讲 图像加雾算法的研究与应用_图像加雾 算法-CSDN博客 接下来将要介绍如何使用深度图像生成雾效图像的方法。利用Synscapes数据集&#xff0c;通过读取EXR格式的…

【实盘】第二十期:2024-06月~第一周

一、每周净值 01 CTA投资组合 CTA多品种全覆盖全天候策略2024年2月至2024年5月底实盘总收益12.753%&#xff0c;当前浮动净值为1.1407&#xff0c;当前平仓净值为1.12753。 月度最大本金回撤0.3%(资金曲线为平仓盈亏,总体回撤应加入浮动持仓的盈亏总体计算,实际当前净值见棕色…

【CS.SE】Tomcat启动闪退问题解决方法

文章目录 1. 引言2. 常见问题及解决方法2.1 环境变量配置错误2.1.1 问题描述2.1.2 解决方法2.1.3 示例 2.2 端口冲突2.2.1 问题描述2.2.2 解决方法2.2.3 示例 2.3 权限问题2.3.1 问题描述2.3.2 解决方法2.3.3 示例 2.4 内存设置不当2.4.1 问题描述2.4.2 解决方法2.4.3 示例 2.5…

typescript-类型别名

typescript-类型别名 类型别名声明 类型别名声明能够定义一个类型别名type AliasName Type type Point {x: number; y: number} type StringType string; type BooleanType true | false type Point {x: number; y: number; z?: number}类型别名不会创建出一种新的类型…

自动驾驶算法——提取点云障碍物轮廓—DBSCAN聚类算法

前言 最近在做项目过程中,我们注意到当考虑车体轮廓去进行全局路径规划(如混合A*算法),如果仅仅将障碍物点云数据中得每一个点只当成点,会造成较大的计算时间,如若将点云聚类成圆形 线型 多边形障碍物 会提高计算效率,这是其中一个原因,另一个原因是,我们需要知道障碍…

各种空气能热泵安装图

空气能热泵安装图 循环式空气能热泵安装图 直热循环式空气能热泵安装图 泳池空气能热泵安装图 循环式水源热泵热安装系统原理图 直热循环式水源热泵安装系统图 空气水源热泵安装图

flutter as连接网易模拟器

网易模拟器下载 Mac 使用MuMu模拟器调试 Flutter开发 Android Studio 安装第三方模拟器—网易MuMu Mac 安卓Studio使用外部模拟器 Mac电脑&#xff1a;Android Studio 连接 MUMU 网易模拟器 Mac 上 Android Studio 链接网易 MuMu 模拟器调试 在 .zshrc 中设置 adb 二进制文…

重构某测试站点

一、计算校验值 校验值结果&#xff1a; 文件名称&#xff1a;培训用centos.rar&#xff0c;文件大小&#xff1a;1,335,759,953&#xff0c;MD5&#xff1a;534EC38CDA7DA2196C84AC8F6092514B&#xff0c;SHA1&#xff1a;FD35D86A27A007AE10872980C48653A110DF6067&#xf…

EverWeb 强大的零基础Mac网页设计制作软件

搜索Mac软件之家下载EverWeb 强大的零基础Mac网页设计制作软件 EverWeb 4.2是非专业网页设计师的绝佳网页制作工具&#xff0c;无需编码即可创建美观、响应迅速的网站。只需拖放自己的图像、文本和其他任何html元素到网页布局的任何位置。 EverWeb的功能特性&#xff1a; 下…

C++模板编程—学习C++类库的编程基础

课程总目录 文章目录 一、详解函数模板二、类模板三、类模板实践&#xff1a;实现向量容器vector四、理解容器空间配置器allocator的重要性 一、详解函数模板 模板的意义&#xff1a;对类型也可以进行参数化了 // 也可以用template<class T>&#xff0c;但class容易和类…

适用于 Windows 的 8 大数据恢复软件

数据恢复软件可帮助您恢复因意外删除或由于某些技术故障&#xff08;如硬盘损坏等&#xff09;而丢失的数据。这些工具可帮助您从硬盘驱动器 (HDD) 中高效地恢复丢失的数据&#xff0c;因为这些工具不支持从 SSD 恢复数据。重要的是要了解&#xff0c;您删除的数据不会被系统永…

NodeJs实现脚本:将xlxs文件输出到json文件中

文章目录 前期工作和依赖笔记功能代码输出 最近有一个功能&#xff0c;将json文件里的内容抽取到一个xlxs中&#xff0c;然后维护xlxs文件。当要更新json文件时&#xff0c;就更新xlxs的内容并把它传回json中。这个脚本主要使用NodeJS写。 以下是完成此功能时做的一些笔记。 …

【面试八股总结】内存页面置换算法

参考资料&#xff1a;小林coding、阿秀 缺页中断 在 CPU 里访问一条 Load M 指令&#xff0c;然后 CPU 会去找 M 所对应的页表项。如果该页表项的状态位是「有效的」&#xff0c;那 CPU 就可以直接去访问物理内存了&#xff0c;如果状态位是「无效的」&#xff0c;则 CPU 则会…