(笔记)输入法框架协作机制深度分析

发布于:2025-08-29 ⋅ 阅读:(16) ⋅ 点赞:(0)

概述

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 输入法框架核心价值

  1. 统一输入接口: 为所有应用提供标准化的文本输入接口
  2. 灵活输入法切换: 支持多种输入法的动态切换和管理
  3. 高效进程间通信: 通过Binder实现输入法与应用的高效交互
  4. 智能窗口管理: 与窗口系统深度集成的输入法窗口管理
  5. 丰富文本操作: 支持组合文本、自动补全、纠错等高级功能

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 - 输入管理服务

网站公告

今日签到

点亮在社区的每一天
去签到