当前位置: 首页 > news >正文

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, 说明没有任何输入法连接上

相关文章:

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

2.2. IME管理端&#xff08;IMMS&#xff09;初始化流程 IMMS运行在system server进程中&#xff0c;属于系统服务的一部分&#xff0c;用于控制输入法的显示/隐藏、切换、绑定等操作。 涉及代码文件路径&#xff1a; IMMS运行在system server进程中&#xff0c;属于系统服务的…...

elasticsearch安装与使用(4)-搜索入门

1、创建索引 PUT /hotel {"mappings": {"properties":{"title":{"type": "text"},"city":{"type": "keyword"},"price":{"type":"double"}}} }2、写入文档 …...

【UML用户指南】-12-对高级结构建模-接口、类型和角色

目录 1、名称 2、操作 3、关系 4、理解接口 5、常用建模技术 5.1、对系统中的接缝建模 5.2、对静态类型和动态类型建模 5.2.1、对静态类型建模 5.2.2、对动态类型建模 使接口易于理解和易于访问 接口在关于一个抽象做什么的描述与关于这个抽象如何做的实现之间定义了…...

C++笔试强训day42

目录 1.最大差值 2.兑换零钱 3.小红的子串 1.最大差值 链接https://www.nowcoder.com/practice/a01abbdc52ba4d5f8777fb5dae91b204?tpId182&tqId34396&rp1&ru/exam/company&qru/exam/company&sourceUrl%2Fexam%2Fcompany&difficulty2&judgeSta…...

Docker 中运行的 MySQL 数据库与 Docker 外部的管理系统连接

步骤 1&#xff1a;运行 MySQL 容器 首先&#xff0c;确保你的 Docker 容器中运行了 MySQL 数据库。 docker run --name mysql-container -e MYSQL_ROOT_PASSWORDmy-secret-pw -d -p 3306:3306 mysql:latest--name mysql-container 为容器命名。-e MYSQL_ROOT_PASSWORDmy-sec…...

10 设备树

