Android 输入法设置文章

  Android 9.0 默认输入法的设置流程分析

  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方法,部分源码如下:

http://aosp.opersys.com/xref/android-9.0.0_r61/xref/frameworks/base/services/core/java/com/android/server/InputMethodManagerService.java#3616

点击查看代码

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);

http://aosp.opersys.com/xref/android-9.0.0_r61/xref/frameworks/base/core/java/com/android/internal/inputmethod/InputMethodUtils.java#1052

http://aosp.opersys.com/xref/android-9.0.0_r61/xref/frameworks/base/core/java/com/android/internal/inputmethod/InputMethodUtils.java#1108

分析到这里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

http://aosp.opersys.com/xref/android-9.0.0_r61/xref/frameworks/base/core/java/com/android/internal/inputmethod/InputMethodUtils.java#1315

至此default_input_method这个key-value也有了默认值

Android 9.0 默认输入法的设置流程分析的更多相关文章

  1. Android 如何修改默认输入法

    前言          欢迎大家我分享和推荐好用的代码段~~ 声明          欢迎转载,但请保留文章原始出处:          CSDN:http://www.csdn.net        ...

  2. Android 获取系统默认输入法

    import android.provider.Settings; import android.text.TextUtils; 获取默认输入法包名: private String getDefaul ...

  3. Android-语言设置流程分析

    Android手机语言切换行为,是通过设置-语言和输入法-语言来改变手机的语言,其实这个功能很少被用户使用.     以Android5.1工程源码为基础,从设置app入手来分析和学习语言切换的过程: ...

  4. Android系统开发--灯光系统之电池灯的流程分析

    Android系统开发--Android灯光系统之电池灯的流程分析 前期系统准备 运行初始化,创建系统服务 创建电池服务,获得电池灯;创建监听者监听上报电池事件: mSystemServiceMana ...

  5. android 5.0 默认水波纹背景属性,可设置不论什么View

    actionBarItemBackground   5.0以上超出边界圆形水波纹 selectableItemBackground  5.0以上边界内圆形水波纹 这两个属性在5.0下面是默认的灰色效果 ...

  6. Android恢复出厂设置流程分析【Android源码解析十】

    最近看恢复出厂的一个问题,以前也查过这方面的流程,所以这里整理一些AP+framework层的流程: 在setting-->备份与重置--->恢复出厂设置--->重置手机---> ...

  7. centos 6.0中文输入法的设置

    我的centos 6.0 是全英文,中间写代码需要用到中文注释,自己摸索了下,搞好了就做个记录. 1).开机进入桌面,左上角有Applications , Places, System三个可扩展通道, ...

  8. Android4.0源码Launcher启动流程分析【android源码Launcher系列一】

    最近研究ICS4.0的Launcher,发现4.0和2.3有稍微点区别,但是区别不是特别大,所以我就先整理一下Launcher启动的大致流程. Launcher其实是贯彻于手机的整个系统的,时时刻刻都 ...

  9. Android 6.0 默认关闭定位和GPS,开启后默认选省电

    默认关闭定位和GPS 修改位置 frameworks/base/packages/SettingsProvider/res/values/defaults.xml <string name=&q ...

随机推荐

  1. sqlite用法总结

    p.p1 { margin: 0; font: 16px "Helvetica Neue"; color: rgba(0, 0, 255, 1) } p.p2 { margin: ...

  2. 2012年第三届蓝桥杯C/C++程序设计本科B组省赛 密码发生器

    密码发生器 题目描述: ```bash 在对银行账户等重要权限设置密码的时候,我们常常遇到这样的烦恼:如果为了好记用生日吧,容易被破解,不安全:如果设置不好记的密码,又担心自己也会忘记:如果写在纸上, ...

  3. Django基础-004 上下文管理器&中间件&前端公共代码复用

    一.上下文管理器 在views中重复使用的代码,可以在上下文管理器中实现 上下文管理器的处理流程如下: 1.先走完views里面的代码,将结果返回给前端 2.然后再将上下文的结果返回给前端 3.上下文 ...

  4. Django基础-003 配置Django自带的后台管理,操作数据库

    插入测试数据,可以自己写页面来插入数据 也可以使用Django自带的后台管理,来操作数据表 1.创建用户 python manage.py createsuperuser 2.在浏览器输入地址,进入D ...

  5. 三分钟掌握共享内存 & Actor并发模型

    吃点好的,很有必要.今天介绍常见的两种并发模型: 共享内存&Actor 共享内存 面向对象编程中,万物都是对象,数据+行为=对象: 多核时代,可并行多个线程,但是受限于资源对象,线程之间存在对 ...

  6. 无需kubectl!快速使用Prometheus监控Etcd

    在本文中,我们将安装一个Etcd集群并使用Prometheus和Grafana配置监控,以上这些操作我们都通过Rancher进行. 我们将看到在不需要依赖的情况下充分利用Rancher的应用商店实现这 ...

  7. PAT甲级:1066 Root of AVL Tree (25分)

    PAT甲级:1066 Root of AVL Tree (25分) 题干 An AVL tree is a self-balancing binary search tree. In an AVL t ...

  8. Innodb 锁的介绍

    如下博文是参考如下博文内容,再加整理. http://blog.chinaunix.net/uid-24111901-id-2627857.html http://blog.csdn.net/wang ...

  9. 前端基础EL表达式(八)

    一.什么是EL表达式? 1.什么是EL表达式? EL(Expression Language) 是为了使JSP写起来更加简单.表达式语言的灵感来自于 ECMAScript 和 XPath 表达式语言, ...

  10. 第八篇 -- 用U盘制作启动盘装Win10系统

    下载装机吧:http://www.zhuangjiba.com 装Win10参考文章:http://www.zhuangjiba.com/bios/13249.html U盘启动盘制作 1.首先将U盘 ...