概述
Android输入法框架(IMF - Input Method Framework)是Android系统中负责管理虚拟键盘和文本输入的核心组件。该框架协调输入法服务(IME)、应用程序和系统输入系统之间的复杂交互,为用户提供灵活高效的文本输入体验。本文深入分析Android 7.0中IME如何与输入系统协作,包括架构设计、生命周期管理、事件处理和窗口管理等关键机制。
输入法框架整体架构
┌─────────────────────────────────────────────────────────────┐
│ 应用层 │
│ ┌─────────────────┐ ┌─────────────────┐ ┌─────────────┐ │
│ │ EditText │ │ WebView │ │ Custom │ │
│ │ (文本编辑) │ │ (Web输入) │ │ Input │ │
│ └─────────────────┘ └─────────────────┘ └─────────────┘ │
│ │ │ │ │
│ ▼ ▼ ▼ │
│ ┌─────────────────────────────────────────────────────────┐ │
│ │ InputMethodManager │ │
│ │ (客户端输入法管理器) │ │
│ └─────────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────┘
│
▼ (Binder IPC)
┌─────────────────────────────────────────────────────────────┐
│ 系统服务层 │
│ ┌─────────────────────────────────────────────────────────┐ │
│ │ InputMethodManagerService │ │
│ │ (输入法管理服务) │ │
│ │ ┌─────────────────────────────────────────────────┐ │ │
│ │ │ IME生命周期管理 │ │ │
│ │ │ ┌─────────────┐ ┌─────────────────────────┐ │ │ │
│ │ │ │ IME绑定 │ │ 窗口目标管理 │ │ │ │
│ │ │ │ 和启动 │ │ mCurMethodTarget │ │ │ │
│ │ │ └─────────────┘ └─────────────────────────┘ │ │ │
│ │ └─────────────────────────────────────────────────┘ │ │
│ │ ┌─────────────────────────────────────────────────┐ │ │
│ │ │ 输入连接管理 │ │ │
│ │ │ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │ │ │
│ │ │ │InputBinding │ │InputConnection│ │InputContext │ │ │ │
│ │ │ │ 会话 │ │ 文本接口 │ │ 上下文 │ │ │ │
│ │ │ └─────────────┘ └─────────────┘ └─────────────┘ │ │ │
│ │ └─────────────────────────────────────────────────┘ │ │
│ └─────────────────────────────────────────────────────────┘ │
│ ┌─────────────────────────────────────────────────────────┐ │
│ │ WindowManagerService │ │
│ │ (窗口管理服务) │ │
│ │ ┌─────────────────────────────────────────────────┐ │ │
│ │ │ IME窗口管理 │ │ │
│ │ │ ┌─────────────┐ ┌─────────────────────────┐ │ │ │
│ │ │ │ 输入法目标 │ │ IME窗口层级 │ │ │ │
│ │ │ │ 窗口跟踪 │ │ 和焦点管理 │ │ │ │
│ │ │ └─────────────┘ └─────────────────────────┘ │ │ │
│ │ └─────────────────────────────────────────────────┘ │ │
│ └─────────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────┘
│
▼ (进程间通信)
┌─────────────────────────────────────────────────────────────┐
│ 输入法进程 │
│ ┌─────────────────────────────────────────────────────────┐ │
│ │ InputMethodService │ │
│ │ (输入法服务基类) │ │
│ │ ┌─────────────────────────────────────────────────┐ │ │
│ │ │ UI组件管理 │ │ │
│ │ │ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │ │ │
│ │ │ │ InputView │ │CandidatesView│ │ExtractView │ │ │ │
│ │ │ │ (键盘界面) │ │ (候选词) │ │ (提取界面) │ │ │ │
│ │ │ └─────────────┘ └─────────────┘ └─────────────┘ │ │ │
│ │ └─────────────────────────────────────────────────┘ │ │
│ │ ┌─────────────────────────────────────────────────┐ │ │
│ │ │ 事件处理 │ │ │
│ │ │ ┌─────────────┐ ┌─────────────────────────┐ │ │ │
│ │ │ │ 键盘事件 │ │ 文本提交 │ │ │ │
│ │ │ │ 处理 │ │ 和编辑操作 │ │ │ │
│ │ │ └─────────────┘ └─────────────────────────┘ │ │ │
│ │ └─────────────────────────────────────────────────┘ │ │
│ └─────────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────┘
│
▼ (输入事件)
┌─────────────────────────────────────────────────────────────┐
│ 输入系统 │
│ ┌─────────────────────────────────────────────────────────┐ │
│ │ InputManagerService │ │
│ │ (输入管理服务) │ │
│ │ ┌─────────────────────────────────────────────────┐ │ │
│ │ │ 事件分发 │ │ │
│ │ │ InputDispatcher → IME窗口 → 应用窗口 │ │ │
│ │ └─────────────────────────────────────────────────┘ │ │
│ └─────────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────┘
1. 输入法框架核心组件
1.1 InputMethodManagerService (IMMS)
文件路径: frameworks/base/services/core/java/com/android/server/InputMethodManagerService.java
/**
* InputMethodManagerService是输入法框架的核心系统服务
* 负责管理所有输入法的生命周期、绑定和切换
*/
public class InputMethodManagerService extends IInputMethodManager.Stub
implements ServiceConnection, Handler.Callback {
static final String TAG = "InputMethodManagerService";
// === 核心管理对象 ===
/** 当前绑定的输入法 */
@Nullable
InputMethodInfo mCurMethodId;
/** 当前输入法的客户端连接 */
@Nullable
ClientState mCurClient;
/** 当前输入法会话 */
@Nullable
IInputMethodSession mCurMethod;
/** 当前的输入法目标窗口 */
@Nullable
IBinder mCurMethodTarget;
/** 输入法窗口令牌 */
@Nullable
IBinder mCurToken;
/** 输入法服务接口 */
@Nullable
IInputMethod mCurMethod;
/** 输入法意图(用于绑定服务) */
@Nullable
Intent mCurIntent;
/** 所有已安装的输入法列表 */
final ArrayList<InputMethodInfo> mMethodList = new ArrayList<>();
/** 所有输入法的映射表 */
final HashMap<String, InputMethodInfo> mMethodMap = new HashMap<>();
/** 客户端状态管理 */
final HashMap<IBinder, ClientState> mClients = new HashMap<>();
// === 窗口管理相关 ===
/** 窗口管理服务接口 */
final WindowManagerInternal mWindowManagerInternal;
/** 当前显示的输入法窗口 */
final ArrayList<WindowState> mImeWindowVis = new ArrayList<>();
/** 输入法窗口是否可见 */
boolean mInputShown;
/** 输入法窗口显示模式 */
int mImeWindowVis;
// 构造函数
public InputMethodManagerService(Context context) {
mContext = context;
mRes = context.getResources();
mHandler = new Handler(this);
mCaller = new HandlerCaller(context, null, this, false /*asyncHandler*/);
mWindowManagerInternal = LocalServices.getService(WindowManagerInternal.class);
mPackageManagerInternal = LocalServices.getService(PackageManagerInternal.class);
mInputManagerInternal = LocalServices.getService(InputManagerInternal.class);
// 初始化输入法设置
mSettings = new InputMethodSettings(
mRes, context.getContentResolver(), mMethodMap, mMethodList,
context.getUserId(), !mSystemReady);
// 更新输入法列表
updateInputMethodsFromSettingsLocked(true /* enabledChanged */);
// 注册包管理器监听
IntentFilter packageFilter = new IntentFilter();
packageFilter.addAction(Intent.ACTION_PACKAGE_ADDED);
packageFilter.addAction(Intent.ACTION_PACKAGE_CHANGED);
packageFilter.addAction(Intent.ACTION_PACKAGE_REMOVED);
packageFilter.addAction(Intent.ACTION_PACKAGE_REPLACED);
packageFilter.addDataScheme("package");
mContext.registerReceiver(new ImmsBroadcastReceiver(), packageFilter);
}
}
// 客户端状态
final class ClientState {
final IInputMethodClient client; // 客户端接口
final IInputContext inputContext; // 输入上下文
final int uid; // 用户ID
final int pid; // 进程ID
final InputBinding binding; // 输入绑定
final HashMap<IBinder, SessionState> sessions = new HashMap<>(); // 会话状态
ClientState(IInputMethodClient _client, IInputContext _inputContext,
int _uid, int _pid) {
client = _client;
inputContext = _inputContext;
uid = _uid;
pid = _pid;
binding = new InputBinding(null, inputContext.asBinder(), uid, pid);
}
}
// 会话状态
final class SessionState {
final ClientState client; // 所属客户端
final IInputMethod method; // 输入法接口
final IInputMethodSession session; // 输入法会话
final InputChannel channel; // 输入通道
SessionState(ClientState _client, IInputMethod _method,
IInputMethodSession _session, InputChannel _channel) {
client = _client;
method = _method;
session = _session;
channel = _channel;
}
}
1.2 InputMethodManager (IMM)
文件路径: frameworks/base/core/java/android/view/inputmethod/InputMethodManager.java
/**
* InputMethodManager是客户端应用程序与输入法框架交互的主要接口
* 每个应用进程都有一个IMM实例
*/
public final class InputMethodManager {
static final String TAG = "InputMethodManager";
// === 核心状态 ===
/** 输入法管理服务的Binder接口 */
final IInputMethodManager mService;
/** 主线程Looper */
final Looper mMainLooper;
/** 主线程Handler */
final H mH;
/** 当前绑定的输入法服务 */
IInputMethodSession mCurMethod;
/** 当前的输入连接 */
InputConnection mCurrentTextBoxAttribute;
/** 当前获得焦点的View */
View mCurRootView;
/** 当前活动的View */
View mServedView;
/** 编辑器信息 */
EditorInfo mCurrentTextBoxAttribute;
/** 输入连接包装器 */
IInputConnectionWrapper mServedInputConnectionWrapper;
/** 完成事件计数器 */
int mRequestUpdateCursorAnchorInfoMonitorMode;
// === 客户端状态 ===
/** 当前客户端 */
final IInputMethodClient.Stub mClient = new IInputMethodClient.Stub() {
@Override
protected void dump(FileDescriptor fd, PrintWriter fout, String[] args) {
// 客户端状态转储
}
@Override
public void onBindMethod(InputBindResult res) {
// 绑定输入法回调
mH.obtainMessage(MSG_BIND, res).sendToTarget();
}
@Override
public void onUnbindMethod(int sequence, int unbindReason) {
// 解绑输入法回调
mH.obtainMessage(MSG_UNBIND, sequence, unbindReason).sendToTarget();
}
@Override
public void setActive(boolean active, boolean fullscreen) {
// 设置活动状态回调
mH.obtainMessage(MSG_SET_ACTIVE, active ? 1 : 0, fullscreen ? 1 : 0).sendToTarget();
}
@Override
public void scheduleStartInputIfNecessary() {
// 调度输入启动回调
mH.obtainMessage(MSG_START_INPUT_IF_NECESSARY).sendToTarget();
}
@Override
public void reportFullscreenMode(boolean enabled) {
// 全屏模式报告回调
mH.obtainMessage(MSG_REPORT_FULLSCREEN_MODE, enabled ? 1 : 0, 0).sendToTarget();
}
};
// 构造函数
InputMethodManager(IInputMethodManager service, Looper looper) {
mService = service;
mMainLooper = looper;
mH = new H(looper);
mIInputContext = new IInputConnectionWrapper(mMainLooper,
mDummyInputConnection, this);
if (sInstance == null) {
sInstance = this;
}
}
/**
* 显示输入法
*/
public boolean showSoftInput(View view, int flags) {
return showSoftInput(view, flags, null);
}
public boolean showSoftInput(View view, int flags, ResultReceiver resultReceiver) {
checkFocus();
synchronized (mH) {
if (mServedView != view && (mServedView == null
|| !mServedView.checkInputConnectionProxy(view))) {
return false;
}
try {
return mService.showSoftInput(mClient, flags, resultReceiver);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
}
/**
* 隐藏输入法
*/
public boolean hideSoftInputFromWindow(IBinder windowToken, int flags) {
return hideSoftInputFromWindow(windowToken, flags, null);
}
public boolean hideSoftInputFromWindow(IBinder windowToken, int flags,
ResultReceiver resultReceiver) {
checkFocus();
synchronized (mH) {
if (mServedView == null || mServedView.getWindowToken() != windowToken) {
return false;
}
try {
return mService.hideSoftInput(mClient, flags, resultReceiver);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
}
}
1.3 InputMethodService (IMS)
文件路径: frameworks/base/core/java/android/inputmethodservice/InputMethodService.java
/**
* InputMethodService提供了输入法的标准实现基类
* 具体的输入法应用继承此类并实现相关方法
*/
public class InputMethodService extends AbstractInputMethodService
implements KeyEvent.Callback {
static final String TAG = "InputMethodService";
// === UI相关常量 ===
static final int BACK_DISPOSITION_DEFAULT = 0;
static final int BACK_DISPOSITION_WILL_NOT_DISMISS = 1;
static final int BACK_DISPOSITION_WILL_DISMISS = 2;
static final int BACK_DISPOSITION_ADJUST_NOTHING = 3;
// === 核心组件 ===
/** 输入法管理器 */
InputMethodManager mImm;
/** 当前输入绑定 */
InputBinding mInputBinding;
/** 当前输入连接 */
InputConnection mInputConnection;
/** 当前输入启动状态 */
boolean mInputStarted;
/** 当前输入视图启动状态 */
boolean mInputViewStarted;
/** 候选词视图启动状态 */
boolean mCandidatesViewStarted;
// === UI组件 ===
/** 输入法窗口 */
SoftInputWindow mWindow;
/** 输入视图 */
View mInputView;
/** 候选词视图 */
View mCandidatesView;
/** 提取文本视图 */
View mExtractView;
/** 提取文本编辑器 */
ExtractEditText mExtractEditText;
/** 提取访问点 */
ViewGroup mExtractAccessories;
/** 提取操作 */
View mExtractAction;
/** 全屏模式 */
boolean mIsFullscreen;
/** 输入区域显示 */
boolean mInputViewShown;
/** 候选词区域显示 */
boolean mCandidatesViewShown;
/** 状态栏显示 */
boolean mStatusIcon;
// === 生命周期方法 ===
/**
* 初始化界面,在配置变化时调用
*/
public void onInitializeInterface() {
// 子类可重写此方法进行界面初始化
}
/**
* 绑定到新的输入目标
*/
@Override
public void onBindInput() {
// 子类可重写此方法处理绑定事件
}
/**
* 解除输入绑定
*/
@Override
public void onUnbindInput() {
// 子类可重写此方法处理解绑事件
}
/**
* 开始新的输入会话
*/
public void onStartInput(EditorInfo attribute, boolean restarting) {
// 子类可重写此方法处理输入启动
}
/**
* 重启当前输入会话
*/
public void onRestartInput(EditorInfo attribute, boolean restarting) {
// 子类可重写此方法处理输入重启
}
/**
* 输入视图启动
*/
public void onStartInputView(EditorInfo info, boolean restarting) {
// 子类可重写此方法处理输入视图启动
}
/**
* 输入视图结束
*/
public void onFinishInputView(boolean finishingInput) {
// 子类可重写此方法处理输入视图结束
}
/**
* 候选词视图启动
*/
public void onStartCandidatesView(EditorInfo info, boolean restarting) {
// 子类可重写此方法处理候选词视图启动
}
/**
* 候选词视图结束
*/
public void onFinishCandidatesView(boolean finishingInput) {
// 子类可重写此方法处理候选词视图结束
}
// === UI创建方法 ===
/**
* 创建输入视图
*/
@CallSuper
public View onCreateInputView() {
return null;
}
/**
* 创建候选词视图
*/
@CallSuper
public View onCreateCandidatesView() {
return null;
}
/**
* 创建提取文本视图
*/
@CallSuper
public View onCreateExtractTextView() {
return null;
}
// === 输入处理方法 ===
/**
* 按键处理
*/
public boolean onKeyDown(int keyCode, KeyEvent event) {
int c = event.getUnicodeChar();
if (c > 0) {
onKey(c, null);
return true;
}
return false;
}
/**
* 字符输入处理
*/
public void onKey(int primaryCode, int[] keyCodes) {
InputConnection ic = getCurrentInputConnection();
if (ic == null) return;
ic.commitText(String.valueOf((char) primaryCode), 1);
}
/**
* 文本提交
*/
public void commitText(CharSequence text, int newCursorPosition) {
InputConnection ic = getCurrentInputConnection();
if (ic == null) return;
ic.commitText(text, newCursorPosition);
}
/**
* 发送按键事件
*/
public void sendKeyChar(char charCode) {
switch (charCode) {
case '\n': // 回车
sendDefaultEditorAction(false);
break;
default:
// 发送字符
if (charCode >= '0' && charCode <= '9') {
sendDownUpKeyEvents(charCode - '0' + KeyEvent.KEYCODE_0);
} else {
InputConnection ic = getCurrentInputConnection();
if (ic != null) {
ic.commitText(String.valueOf(charCode), 1);
}
}
break;
}
}
}
2. 输入法生命周期管理
2.1 输入法绑定流程
public class InputMethodManagerService {
/**
* 开始输入法绑定流程
* 当应用获得焦点或输入法需要切换时调用
*/
InputBindResult startInputLocked(@NonNull IInputMethodClient client,
IInputContext inputContext, @NonNull EditorInfo attribute,
int startInputFlags) {
if (DEBUG) Slog.v(TAG, "startInputLocked: client=" + client.asBinder()
+ " inputContext=" + inputContext + " attribute=" + attribute);
// 检查客户端状态
ClientState cs = mClients.get(client.asBinder());
if (cs == null) {
throw new IllegalArgumentException("unknown client " + client.asBinder());
}
// 检查输入法是否需要重新绑定
if (mCurMethodId == null) {
return mNoBinding;
}
// 检查是否需要切换客户端
ClientState oldClient = mCurClient;
mCurClient = cs;
mCurInputContext = inputContext;
mCurAttribute = attribute;
// 如果输入法未绑定,开始绑定流程
if (mCurMethod == null) {
if (DEBUG) Slog.v(TAG, "Binding to method " + mCurMethodId);
return startInputInnerLocked();
}
// 输入法已绑定,直接启动输入
return attachNewInputLocked(startInputFlags);
}
/**
* 内部输入启动逻辑
*/
InputBindResult startInputInnerLocked() {
if (mCurMethodId == null) {
return mNoBinding;
}
if (!mSystemReady) {
// 系统还未准备好
return new InputBindResult(InputBindResult.ResultCode.ERROR_SYSTEM_NOT_READY,
null, null, mCurMethodId, mCurSeq, mCurUserActionNotificationSequenceNumber);
}
InputMethodInfo info = mMethodMap.get(mCurMethodId);
if (info == null) {
throw new IllegalArgumentException("Unknown id: " + mCurMethodId);
}
// 解绑当前输入法
unbindCurrentMethodLocked(true);
// 绑定新的输入法
mCurIntent = new Intent(InputMethod.SERVICE_INTERFACE);
mCurIntent.setComponent(info.getComponent());
mCurIntent.putExtra(Intent.EXTRA_CLIENT_LABEL,
com.android.internal.R.string.input_method_binding_label);
mCurIntent.putExtra(Intent.EXTRA_CLIENT_INTENT, PendingIntent.getActivity(
mContext, 0, new Intent(Settings.ACTION_INPUT_METHOD_SETTINGS), 0));
if (bindCurrentInputMethodService(mCurIntent, this, IME_CONNECTION_BIND_FLAGS)) {
mLastBindTime = SystemClock.uptimeMillis();
mHaveConnection = true;
mCurId = info.getId();
mCurToken = new Binder();
try {
if (DEBUG) Slog.v(TAG, "Adding window token: " + mCurToken);
mIWindowManager.addWindowToken(mCurToken, TYPE_INPUT_METHOD,
DEFAULT_DISPLAY);
} catch (RemoteException e) {
Slog.w(TAG, "Failed adding window token", e);
}
return new InputBindResult(InputBindResult.ResultCode.SUCCESS_WAITING_IME_BINDING,
null, null, mCurId, mCurSeq, mCurUserActionNotificationSequenceNumber);
}
return mNoBinding;
}
/**
* 服务连接回调 - 输入法服务已连接
*/
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
synchronized (mMethodMap) {
if (mCurIntent != null && name.equals(mCurIntent.getComponent())) {
mCurMethod = IInputMethod.Stub.asInterface(service);
if (mCurToken == null) {
Slog.w(TAG, "Service connected without a token!");
unbindCurrentMethodLocked(false);
return;
}
if (DEBUG) Slog.v(TAG, "Initiating attach with token: " + mCurToken);
executeOrSendMessage(mCurMethod, mCaller.obtainMessageOO(
MSG_ATTACH_TOKEN, mCurMethod, mCurToken));
if (mCurClient != null) {
clearClientSessionLocked(mCurClient);
requestClientSessionLocked(mCurClient);
}
}
}
}
/**
* 服务连接断开回调
*/
@Override
public void onServiceDisconnected(ComponentName name) {
synchronized (mMethodMap) {
if (DEBUG) Slog.v(TAG, "Service disconnected: " + name
+ " mCurIntent=" + mCurIntent);
if (mCurMethod != null && mCurIntent != null
&& name.equals(mCurIntent.getComponent())) {
clearCurMethodLocked();
// 重新绑定输入法
mLastBindTime = SystemClock.uptimeMillis();
mShowRequested = mInputShown;
mInputShown = false;
unbindCurrentClientLocked(UnbindReason.DISCONNECT_IME);
}
}
}
}
2.2 输入会话管理
public class InputMethodManagerService {
/**
* 请求客户端会话
*/
void requestClientSessionLocked(ClientState cs) {
if (!cs.sessionRequested) {
if (DEBUG) Slog.v(TAG, "Creating new session for client " + cs);
InputChannel[] channels = InputChannel.openInputChannelPair(cs.toString());
cs.sessionRequested = true;
final IInputMethodSession session = new IInputMethodSessionWrapper(
mContext, channels[1], cs.client);
final InputChannel clientChannel = channels[0];
executeOrSendMessage(mCurMethod, mCaller.obtainMessageOOO(
MSG_CREATE_SESSION, mCurMethod, clientChannel, new MethodCallback(cs, session)));
}
}
/**
* 输入法会话创建回调
*/
private class MethodCallback extends IInputSessionCallback.Stub {
private final ClientState mClientState;
private final IInputMethodSession mSession;
MethodCallback(ClientState clientState, IInputMethodSession session) {
mClientState = clientState;
mSession = session;
}
@Override
public void sessionCreated(IInputMethodSession session) {
long ident = Binder.clearCallingIdentity();
try {
synchronized (mMethodMap) {
if (mClientState.client != null) {
clearClientSessionLocked(mClientState);
mClientState.sessionRequested = false;
SessionState sessionState = new SessionState(mClientState,
mCurMethod, session, channel);
mClientState.curSession = sessionState;
sessionState.method = SessionMethodWrapper.newInstance(session);
try {
sessionState.method.initializeSession(sessionState.session,
mClientState.binding.getConnectionToken(),
mClientState.binding.getUid(),
mClientState.binding.getPid());
} catch (RemoteException e) {
Slog.w(TAG, "Session initialization failed", e);
}
}
}
} finally {
Binder.restoreCallingIdentity(ident);
}
}
}
}
3. 窗口管理与输入系统协作
3.1 输入法目标窗口管理
// WindowManagerService中的输入法窗口管理
public class WindowManagerService {
/** 当前输入法目标窗口 */
WindowState mInputMethodTarget = null;
/** 上一个输入法目标窗口 */
WindowState mLastInputMethodTargetWindow = null;
/** 输入法窗口 */
WindowState mInputMethodWindow = null;
/** 输入法对话框窗口列表 */
final ArrayList<WindowState> mInputMethodDialogs = new ArrayList<>();
/**
* 更新输入法目标窗口
* 在窗口焦点变化时调用
*/
boolean updateInputMethodTargetLocked(WindowState targetWin) {
WindowState newTarget = null;
if (targetWin == null) {
// 重新计算输入法目标
newTarget = computeInputMethodTargetLocked();
} else {
newTarget = targetWin;
}
if (DEBUG_INPUT_METHOD) {
Slog.v(TAG_WM, "Proposed new IME target: " + newTarget);
}
// 检查目标是否发生变化
if (mInputMethodTarget == newTarget) {
return false;
}
if (newTarget != null) {
AppWindowToken token = newTarget.mAppToken;
if (token != null) {
// 检查应用是否准备好接收输入法
if (token.mAppAnimator.freezingScreen) {
newTarget = null;
}
}
}
if (DEBUG_INPUT_METHOD) {
Slog.v(TAG_WM, "Final new IME target: " + newTarget);
Slog.v(TAG_WM, "Last IME target: " + mInputMethodTarget);
}
if (newTarget == mInputMethodTarget) {
return false;
}
final WindowState oldTarget = mInputMethodTarget;
mInputMethodTarget = newTarget;
// 通知输入法管理器
if (mInputMethodManagerInternal != null) {
mInputMethodManagerInternal.updateInputMethodTargetWindow(oldTarget, newTarget);
}
return true;
}
/**
* 计算输入法目标窗口
* 通常是当前焦点窗口,但也考虑特殊情况
*/
private WindowState computeInputMethodTargetLocked() {
// 首先检查当前焦点窗口
if (mCurrentFocus != null && mCurrentFocus.canReceiveKeys() &&
!mCurrentFocus.mAttrs.isModal()) {
return mCurrentFocus;
}
// 如果焦点窗口是模态的,寻找其父窗口
WindowState target = mCurrentFocus;
while (target != null) {
if (target.canBeImeTarget()) {
return target;
}
target = target.mParentWindow;
}
// 最后的备选方案
return null;
}
/**
* 设置输入法窗口状态
*/
public void setInputMethodWindowState(IBinder token, boolean visible, boolean touchable) {
synchronized (mWindowMap) {
WindowState imWindow = null;
if (token != null) {
imWindow = mTokenMap.get(token);
}
if (imWindow != null) {
if (DEBUG_INPUT_METHOD) {
Slog.v(TAG_WM, "Setting IM window state: visible=" + visible
+ " touchable=" + touchable + " " + imWindow);
}
imWindow.mPolicyVisibility = visible;
if (touchable) {
imWindow.mAttrs.flags &= ~WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE;
} else {
imWindow.mAttrs.flags |= WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE;
}
scheduleNotifyWindowTransitionIfNeededLocked(imWindow,
imWindow.mPolicyVisibility ? TRANSIT_ENTER : TRANSIT_EXIT);
updateInputMethodTargetLocked(null);
scheduleAnimationLocked();
}
}
}
}
// WindowState中的输入法相关方法
public final class WindowState {
/**
* 检查窗口是否可以成为输入法目标
*/
boolean canBeImeTarget() {
if (mIsImWindow) {
// 输入法窗口本身不能成为目标
return false;
}
final boolean windowsAreFocusable = mAppToken == null || mAppToken.windowsAreFocusable();
if (!windowsAreFocusable) {
// 如果应用窗口不可聚焦,则不能成为输入法目标
return false;
}
final int fl = mAttrs.flags & (FLAG_NOT_FOCUSABLE | FLAG_ALT_FOCUSABLE_IM);
if (fl != 0 && fl != FLAG_ALT_FOCUSABLE_IM) {
return false;
}
if (DEBUG_INPUT_METHOD) {
Slog.i(TAG_WM, "isVisibleOrAdding " + this + ": " + isVisibleOrAdding());
if (!isVisibleOrAdding()) {
Slog.i(TAG_WM, " mSurface=" + mWinAnimator.mSurfaceControl
+ " relayoutCalled=" + mRelayoutCalled + " viewVis=" + mViewVisibility
+ " policyVis=" + mPolicyVisibility + " attachHid=" + mAttachedHidden
+ " exiting=" + mAnimatingExit + " destroying=" + mDestroying);
if (mAppToken != null) {
Slog.i(TAG_WM, " mAppToken.hiddenRequested=" + mAppToken.hiddenRequested);
}
}
}
return isVisibleOrAdding();
}
}
3.2 输入事件分发协作
// InputMonitor中的输入法相关事件分发
final class InputMonitor implements InputManagerService.WindowManagerCallbacks {
/**
* 更新输入窗口信息,考虑输入法窗口
*/
public void updateInputWindowsLocked(boolean force) {
if (!force && !mUpdateInputWindowsNeeded) {
return;
}
mUpdateInputWindowsNeeded = false;
// 清空现有窗口句柄
mInputWindowHandles.clear();
final ArrayList<WindowState> windows = mService.mWindows;
// 首先添加输入法窗口(如果存在且可见)
if (mService.mInputMethodWindow != null && mService.mInputMethodWindow.isVisibleLw()) {
addInputMethodWindowToListLocked(mService.mInputMethodWindow);
}
// 添加输入法对话框窗口
for (int i = mService.mInputMethodDialogs.size() - 1; i >= 0; i--) {
WindowState dialog = mService.mInputMethodDialogs.get(i);
if (dialog.isVisibleLw()) {
addInputWindowHandleLw(dialog.mInputWindowHandle, dialog);
}
}
// 添加其他窗口
for (int i = windows.size() - 1; i >= 0; i--) {
final WindowState child = windows.get(i);
if (child == mService.mInputMethodWindow ||
mService.mInputMethodDialogs.contains(child)) {
// 跳过已经添加的输入法窗口
continue;
}
final InputChannel inputChannel = child.mInputChannel;
final InputWindowHandle inputWindowHandle = child.mInputWindowHandle;
if (inputChannel == null || inputWindowHandle == null || child.mRemoved) {
continue;
}
final boolean hasFocus = (child == mInputFocus);
final boolean isVisible = child.isVisibleLw();
addInputWindowHandleLw(inputWindowHandle, child, hasFocus, isVisible);
}
// 发送输入窗口信息给InputManager
mService.mInputManager.setInputWindows(mInputWindowHandles.toArray(
new InputWindowHandle[mInputWindowHandles.size()]));
}
/**
* 添加输入法窗口到输入窗口列表
*/
private void addInputMethodWindowToListLocked(WindowState imWindow) {
final InputWindowHandle inputWindowHandle = imWindow.mInputWindowHandle;
if (inputWindowHandle == null || imWindow.mRemoved) {
return;
}
// 设置输入法窗口特殊属性
inputWindowHandle.name = imWindow.toString();
inputWindowHandle.layoutParamsType = imWindow.mAttrs.type;
inputWindowHandle.layoutParamsFlags = imWindow.mAttrs.flags;
inputWindowHandle.visible = imWindow.isVisibleLw();
inputWindowHandle.canReceiveKeys = imWindow.canReceiveKeys();
inputWindowHandle.hasFocus = false; // 输入法窗口通常不获得焦点
inputWindowHandle.hasWallpaper = false;
inputWindowHandle.paused = false;
inputWindowHandle.layer = imWindow.mLayer;
inputWindowHandle.ownerPid = imWindow.mSession.mPid;
inputWindowHandle.ownerUid = imWindow.mSession.mUid;
inputWindowHandle.inputFeatures = imWindow.mAttrs.inputFeatures;
// 设置输入法窗口的触摸区域
final Rect frame = imWindow.mFrame;
inputWindowHandle.frameLeft = frame.left;
inputWindowHandle.frameTop = frame.top;
inputWindowHandle.frameRight = frame.right;
inputWindowHandle.frameBottom = frame.bottom;
inputWindowHandle.touchableRegion.set(frame);
mInputWindowHandles.add(inputWindowHandle);
}
}
4. 输入连接和文本操作
4.1 InputConnection接口
/**
* InputConnection接口定义了输入法与应用程序之间的文本操作协议
*/
public interface InputConnection {
/**
* 获取光标周围的文本
*/
CharSequence getTextBeforeCursor(int n, int flags);
CharSequence getTextAfterCursor(int n, int flags);
/**
* 获取当前选中的文本
*/
CharSequence getSelectedText(int flags);
/**
* 获取光标位置
*/
int getCursorCapsMode(int reqModes);
/**
* 获取提取的文本
*/
ExtractedText getExtractedText(ExtractedTextRequest request, int flags);
/**
* 删除光标周围的文本
*/
boolean deleteSurroundingText(int beforeLength, int afterLength);
boolean deleteSurroundingTextInCodePoints(int beforeLength, int afterLength);
/**
* 设置文本选择
*/
boolean setSelection(int start, int end);
/**
* 设置组合文本
*/
boolean setComposingText(CharSequence text, int newCursorPosition);
boolean setComposingRegion(int start, int end);
/**
* 完成组合文本
*/
boolean finishComposingText();
/**
* 提交文本
*/
boolean commitText(CharSequence text, int newCursorPosition);
/**
* 提交补全信息
*/
boolean commitCompletion(CompletionInfo text);
boolean commitCorrection(CorrectionInfo correctionInfo);
/**
* 发送按键事件
*/
boolean sendKeyEvent(KeyEvent event);
/**
* 清除元数据状态
*/
boolean clearMetaKeyStates(int states);
/**
* 报告全屏模式
*/
boolean reportFullscreenMode(boolean enabled);
/**
* 执行私有命令
*/
boolean performPrivateCommand(String action, Bundle data);
/**
* 请求光标更新
*/
boolean requestCursorUpdates(int cursorUpdateMode);
/**
* 获取Handler用于回调
*/
Handler getHandler();
/**
* 关闭连接
*/
void closeConnection();
/**
* 提交内容
*/
boolean commitContent(InputContentInfo inputContentInfo, int flags, Bundle opts);
}
// BaseInputConnection - InputConnection的基础实现
public class BaseInputConnection implements InputConnection {
final View mTargetView; // 目标视图
final ComposingText mComposingText; // 组合文本管理
private Object[] mDefaultComposingSpans; // 默认组合文本样式
Editable mEditable; // 可编辑文本
KeyCharacterMap mKeyCharacterMap; // 按键字符映射
BaseInputConnection(View targetView, boolean fullEditor) {
mTargetView = targetView;
mComposingText = fullEditor ? new ComposingText() : null;
}
@Override
public boolean commitText(CharSequence text, int newCursorPosition) {
if (DEBUG) Log.v(TAG, "commitText " + text);
replaceText(text, newCursorPosition, false);
sendCurrentText();
return true;
}
@Override
public boolean setComposingText(CharSequence text, int newCursorPosition) {
if (DEBUG) Log.v(TAG, "setComposingText " + text);
replaceText(text, newCursorPosition, true);
return true;
}
/**
* 替换文本的核心方法
*/
private void replaceText(CharSequence text, int newCursorPosition, boolean composing) {
final Editable content = getEditable();
if (content == null) {
return;
}
beginBatchEdit();
// 删除当前组合文本
removeComposingSpans(content);
// 计算新的光标位置
int a, b;
if (mComposingTextStart < 0) {
a = Selection.getSelectionStart(content);
b = Selection.getSelectionEnd(content);
} else {
a = mComposingTextStart;
b = mComposingTextEnd;
}
if (a < 0) a = 0;
if (b < 0) b = 0;
if (b < a) {
int tmp = a;
a = b;
b = tmp;
}
// 替换文本
if (text != null) {
content.replace(a, b, text);
if (composing) {
// 标记为组合文本
Spannable sp = (Spannable) content;
setComposingSpans(sp, a, a + text.length());
mComposingTextStart = a;
mComposingTextEnd = a + text.length();
} else {
mComposingTextStart = mComposingTextEnd = -1;
}
// 设置新的光标位置
if (newCursorPosition > 0) {
newCursorPosition += a + text.length() - 1;
} else {
newCursorPosition += a;
}
if (newCursorPosition < 0) newCursorPosition = 0;
if (newCursorPosition > content.length())
newCursorPosition = content.length();
Selection.setSelection(content, newCursorPosition);
}
endBatchEdit();
}
}
4.2 IInputContext包装器
// IInputConnectionWrapper - 跨进程InputConnection包装器
final class IInputConnectionWrapper extends IInputContext.Stub {
private static final String TAG = "IInputConnectionWrapper";
private final WeakReference<InputMethodManager> mInputMethodManager;
private final WeakReference<View> mServedView;
private final WeakReference<InputConnection> mInputConnection;
private final Looper mMainLooper;
private final Handler mH;
// 消息处理
private static final int DO_GET_TEXT_BEFORE_CURSOR = 10;
private static final int DO_GET_TEXT_AFTER_CURSOR = 20;
private static final int DO_GET_SELECTED_TEXT = 25;
private static final int DO_GET_CURSOR_CAPS_MODE = 30;
private static final int DO_GET_EXTRACTED_TEXT = 40;
private static final int DO_COMMIT_TEXT = 50;
private static final int DO_COMMIT_COMPLETION = 55;
private static final int DO_COMMIT_CORRECTION = 56;
private static final int DO_SET_SELECTION = 57;
private static final int DO_PERFORM_EDITOR_ACTION = 58;
private static final int DO_PERFORM_CONTEXT_MENU_ACTION = 59;
private static final int DO_SET_COMPOSING_TEXT = 60;
private static final int DO_SET_COMPOSING_REGION = 63;
private static final int DO_FINISH_COMPOSING_TEXT = 65;
private static final int DO_SEND_KEY_EVENT = 70;
private static final int DO_DELETE_SURROUNDING_TEXT = 80;
private static final int DO_DELETE_SURROUNDING_TEXT_IN_CODE_POINTS = 81;
private static final int DO_BEGIN_BATCH_EDIT = 90;
private static final int DO_END_BATCH_EDIT = 95;
private static final int DO_REPORT_FULLSCREEN_MODE = 100;
private static final int DO_PERFORM_PRIVATE_COMMAND = 120;
private static final int DO_CLEAR_META_KEY_STATES = 130;
private static final int DO_REQUEST_UPDATE_CURSOR_ANCHOR_INFO = 140;
private static final int DO_CLOSE_CONNECTION = 150;
private static final int DO_COMMIT_CONTENT = 160;
class MyHandler extends Handler {
MyHandler(Looper looper) {
super(looper);
}
@Override
public void handleMessage(Message msg) {
executeMessage(msg);
}
}
IInputConnectionWrapper(Looper mainLooper, InputConnection conn,
InputMethodManager inputMethodManager, View servedView) {
mInputConnection = new WeakReference<>(conn);
mMainLooper = mainLooper;
mH = new MyHandler(mMainLooper);
mInputMethodManager = new WeakReference<>(inputMethodManager);
mServedView = new WeakReference<>(servedView);
}
@Override
public void getTextBeforeCursor(int length, int flags, int seq,
IInputContextCallback callback) {
dispatchMessage(obtainMessageIISC(DO_GET_TEXT_BEFORE_CURSOR, length, flags, seq, callback));
}
@Override
public void commitText(CharSequence text, int newCursorPosition, int seq,
IInputContextCallback callback) {
dispatchMessage(obtainMessageIOSC(DO_COMMIT_TEXT, newCursorPosition,
text, seq, callback));
}
@Override
public void setComposingText(CharSequence text, int newCursorPosition, int seq,
IInputContextCallback callback) {
dispatchMessage(obtainMessageIOSC(DO_SET_COMPOSING_TEXT, newCursorPosition,
text, seq, callback));
}
void executeMessage(Message msg) {
switch (msg.what) {
case DO_GET_TEXT_BEFORE_CURSOR: {
SomeArgs args = (SomeArgs) msg.obj;
try {
InputConnection ic = getInputConnection();
if (ic == null || !isActive()) {
Log.w(TAG, "getTextBeforeCursor on inactive InputConnection");
args.callback.setTextBeforeCursor(null, args.seq);
return;
}
args.callback.setTextBeforeCursor(ic.getTextBeforeCursor(
args.argi1, args.argi2), args.seq);
} catch (RemoteException e) {
Log.w(TAG, "Got RemoteException calling setTextBeforeCursor", e);
}
break;
}
case DO_COMMIT_TEXT: {
SomeArgs args = (SomeArgs) msg.obj;
try {
InputConnection ic = getInputConnection();
if (ic == null || !isActive()) {
Log.w(TAG, "commitText on inactive InputConnection");
args.callback.commitText(args.seq);
return;
}
ic.commitText((CharSequence) args.arg1, args.argi1);
args.callback.commitText(args.seq);
} catch (RemoteException e) {
Log.w(TAG, "Got RemoteException calling commitText", e);
}
break;
}
case DO_SET_COMPOSING_TEXT: {
SomeArgs args = (SomeArgs) msg.obj;
try {
InputConnection ic = getInputConnection();
if (ic == null || !isActive()) {
Log.w(TAG, "setComposingText on inactive InputConnection");
args.callback.setComposingText(args.seq);
return;
}
ic.setComposingText((CharSequence) args.arg1, args.argi1);
args.callback.setComposingText(args.seq);
} catch (RemoteException e) {
Log.w(TAG, "Got RemoteException calling setComposingText", e);
}
break;
}
// ... 其他消息处理
}
}
private InputConnection getInputConnection() {
InputConnection ic = mInputConnection.get();
if (ic == null) {
return null;
}
return ic;
}
private boolean isActive() {
InputMethodManager imm = mInputMethodManager.get();
View servedView = mServedView.get();
return imm != null && servedView != null &&
imm.isActive(servedView) && imm.hasActiveConnection();
}
}
5. 输入法窗口管理
5.1 SoftInputWindow实现
// InputMethodService中的窗口管理
public class InputMethodService {
/**
* 软输入窗口实现
*/
public class SoftInputWindow extends Dialog {
final String mName;
final Callback mCallback;
final KeyEvent.Callback mKeyEventCallback;
final KeyEvent.DispatcherState mDispatcherState = new KeyEvent.DispatcherState();
final int mWindowType;
final int mGravity;
final boolean mTakesFocus;
private final Rect mBounds = new Rect();
SoftInputWindow(Context context, String name, int theme,
Callback callback, KeyEvent.Callback keyEventCallback,
KeyEvent.DispatcherState dispatcherState, int windowType,
int gravity, boolean takesFocus) {
super(context, theme);
mName = name;
mCallback = callback;
mKeyEventCallback = keyEventCallback;
mWindowType = windowType;
mGravity = gravity;
mTakesFocus = takesFocus;
initDockWindow();
}
@Override
public void onWindowFocusChanged(boolean hasFocus) {
super.onWindowFocusChanged(hasFocus);
mDispatcherState.reset();
}
@Override
public boolean dispatchKeyEvent(KeyEvent event) {
if (mKeyEventCallback != null && mKeyEventCallback.onKeyDown(event.getKeyCode(), event)) {
return true;
}
return super.dispatchKeyEvent(event);
}
@Override
public boolean dispatchKeyShortcutEvent(KeyEvent event) {
return super.dispatchKeyShortcutEvent(event);
}
@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
// 处理触摸事件
getWindow().getDecorView().dispatchTouchEvent(ev);
return true;
}
/**
* 初始化停靠窗口
*/
void initDockWindow() {
WindowManager.LayoutParams lp = getWindow().getAttributes();
lp.type = mWindowType;
lp.setTitle(mName);
if (mTakesFocus) {
lp.flags &= ~WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
} else {
lp.flags |= WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
}
lp.gravity = mGravity;
updateWidthHeight(lp);
getWindow().setAttributes(lp);
}
/**
* 更新窗口尺寸
*/
void updateWidthHeight(WindowManager.LayoutParams lp) {
if (lp.width != ViewGroup.LayoutParams.WRAP_CONTENT) {
if (lp.width == ViewGroup.LayoutParams.MATCH_PARENT) {
lp.width = mBounds.width();
}
}
if (lp.height != ViewGroup.LayoutParams.WRAP_CONTENT) {
if (lp.height == ViewGroup.LayoutParams.MATCH_PARENT) {
lp.height = mBounds.height();
}
}
}
}
/**
* 显示输入法窗口
*/
public void showWindow(boolean showInput) {
if (DEBUG) Log.v(TAG, "Showing window: showInput=" + showInput
+ " mShowInputRequested=" + mShowInputRequested
+ " mWindowVisible=" + mWindowVisible
+ " mInputStarted=" + mInputStarted
+ " mShowInputFlags=" + mShowInputFlags);
if (mInShowWindow) {
Log.w(TAG, "Re-entrance in to showWindow");
return;
}
try {
mWindowVisible = true;
mInShowWindow = true;
showWindowInner(showInput);
} catch (BadTokenException e) {
// 令牌可能已经无效,隐藏窗口
mWindowVisible = false;
mWindowAdded = false;
hideWindow();
throw e;
} finally {
mInShowWindow = false;
}
}
/**
* 显示窗口内部实现
*/
void showWindowInner(boolean showInput) {
boolean doShowInput = false;
final boolean wasVisible = mWindowVisible;
mWindowVisible = true;
mShowInputRequested = showInput;
if (!mWindowAdded || !mWindowCreated) {
mWindowAdded = true;
mWindowCreated = true;
initialize();
onCreateCandidatesView();
onCreateInputView();
}
if (DEBUG) Log.v(TAG, "showWindow: updating UI");
onConfigureWindow(mWindow, mInputEditorInfo, mShowInputRequested);
if (!mWindowVisible) {
mWindow.show();
mShouldClearInsetOfPreviousIme = true;
}
onWindowShown();
mWindowVisible = true;
if ((mShowInputRequested || mInputViewStarted) && mInputView != null) {
if (DEBUG) Log.v(TAG, "showWindow: showing input");
doShowInput = true;
}
if (doShowInput) {
showInputViewInner();
}
}
/**
* 隐藏输入法窗口
*/
public void hideWindow() {
if (DEBUG) Log.v(TAG, "hideWindow");
finishViews();
if (mWindowVisible) {
mWindow.hide();
mWindowVisible = false;
onWindowHidden();
mShouldClearInsetOfPreviousIme = false;
}
}
/**
* 配置窗口属性
*/
public void onConfigureWindow(Window win, boolean isFullscreen, boolean isCandidatesOnly) {
final int currentHeight = mWindow.getWindow().getDecorView().getHeight();
final int newHeight = onComputeInsets(mTmpInsets);
if (currentHeight != newHeight) {
if (DEBUG) {
Log.v(TAG, "onConfigureWindow: height changed " + currentHeight + " -> " + newHeight);
}
mWindow.getWindow().setLayout(ViewGroup.LayoutParams.MATCH_PARENT, newHeight);
}
}
}
5.2 输入法窗口层级管理
// InputMethodManagerService中的窗口显示控制
public class InputMethodManagerService {
/**
* 显示当前输入法
*/
boolean showCurrentInputLocked(int flags, ResultReceiver resultReceiver) {
mShowRequested = true;
mShowRequestedFlags = flags;
mShowForced = (flags & InputMethodManager.SHOW_FORCED) != 0;
mShowExplicitlyRequested = (flags & InputMethodManager.SHOW_IMPLICIT) == 0;
if (mCurMethod != null) {
if (DEBUG) Slog.v(TAG, "showCurrentInputLocked: mCurToken=" + mCurToken);
executeOrSendMessage(mCurMethod, mCaller.obtainMessageIOO(
MSG_SHOW_SOFT_INPUT, getImeShowFlags(), mCurMethod, resultReceiver));
mInputShown = true;
if (mHaveConnection && !mVisibleBound) {
bindCurrentInputMethodService(
mCurIntent, mVisibleConnection, VISIBLE_SERVICE_BIND_FLAGS);
mVisibleBound = true;
}
res = true;
} else if (mHaveConnection && SystemClock.uptimeMillis()
>= (mLastBindTime + TIME_TO_RECONNECT)) {
// 重新绑定输入法服务
EventLog.writeEvent(EventLogTags.IMF_FORCE_RECONNECT_IME, mCurMethodId,
SystemClock.uptimeMillis() - mLastBindTime, 0);
Slog.w(TAG, "Force disconnect/connect to the IME in showCurrentInputLocked()");
mContext.unbindService(this);
bindCurrentInputMethodService(mCurIntent, this, IME_CONNECTION_BIND_FLAGS);
} else {
if (DEBUG) {
Slog.d(TAG, "Can't show input: connection = " + mHaveConnection + ", time = "
+ ((mLastBindTime + TIME_TO_RECONNECT) - SystemClock.uptimeMillis()));
}
}
return res;
}
/**
* 隐藏当前输入法
*/
boolean hideCurrentInputLocked(int flags, ResultReceiver resultReceiver) {
if ((flags & InputMethodManager.HIDE_IMPLICIT_ONLY) != 0
&& (mShowExplicitlyRequested || mShowForced)) {
if (DEBUG) Slog.v(TAG, "Not hiding: explicit show not cancelled by non-explicit hide");
return false;
}
if (mShowForced && (flags & InputMethodManager.HIDE_NOT_ALWAYS) != 0) {
if (DEBUG) Slog.v(TAG, "Not hiding: forced show not cancelled by not-always hide");
return false;
}
boolean res;
if (mInputShown && mCurMethod != null) {
executeOrSendMessage(mCurMethod, mCaller.obtainMessageOO(
MSG_HIDE_SOFT_INPUT, mCurMethod, resultReceiver));
res = true;
} else {
res = false;
}
if (mHaveConnection && mVisibleBound) {
mContext.unbindService(mVisibleConnection);
mVisibleBound = false;
}
mInputShown = false;
mShowRequested = false;
mShowExplicitlyRequested = false;
mShowForced = false;
return res;
}
/**
* 获取输入法显示标志
*/
private int getImeShowFlags() {
int flags = 0;
if (mShowForced) {
flags |= InputMethod.SHOW_FORCED
| InputMethod.SHOW_EXPLICIT;
} else if (mShowExplicitlyRequested) {
flags |= InputMethod.SHOW_EXPLICIT;
}
return flags;
}
/**
* 设置输入法窗口状态
*/
public void setImeWindowStatus(IBinder token, IBinder startInputToken, int vis,
int backDisposition) {
if (DEBUG) Slog.v(TAG, "setImeWindowStatus: vis=" + vis
+ " backDisposition=" + backDisposition);
synchronized (mMethodMap) {
if (!calledWithValidToken(token)) {
return;
}
mImeWindowVis = vis;
mBackDisposition = backDisposition;
updateSystemUiLocked(vis, backDisposition);
}
final long ident = Binder.clearCallingIdentity();
try {
// 通知窗口管理器输入法窗口状态变化
if (mCurToken == token) {
mWindowManagerInternal.setInputMethodWindowState(token,
(vis & InputMethodService.IME_VISIBLE) != 0,
(vis & InputMethodService.IME_VISIBLE) != 0 &&
(vis & InputMethodService.IME_INPUT_SHOWN) != 0);
}
} finally {
Binder.restoreCallingIdentity(ident);
}
}
}
6. 性能优化和调试
6.1 输入法性能监控
性能指标 | 监控方法 | 优化目标 |
---|---|---|
绑定延迟 | SERVICE_CONNECT耗时 | < 200ms |
显示延迟 | showSoftInput耗时 | < 100ms |
隐藏延迟 | hideSoftInput耗时 | < 50ms |
文本提交延迟 | commitText响应时间 | < 16ms |
内存使用 | InputConnection对象数量 | 合理范围 |
6.2 调试工具和日志
// InputMethodManagerService中的调试支持
public class InputMethodManagerService {
private static final boolean DEBUG = false;
private static final boolean DEBUG_RESTORE = DEBUG || false;
private static final boolean DEBUG_FLOW = DEBUG || false;
/**
* 转储输入法状态
*/
@Override
protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
if (!DumpUtils.checkDumpPermission(mContext, TAG, pw)) return;
final Printer p = new PrintWriterPrinter(pw);
synchronized (mMethodMap) {
p.println("Current Input Method Manager state:");
int N = mMethodList.size();
p.println(" Input Methods: mMethodMapUpdateCount=" + mMethodMapUpdateCount);
for (int i = 0; i < N; i++) {
InputMethodInfo info = mMethodList.get(i);
p.println(" InputMethod #" + i + ":");
info.dump(p, " ");
}
p.println(" Clients:");
for (ClientState ci : mClients.values()) {
p.println(" Client " + ci + ":");
p.println(" sessions=" + ci.sessions.size());
p.println(" uid=" + ci.uid + " pid=" + ci.pid);
}
p.println(" mCurMethodId=" + mCurMethodId);
p.println(" mCurClient=" + mCurClient);
p.println(" mCurMethod=" + mCurMethod);
p.println(" mCurToken=" + mCurToken);
p.println(" mCurIntent=" + mCurIntent);
p.println(" mShowRequested=" + mShowRequested
+ " mShowExplicitlyRequested=" + mShowExplicitlyRequested
+ " mShowForced=" + mShowForced
+ " mInputShown=" + mInputShown);
p.println(" mSystemReady=" + mSystemReady + " mInteractive=" + mScreenOn);
p.println(" mSettingsObserver=" + mSettingsObserver);
}
}
/**
* 调试输入连接状态
*/
private void dumpInputConnection(PrintWriter pw, InputConnection ic) {
pw.println(" InputConnection:");
if (ic != null) {
try {
CharSequence before = ic.getTextBeforeCursor(10, 0);
CharSequence after = ic.getTextAfterCursor(10, 0);
CharSequence selected = ic.getSelectedText(0);
pw.println(" textBefore='" + before + "'");
pw.println(" textAfter='" + after + "'");
pw.println(" selectedText='" + selected + "'");
} catch (Exception e) {
pw.println(" Exception: " + e);
}
} else {
pw.println(" null");
}
}
}
// InputMethodManager中的调试支持
public final class InputMethodManager {
private static final boolean DEBUG = false;
/**
* 转储客户端状态
*/
public void dumpDebug(ProtoOutputStream proto, long fieldId) {
final long token = proto.start(fieldId);
synchronized (mH) {
proto.write(IMM_DISPLAY_ID, mDisplayId);
proto.write(IMM_CUR_ID, Objects.toString(mCurId));
proto.write(IMM_FULLSCREEN_MODE, mFullscreenMode);
proto.write(IMM_ACTIVE, mActive);
proto.write(IMM_SERVED_CONNECTING, mServedConnecting);
}
proto.end(token);
}
/**
* 检查焦点一致性
*/
void checkFocus() {
if (checkFocusNoStartInput(false)) {
startInputInner(StartInputReason.CHECK_FOCUS, null, 0, 0, 0);
}
}
boolean checkFocusNoStartInput(boolean forceNewSequenceNumber) {
// This is just checking that the current focus is the same as the last
// focus reported by the current active view. This is really only needed
// to catch bugs in the view hierarchy that would otherwise cause us to
// think that the focus has changed when it really hasn't -- the end result
// being that we start input on a view that doesn't really have focus.
if (mServedView == mNextServedView && !forceNewSequenceNumber) {
return false;
}
if (DEBUG) Log.v(TAG, "checkFocus: view=" + mServedView
+ " next=" + mNextServedView
+ " forceNewSequenceNumber=" + forceNewSequenceNumber
+ " package=" + (mServedView != null
? mServedView.getContext().getPackageName() : "<none>"));
if (mNextServedView == null) {
finishInputLocked();
// In this case, we used to have a focused view on the window,
// but no longer do. We should stop any input method (note that
// hasWindowFocus() should have been true and thus we should
// be stopping input as needed via the setWindowFocus() path).
closeCurrentInput();
return false;
}
final View servedView = mNextServedView;
// Focus has moved to a different view.
mServedView = servedView;
mCompletions = null;
mServedConnecting = true;
return true;
}
}
7. 总结
7.1 输入法框架核心价值
- 统一输入接口: 为所有应用提供标准化的文本输入接口
- 灵活输入法切换: 支持多种输入法的动态切换和管理
- 高效进程间通信: 通过Binder实现输入法与应用的高效交互
- 智能窗口管理: 与窗口系统深度集成的输入法窗口管理
- 丰富文本操作: 支持组合文本、自动补全、纠错等高级功能
7.2 协作机制特点
- 三层架构: 应用层、系统服务层、输入法进程的清晰分层
- 事件驱动: 基于焦点变化和用户交互的事件驱动机制
- 异步通信: 通过Handler和Binder实现的异步消息传递
- 状态同步: 多组件间的状态一致性维护
- 资源管理: 智能的输入法生命周期和资源管理
7.3 系统集成优势
输入法框架作为Android UI系统的重要组成部分:
- 与WindowManager集成: 智能的窗口焦点和层级管理
- 与InputManager协作: 高效的输入事件分发和处理
- 与ActivityManager协调: 应用生命周期感知的输入法管理
- 与PackageManager联动: 输入法应用的安装、更新和权限管理
相关文件路径
系统服务文件
frameworks/base/services/core/java/com/android/server/InputMethodManagerService.java
- 输入法管理服务frameworks/base/services/java/com/android/server/SystemServer.java
- 系统服务启动
客户端框架
frameworks/base/core/java/android/view/inputmethod/InputMethodManager.java
- 客户端输入法管理器frameworks/base/core/java/android/view/inputmethod/InputConnection.java
- 输入连接接口frameworks/base/core/java/android/inputmethodservice/InputMethodService.java
- 输入法服务基类
窗口集成
frameworks/base/services/core/java/com/android/server/wm/WindowManagerService.java
- 窗口管理服务frameworks/base/core/java/android/view/ViewRootImpl.java
- 视图根实现
输入系统集成
frameworks/base/services/core/java/com/android/server/wm/InputMonitor.java
- 输入监控器frameworks/base/services/core/java/com/android/server/input/InputManagerService.java
- 输入管理服务