掌握设备树是 Linux 驱动开发人员必备的技能! 1、什么是设备树 新版本 Linux 中,ARM 相关的驱动全部采用了设备树。Linux-4.1.15 支持设备树。我们了解一下设备树的起源、重点学习一下设备树语法。 设备树:Device Tree,就是“设备”和“树”,描述设备树的文件叫做 DTS(…...

【架构分析】GPU执行GEMM矩阵运算实例演示

背景介绍 Cutlass是 NVIDIA 提供的一套用于高效实现矩阵乘法和卷积操作的 C 库。它以 CUDA 为基础&#xff0c;提供了高度优化的数学运算&#xff0c;尤其适用于GPU上的高性能并行计算。本文以GEMM矩阵运算作为实例&#xff0c;展示Cutlass在GPU上执行GEMM运算的过程 实例演示…...

从《千脑智能》看大模型

千脑智能与大模型 千脑智能介绍 世界模型千脑智能理论——对大脑的全新理解旧大脑&#xff1a;演化的历史烙印新大脑&#xff1a;智慧的创新引擎新旧大脑的互动与争斗启示与借鉴 大脑对信息的处理和建模六根六尘六识 新脑&#xff1a;智能的创新中枢旧脑&#xff1a;生存的本能…...

k8s Pods漂移时间配置

默认为300秒 apiVersion: apps/v1 kind: Deployment metadata:name: my-test spec:replicas: 1selector:matchLabels:app: my-apptemplate:metadata:labels:app: my-appspec:containers:- name: my-containerimage: nginx:latestports:- containerPort: 80tolerations:- key: &…...

Python - json 美化格式、保存文件

文章目录 读取长篇幅的 jsonl 文件时&#xff0c;我们难以了解 json 的格式&#xff0c;复制出来贴到 sojson 之类的网站&#xff0c;当数据量大的时候感觉麻烦。 不如自己写个 json 格式美化&#xff0c;然后保存到文件。 text open(file_path).readline() # 读取 jsonl 文…...

博客目录~

1、Jenkins构建打包部署前端Vue项目至Nginx-CSDN博客 2、https://blog.csdn.net/askuld/article/details/139429298 3、基于DockerJenkins实现自动部署SpringBootMaven项目-CSDN博客 4、时序数据库ClickHouse的安装使用_clickhouse安装使用-CSDN博客 5、Valid&#xff0c…...

RPC RMI 区别以及在java中的应用

文章目录 1. 简介1.1 什么是RPC1.2 什么是RMI 2. RPC与RMI的区别2.1 RPC和RMI的优缺点对比RPC的优点RPC的缺点RMI的优点RMI的缺点 2.2 选择RPC还是RMI&#xff1f;应用场景和考虑因素选择RPC的场景选择RMI的场景 3. RPC在Java框架中的应用3.1 Java中常用的RPC框架3.2 RPC在Java…...

TCP和udp能使用同一个端口通讯吗

TCP和UDP是可以使用同一个端口进行通讯的。这是因为TCP和UDP是两个完全不同的协议&#xff0c;它们工作在传输层&#xff0c;各自维护不同的连接和会话。每个协议都有自己的端口号空间&#xff0c;因此TCP和UDP可以互不干扰地使用相同的端口号。 但是&#xff0c;需要注意的是…...

红黑树的介绍与实现

前言 前面我们介绍了AVL树&#xff0c;AVL树是一棵非常自律的树&#xff0c;有着严格的高度可控制&#xff01;但是正它的自律给他带来了另一个问题&#xff0c;即虽然他的查找效率很高&#xff0c;但是插入和删除由于旋转而导致效率没有那么高。我们上一期的结尾说过经常修改…...

easyexcel将csv转为excel处理数字问题

使用easyexcel可以将csv格式的文件转为.xlsx文件&#xff0c;但是csv中有很多数字&#xff0c;比如&#xff1a;"123","12.34","-111"&#xff0c;默认情况下会将其作为字符串写入.xlsx文件&#xff0c;就如同下面一样&#xff0c;字符类型的数字…...

DDMA信号处理以及数据处理的流程---随机目标生成

Hello&#xff0c;大家好&#xff0c;我是Xiaojie&#xff0c;好久不见&#xff0c;欢迎大家能够和Xiaojie一起学习毫米波雷达知识&#xff0c;Xiaojie准备连载一个系列的文章—DDMA信号处理以及数据处理的流程&#xff0c;本系列文章将从目标生成、信号仿真、测距、测速、cfar…...

爬虫实现思路

现在的人工智能太强大了&#xff0c;只要有问题&#xff0c;输入后就能给出大致的实现思路&#xff1b;我看了下确实没问题&#xff0c;只需要更改一些细节基本就能拿来就用&#xff1b;下面是我实验经历&#xff1a; 问题&#xff1a; c# 书写爬虫爬取按动物名称&#xff0c;…...

神经网络 torch.nn---Non-Linear Activations (ReLU)

ReLU — PyTorch 2.3 documentation torch.nn - PyTorch中文文档 (pytorch-cn.readthedocs.io) 非线性变换的目的 非线性变换的目的是为神经网络引入一些非线性特征&#xff0c;使其训练出一些符合各种曲线或各种特征的模型。 换句话来说&#xff0c;如果模型都是直线特征的…...

【微服务】使用kubekey部署k8s多节点及kubesphere

kubesphere官方部署文档 https://github.com/kubesphere/kubesphere/blob/master/README_zh.md kubuctl命令文档 https://kubernetes.io/zh-cn/docs/reference/kubectl/ k8s资源类型 https://kubernetes.io/zh-cn/docs/reference/kubectl/#%E8%B5%84%E6%BA%90%E7%B1%BB%E5%9E…...

目标检测数据集 - 垃圾桶满溢检测数据集下载「包含VOC、COCO、YOLO三种格式」

数据集介绍&#xff1a;垃圾桶满溢检测数据集&#xff0c;真实场景高质量图片数据&#xff0c;涉及场景丰富&#xff0c;比如城市道边垃圾桶满溢、小区垃圾桶满溢、社区垃圾桶满溢、农村道边垃圾桶满溢、垃圾集中处理点垃圾桶满溢、公园垃圾桶满溢数据等。数据集标注标签划分为…...

Vue记事本应用实现教程

文章目录 1. 项目介绍2. 开发环境准备3. 设计应用界面4. 创建Vue实例和数据模型5. 实现记事本功能5.1 添加新记事项5.2 删除记事项5.3 清空所有记事 6. 添加样式7. 功能扩展&#xff1a;显示创建时间8. 功能扩展&#xff1a;记事项搜索9. 完整代码10. Vue知识点解析10.1 数据绑…...

【WiFi帧结构】

文章目录 帧结构MAC头部管理帧 帧结构 Wi-Fi的帧分为三部分组成&#xff1a;MAC头部frame bodyFCS&#xff0c;其中MAC是固定格式的&#xff0c;frame body是可变长度。 MAC头部有frame control&#xff0c;duration&#xff0c;address1&#xff0c;address2&#xff0c;addre…...

【入坑系列】TiDB 强制索引在不同库下不生效问题

文章目录 背景SQL 优化情况线上SQL运行情况分析怀疑1:执行计划绑定问题?尝试:SHOW WARNINGS 查看警告探索 TiDB 的 USE_INDEX 写法Hint 不生效问题排查解决参考背景 项目中使用 TiDB 数据库,并对 SQL 进行优化了,添加了强制索引。 UAT 环境已经生效,但 PROD 环境强制索…...

SCAU期末笔记 - 数据分析与数据挖掘题库解析

这门怎么题库答案不全啊日 来简单学一下子来 一、选择题&#xff08;可多选&#xff09; 将原始数据进行集成、变换、维度规约、数值规约是在以下哪个步骤的任务?(C) A. 频繁模式挖掘 B.分类和预测 C.数据预处理 D.数据流挖掘 A. 频繁模式挖掘&#xff1a;专注于发现数据中…...

YSYX学习记录(八)

C语言&#xff0c;练习0&#xff1a; 先创建一个文件夹&#xff0c;我用的是物理机&#xff1a; 安装build-essential 练习1&#xff1a; 我注释掉了 #include <stdio.h> 出现下面错误 在你的文本编辑器中打开ex1文件&#xff0c;随机修改或删除一部分&#xff0c;之后…...

家政维修平台实战20:权限设计

目录 1 获取工人信息2 搭建工人入口3 权限判断总结 目前我们已经搭建好了基础的用户体系&#xff0c;主要是分成几个表&#xff0c;用户表我们是记录用户的基础信息&#xff0c;包括手机、昵称、头像。而工人和员工各有各的表。那么就有一个问题&#xff0c;不同的角色&#xf…...

Frozen-Flask :将 Flask 应用“冻结”为静态文件

Frozen-Flask 是一个用于将 Flask 应用“冻结”为静态文件的 Python 扩展。它的核心用途是&#xff1a;将一个 Flask Web 应用生成成纯静态 HTML 文件&#xff0c;从而可以部署到静态网站托管服务上&#xff0c;如 GitHub Pages、Netlify 或任何支持静态文件的网站服务器。 &am…...

JVM 内存结构 详解

内存结构 运行时数据区&#xff1a; Java虚拟机在运行Java程序过程中管理的内存区域。 程序计数器&#xff1a; ​ 线程私有&#xff0c;程序控制流的指示器&#xff0c;分支、循环、跳转、异常处理、线程恢复等基础功能都依赖这个计数器完成。 ​ 每个线程都有一个程序计数…...

HybridVLA——让单一LLM同时具备扩散和自回归动作预测能力:训练时既扩散也回归,但推理时则扩散

前言 如上一篇文章《dexcap升级版之DexWild》中的前言部分所说&#xff0c;在叠衣服的过程中&#xff0c;我会带着团队对比各种模型、方法、策略&#xff0c;毕竟针对各个场景始终寻找更优的解决方案&#xff0c;是我个人和我司「七月在线」的职责之一 且个人认为&#xff0c…...

SQL Server 触发器调用存储过程实现发送 HTTP 请求

文章目录 需求分析解决第 1 步:前置条件,启用 OLE 自动化方式 1:使用 SQL 实现启用 OLE 自动化方式 2:Sql Server 2005启动OLE自动化方式 3:Sql Server 2008启动OLE自动化第 2 步:创建存储过程第 3 步:创建触发器扩展 - 如何调试?第 1 步:登录 SQL Server 2008第 2 步…...