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. c语言字符串存储方式

    #include <stdio.h> // C 语言中,任何数据类型都不可以直接存储一个字符串.那么字符串如何存储? //在 C 语言中,字符串有两种存储方式,一种是通过字符数组存储,另一 ...

  2. navicat for sqlserver 注册过程

    1.安装原软件,不要打开软件2.将Navicat_Keygen_Patch_v3.4_By_DFoX_URET复制到软件安装位置,运行3.选择navicat v12 products:SQL Serv ...

  3. [008] - JavaSE面试题(八):集合

    第一期:Java面试 - 100题,梳理各大网站优秀面试题.大家可以跟着我一起来刷刷Java理论知识 [008] - JavaSE面试题(八):集合 第1问:说一下集合的体系? 单列集合: 双列集合: ...

  4. JPcap入门

    1,参照入门:安装第一个代码:https://blog.csdn.net/qq_37638061/article/details/80710143 2,数据解析,不可用但有启发意义:https://b ...

  5. Qt+MPlayer音乐播放器开发笔记(一):ubuntu上编译MPlayer以及Demo演示

    前言   在ubuntu上实现MPlayer播放器播放音乐.   Demo                Mplayer   MPlayer是一款开源多媒体播放器,以GNU通用公共许可证发布.此款软件 ...

  6. 第三篇 -- Go语言其他类型转String方法

    1. int转string // 法1: int转string num_int := 10 num_str_2 := fmt.Sprintf("%b", num_int) fmt. ...

  7. 第七篇 -- photoshop cs6 激活

    下载photoshop cs6破解版 下载amtlib.dll 破解就是将amtlib.dll替换,路径:C:\Program Files\Adobe\Adobe Photoshop CS6 (64 ...

  8. Easyui设置easyui-textbox不可编辑

    转载自:https://blog.csdn.net/qq_23113521/article/details/78801689 在easyui里由于easyui-textbox被封装,通过一般的jque ...

  9. Spring Cloud Alibaba基础教程:Sentinel

    随着微服务的流行,服务和服务之间的稳定性变得越来越重要.Sentinel 是面向分布式服务架构的流量控制组件,主要以流量为切入点,从流量控制.熔断降级.系统自适应保护等多个维度来保障微服务的稳定性. ...

  10. jvm源码解读--16 cas 用法解析

    CAS的意思是campare and sweep比较交换 这个如果不用代码会比较抽象,那么在源码中进行解释 void ATTR ObjectMonitor::enter(TRAPS) { // The ...