Android 9.0 默认输入法的设置流程分析
Android 输入法设置文章
Android 9.0 添加预置第三方输入法/设置默认输入法(软键盘)
前言
在上一篇文章 Android 9.0 添加预置第三方输入法/设置默认输入法(软键盘) 中我们可以通过设置enabled_input_methods和default_input_method两个key-value的值来显示的指定可选的输入法及默认输入法。
但是,查看Android原生代码,并没任何地方显示的设置这两个值,但是当开机后,我们去console先查看,这两个值却被设置为了google原生输入法,那这两个值是在哪里设置的呢?本篇将简单介绍

设置流程分析
1. Android系统开机后,当ActivityManagerService及PackageManagerService都ready后,systemserver会回调到InputMethodManagerService::systemRunning()方法http://aosp.opersys.com/xref/android-9.0.0_r61/xref/frameworks/base/services/core/java/com/android/server/InputMethodManagerService.java#1508
2. systemRunning()方法中会去设置一些初始参数,并依次调用buildInputMethodListLocked和resetDefaultImeLocked
public void systemRunning(StatusBarManagerService statusBar) {
synchronized (mMethodMap) {
....
final String defaultImiId = mSettings.getSelectedInputMethod(); // 获取默认输入法,第一次开机时应该是空
final boolean imeSelectedOnBoot = !TextUtils.isEmpty(defaultImiId);
buildInputMethodListLocked(!imeSelectedOnBoot /* resetDefaultEnabledIme */);// 传递参数resetDefaultEnabledIme=true
resetDefaultImeLocked(mContext);
updateFromSettingsLocked(true);
....
}
}
3. 接下来我们来看一下buildInputMethodListLocked方法,部分源码如下:
点击查看代码
void buildInputMethodListLocked(boolean resetDefaultEnabledIme) {
if (DEBUG) {
Slog.d(TAG, "--- re-buildInputMethodList reset = " + resetDefaultEnabledIme
+ " \n ------ caller=" + Debug.getCallers(10));
}
if (!mSystemReady) {
Slog.e(TAG, "buildInputMethodListLocked is not allowed until system is ready");
return;
}
mMethodList.clear();
mMethodMap.clear();
mMethodMapUpdateCount++;
mMyPackageMonitor.clearKnownImePackageNamesLocked();
// 第一阶段
// Use for queryIntentServicesAsUser
final PackageManager pm = mContext.getPackageManager();
// Note: We do not specify PackageManager.MATCH_ENCRYPTION_* flags here because the default
// behavior of PackageManager is exactly what we want. It by default picks up appropriate
// services depending on the unlock state for the specified user.
final List<ResolveInfo> services = pm.queryIntentServicesAsUser(
new Intent(InputMethod.SERVICE_INTERFACE),
getComponentMatchingFlags(PackageManager.GET_META_DATA
| PackageManager.MATCH_DISABLED_UNTIL_USED_COMPONENTS),
mSettings.getCurrentUserId());
final HashMap<String, List<InputMethodSubtype>> additionalSubtypeMap =
mFileManager.getAllAdditionalInputMethodSubtypes();
for (int i = 0; i < services.size(); ++i) {
ResolveInfo ri = services.get(i);
ServiceInfo si = ri.serviceInfo;
final String imeId = InputMethodInfo.computeId(ri);
if (!android.Manifest.permission.BIND_INPUT_METHOD.equals(si.permission)) {
Slog.w(TAG, "Skipping input method " + imeId
+ ": it does not require the permission "
+ android.Manifest.permission.BIND_INPUT_METHOD);
continue;
}
if (DEBUG) Slog.d(TAG, "Checking " + imeId);
final List<InputMethodSubtype> additionalSubtypes = additionalSubtypeMap.get(imeId);
try {
InputMethodInfo p = new InputMethodInfo(mContext, ri, additionalSubtypes);
mMethodList.add(p);
final String id = p.getId();
mMethodMap.put(id, p);
if (DEBUG) {
Slog.d(TAG, "Found an input method " + p);
}
} catch (Exception e) {
Slog.wtf(TAG, "Unable to load input method " + imeId, e);
}
}
// Construct the set of possible IME packages for onPackageChanged() to avoid false
// negatives when the package state remains to be the same but only the component state is
// changed.
{
// Here we intentionally use PackageManager.MATCH_DISABLED_COMPONENTS since the purpose
// of this query is to avoid false negatives. PackageManager.MATCH_ALL could be more
// conservative, but it seems we cannot use it for now (Issue 35176630).
final List<ResolveInfo> allInputMethodServices = pm.queryIntentServicesAsUser(
new Intent(InputMethod.SERVICE_INTERFACE),
getComponentMatchingFlags(PackageManager.MATCH_DISABLED_COMPONENTS),
mSettings.getCurrentUserId());
final int N = allInputMethodServices.size();
for (int i = 0; i < N; ++i) {
final ServiceInfo si = allInputMethodServices.get(i).serviceInfo;
if (android.Manifest.permission.BIND_INPUT_METHOD.equals(si.permission)) {
mMyPackageMonitor.addKnownImePackageNameLocked(si.packageName);
}
}
}
//第二阶段
boolean reenableMinimumNonAuxSystemImes = false;
if (resetDefaultEnabledIme || reenableMinimumNonAuxSystemImes) {
final ArrayList<InputMethodInfo> defaultEnabledIme =
InputMethodUtils.getDefaultEnabledImes(mContext, mMethodList,
reenableMinimumNonAuxSystemImes);
final int N = defaultEnabledIme.size();
for (int i = 0; i < N; ++i) {
final InputMethodInfo imi = defaultEnabledIme.get(i);
if (DEBUG) {
Slog.d(TAG, "--- enable ime = " + imi);
}
setInputMethodEnabledLocked(imi.getId(), true);
}
}
}
把代码处理流程大概分两个阶段:
第一阶段:透过PackageManager去检索已安装的输入法app,构建一个List:mMethodList
第二阶段:将上一步骤中检索的的输入法做enable ime处理,此时调用到了setInputMethodEnabledLocked(imi.getId(), true)
4. 再来看看setInputMethodEnabledLocked的内容:这个方法比较简单,调用mSettings.appendAndPutEnabledInputMethodLocked(id, false)去做设置
http://aosp.opersys.com/xref/android-9.0.0_r61/xref/frameworks/base/services/core/java/com/android/server/InputMethodManagerService.java#3987
点击查看代码
boolean setInputMethodEnabledLocked(String id, boolean enabled) {
// Make sure this is a valid input method.
InputMethodInfo imm = mMethodMap.get(id);
if (imm == null) {
throw new IllegalArgumentException("Unknown id: " + mCurMethodId);
}
List<Pair<String, ArrayList<String>>> enabledInputMethodsList = mSettings
.getEnabledInputMethodsAndSubtypeListLocked();
if (enabled) {
for (Pair<String, ArrayList<String>> pair: enabledInputMethodsList) {
if (pair.first.equals(id)) {
// We are enabling this input method, but it is already enabled.
// Nothing to do. The previous state was enabled.
return true;
}
}
mSettings.appendAndPutEnabledInputMethodLocked(id, false);
// Previous state was disabled.
return false;
} else {
StringBuilder builder = new StringBuilder();
if (mSettings.buildAndPutEnabledInputMethodsStrRemovingIdLocked(
builder, enabledInputMethodsList, id)) {
// Disabled input method is currently selected, switch to another one.
final String selId = mSettings.getSelectedInputMethod();
if (id.equals(selId) && !chooseNewDefaultIMELocked()) {
Slog.i(TAG, "Can't find new IME, unsetting the current input method.");
resetSelectedInputMethodAndSubtypeLocked("");
}
// Previous state was enabled.
return true;
} else {
// We are disabling the input method but it is already disabled.
// Nothing to do. The previous state was disabled.
return false;
}
}
}
5. 流程就走到了InputMethodUtils::putEnabledInputMethodStr,将值写入Settings数据库 putString(Settings.Secure.ENABLED_INPUT_METHODS, str);
分析到这里Settings数据库中enabled_input_methods这个key-value就有了默认值了,一般是“com.android.inputmethod.latin/.LatinIME”
6. 接着分析,buildInputMethodListLocked()完成后,返回到systemRunning()中继续调用到resetDefaultImeLocked()
private void resetDefaultImeLocked(Context context) {
// Do not reset the default (current) IME when it is a 3rd-party IME
if (mCurMethodId != null && !InputMethodUtils.isSystemIme(mMethodMap.get(mCurMethodId))) {
return;
}
final List<InputMethodInfo> suitableImes = InputMethodUtils.getDefaultEnabledImes(
context, mSettings.getEnabledInputMethodListLocked());
if (suitableImes.isEmpty()) {
Slog.i(TAG, "No default found");
return;
}
final InputMethodInfo defIm = suitableImes.get(0);
if (DEBUG) {
Slog.i(TAG, "Default found, using " + defIm.getId());
}
setSelectedInputMethodAndSubtypeLocked(defIm, NOT_A_SUBTYPE_ID, false);
}
7. 继续走到setSelectedInputMethodAndSubtypeLocked方法中
点击查看代码
private void setSelectedInputMethodAndSubtypeLocked(InputMethodInfo imi, int subtypeId,
boolean setSubtypeOnly) {
// Updates to InputMethod are transient in VR mode. Its not included in history.
final boolean isVrInput = imi != null && imi.isVrOnly();
if (!isVrInput) {
// Update the history of InputMethod and Subtype
mSettings.saveCurrentInputMethodAndSubtypeToHistory(mCurMethodId, mCurrentSubtype);
}
mCurUserActionNotificationSequenceNumber =
Math.max(mCurUserActionNotificationSequenceNumber + 1, 1);
if (DEBUG) {
Slog.d(TAG, "Bump mCurUserActionNotificationSequenceNumber:"
+ mCurUserActionNotificationSequenceNumber);
}
if (mCurClient != null && mCurClient.client != null) {
executeOrSendMessage(mCurClient.client, mCaller.obtainMessageIO(
MSG_SET_USER_ACTION_NOTIFICATION_SEQUENCE_NUMBER,
mCurUserActionNotificationSequenceNumber, mCurClient));
}
if (isVrInput) {
// Updates to InputMethod are transient in VR mode. Any changes to Settings are skipped.
return;
}
// Set Subtype here
if (imi == null || subtypeId < 0) {
mSettings.putSelectedSubtype(NOT_A_SUBTYPE_ID);
mCurrentSubtype = null;
} else {
if (subtypeId < imi.getSubtypeCount()) {
InputMethodSubtype subtype = imi.getSubtypeAt(subtypeId);
mSettings.putSelectedSubtype(subtype.hashCode());
mCurrentSubtype = subtype;
} else {
mSettings.putSelectedSubtype(NOT_A_SUBTYPE_ID);
// If the subtype is not specified, choose the most applicable one
mCurrentSubtype = getCurrentInputMethodSubtypeLocked();
}
}
if (!setSubtypeOnly) {
// Set InputMethod here
mSettings.putSelectedInputMethod(imi != null ? imi.getId() : "");
}
}
8. mSettings.putSelectedInputMethod(imi != null ? imi.getId() : "") ==> putSelectedInputMethod==>putString(Settings.Secure.DEFAULT_INPUT_METHOD, imeId)
最终将值写入Settings数据库中的default_input_method
至此default_input_method这个key-value也有了默认值
Android 9.0 默认输入法的设置流程分析的更多相关文章
- Android 如何修改默认输入法
前言 欢迎大家我分享和推荐好用的代码段~~ 声明 欢迎转载,但请保留文章原始出处: CSDN:http://www.csdn.net ...
- Android 获取系统默认输入法
import android.provider.Settings; import android.text.TextUtils; 获取默认输入法包名: private String getDefaul ...
- Android-语言设置流程分析
Android手机语言切换行为,是通过设置-语言和输入法-语言来改变手机的语言,其实这个功能很少被用户使用. 以Android5.1工程源码为基础,从设置app入手来分析和学习语言切换的过程: ...
- Android系统开发--灯光系统之电池灯的流程分析
Android系统开发--Android灯光系统之电池灯的流程分析 前期系统准备 运行初始化,创建系统服务 创建电池服务,获得电池灯;创建监听者监听上报电池事件: mSystemServiceMana ...
- android 5.0 默认水波纹背景属性,可设置不论什么View
actionBarItemBackground 5.0以上超出边界圆形水波纹 selectableItemBackground 5.0以上边界内圆形水波纹 这两个属性在5.0下面是默认的灰色效果 ...
- Android恢复出厂设置流程分析【Android源码解析十】
最近看恢复出厂的一个问题,以前也查过这方面的流程,所以这里整理一些AP+framework层的流程: 在setting-->备份与重置--->恢复出厂设置--->重置手机---> ...
- centos 6.0中文输入法的设置
我的centos 6.0 是全英文,中间写代码需要用到中文注释,自己摸索了下,搞好了就做个记录. 1).开机进入桌面,左上角有Applications , Places, System三个可扩展通道, ...
- Android4.0源码Launcher启动流程分析【android源码Launcher系列一】
最近研究ICS4.0的Launcher,发现4.0和2.3有稍微点区别,但是区别不是特别大,所以我就先整理一下Launcher启动的大致流程. Launcher其实是贯彻于手机的整个系统的,时时刻刻都 ...
- Android 6.0 默认关闭定位和GPS,开启后默认选省电
默认关闭定位和GPS 修改位置 frameworks/base/packages/SettingsProvider/res/values/defaults.xml <string name=&q ...
随机推荐
- java面试一日一题:字节java后端工程师面试题
今天来分享下字节一面面试题,各位小伙伴看看都能答上来吗,弄懂下面的问题你离字节又近了一步哦,加油吧 1.自我介绍: 2.问到项目中为什么选择hbase,如果有多个查询条件如何设置数据存储方案: 3.t ...
- ESP32-简单OTA升级
基于ESP-IDF4.1 1 #include "freertos/FreeRTOS.h" 2 #include "freertos/task.h" 3 #in ...
- 视频场景切换检测的FPGA实现
本文将继续讲述图像处理算法的FPGA实现,后续可能更新图像旋转(1080P).画中画.快速DCT等算法.视频场景切换检测常用于视频编解码领域,我选用的算法是双阈值灰度直方图检测法,起初在MATLAB上 ...
- 团队开发day03
完成安卓的登录和注册界面的设计,进行服务器端的开发,设计javabean实体 映射,零售商 ,商品,品牌商,订单类的构建 遇到问题:安卓发起网络请求,客户端回应请求,数据处理设置. 使用传统的方法 / ...
- 【Mysql】InnoDB 引擎中的页目录
一.页目录和槽 接上一篇,现在知道记录在页中按照主键大小顺序串成了单链表. 那么我使用主键查询的时候,最顺其自然的办法肯定是从第一条记录,也就是 Infrimum 记录开始,一直向后找,只要存在总会找 ...
- xshell工具使用
一.下载安装 参考见:https://www.bilibili.com/video/BV1hh411k7vy?from=search&seid=2244124597826079746 流程: ...
- web自动化测试(2):选择selenium优势?与PhantomJS/QTP/Monkey对比
上篇 <web自动化测试(1):再谈UI发展史与UI.功能自动化测试>,自动化测试工具众多, PC端常用的功能自动化测试工具 Selenium:开源工具集,用于回归功能测试或者系统用例说明 ...
- 基于小熊派Hi3861鸿蒙开发的IoT物联网学习【二】
HarmonyOS内核开发-信号量开发案例学习记录 一.LiteOS里面的任务管理介绍: 任务状态通常分为以下四种: 就绪(Ready):该任务在就绪列表中,只等待CPU. 运行(Running) ...
- JavaScript之DOM、DOM树
一 DOM JavaScript操作网页的接口,全称为"文档对象模型"(Document Object Model). 有这几个概念:文档.元素.节点 整个文档是一个文档节点 每个 ...
- Volitle
缓存一致性协议 最出名的是Intel的MESI协议,该协议保证了每个缓存中使用的共享变量的副本是一致的.其思想是:当CPU写数据时,如果发现操作的变量是共享变量,即在其他CPU中也存在该变量的副本,会 ...