Android9.0输入法框架(1)

本文介绍了Android系统中输入法的管理和服务运行机制,重点讲解了InputMethodManagerService(IMMS)如何负责输入法的加载与切换。当程序获取焦点时,InputMethodManager向IMMS请求绑定当前输入法,并在需要输入的View获得焦点时显示输入法。文章详细阐述了从程序创建InputMethodManager实例到输入法显示的全过程,包括启动输入法服务、绑定输入法窗口token、创建连接会话等步骤。
摘要由CSDN通过智能技术生成

Android系统中,输入法可以是可以安装的,也就是说系统可以有多个输入法((sougou输入法,百度输入法),但是只有一个是激活的,当然用户可以切换输入法。同时,输入法是以service的方式运行的,输入法同一时间只能服务一个程序,只有最顶层的可见的程序才能接收到输入法的输入数据。

输入法系统的整个框架
在这里插入图片描述
InputMethodManagerService(IMMS)负责管理系统的所有输入法,包括输入法service(InputMethodService简称IMS)加载及切换。程序获得焦点时,就会通过InputMethodManager向InputMethodManagerService通知自己获得焦点并请求绑定自己到当前输入法上。同时,当程序的某个需要输入法的view比如EditorView获得焦点时就会通过InputMethodManager向InputMethodManagerService请求显示输入法,而这时InputMethodManagerService收到请求后,会将请求的EditText的数据通信接口发送给当前输入法,并请求显输入法。输入法收到请求后,就显示自己的UI dialog,同时保存目标view的数据结构,当用户实现输入后,直接通过view的数据通信接口将字符传递到对应的View。接下来就来分析这些过程。

InputMethodManager创建
每个程序有一个InputMethodManager实例,这个是程序和InputMethodManagerService通信的接口,该实例在ViewRootImpl初始化的时候创建。

    public ViewRootImpl(Context context, Display display) {
        mContext = context;
GORDON
        mWindowSession = WindowManagerGlobal.getWindowSession();

   public static IWindowSession getWindowSession() {
        synchronized (WindowManagerGlobal.class) {
            if (sWindowSession == null) {
                try {
GORDON 这个进程的InputMethodManager实例就生成了 
                    InputMethodManager imm = InputMethodManager.getInstance();
                    IWindowManager windowManager = getWindowManagerService();
                    sWindowSession = windowManager.openSession(
                            new IWindowSessionCallback.Stub() {
                                @Override
                                public void onAnimatorScaleChanged(float scale) {
                                    ValueAnimator.setDurationScale(scale);
                                }
                            },
                            imm.getClient(), imm.getInputContext());
                } catch (RemoteException e) {
                    throw e.rethrowFromSystemServer();
                }
            }
            return sWindowSession;
        }
    }

    public static InputMethodManager getInstance() {
        synchronized (InputMethodManager.class) {
            if (sInstance == null) {
                try {
                    sInstance = new InputMethodManager(Looper.getMainLooper());
                } catch (ServiceNotFoundException e) {
                    throw new IllegalStateException(e);
                }
            }
            return sInstance;
        }
    }

    InputMethodManager(Looper looper) throws ServiceNotFoundException {
GORDON  InputMethodManager其实就是一个Binder InputMethodManagerService的proxy  
        this(IInputMethodManager.Stub.asInterface(
                ServiceManager.getServiceOrThrow(Context.INPUT_METHOD_SERVICE)), looper);
    }

Client程序的Window获得焦点
时序图
在这里插入图片描述
系统WindowManagerService更新焦点window
哪个程序获得焦点是由系统决定的,是由WindowManagerService决定的,当系统的window状态发生变化时(比如window新增,删除)就会调用函数updateFocusedWindowLocked来更新焦点window。
WindowManagerService.java

// TODO: Move to DisplayContent
boolean updateFocusedWindowLocked(int mode, boolean updateInputWindows) {
GORDON 计算焦点
    WindowState newFocus = mRoot.computeFocusedWindow();
    if (mCurrentFocus != newFocus) {
        Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "wmUpdateFocus");
        // This check makes sure that we don't already have the focus
        // change message pending.
        mH.removeMessages(H.REPORT_FOCUS_CHANGE);
GORDON 焦点发现改变时发送message REPORT_FOCUS_CHANGE
        mH.sendEmptyMessage(H.REPORT_FOCUS_CHANGE);

WindowState computeFocusedWindow() {
    // While the keyguard is showing, we must focus anything besides the main display.
    // Otherwise we risk input not going to the keyguard when the user expects it to.
    final boolean forceDefaultDisplay = mService.isKeyguardShowingAndNotOccluded();
    for (int i = mChildren.size() - 1; i >= 0; i--) {
        final DisplayContent dc = mChildren.get(i);
        GORDON
        final WindowState win = dc.findFocusedWindow();
        if (win != null) {
            if (forceDefaultDisplay && !dc.isDefaultDisplay) {
                EventLog.writeEvent(0x534e4554, "71786287", win.mOwnerUid, "");
                continue;
            }
            return win;
        }
    }
    return null;
}

WindowState findFocusedWindow() {
    mTmpWindow = null;
    GORDON
    forAllWindows(mFindFocusedWindow, true /* traverseTopToBottom */);
    if (mTmpWindow == null) {
        if (DEBUG_FOCUS_LIGHT) Slog.v(TAG_WM, "findFocusedWindow: No focusable windows.");
        return null;
    }
    GORDON  mTmpWindow为找到的焦点window
    return mTmpWindow;
}

@Override
boolean forAllWindows(ToBooleanFunction<WindowState> callback, boolean traverseTopToBottom) {
    // Special handling so we can process IME windows with #forAllImeWindows above their IME
    // target, or here in order if there isn't an IME target.
    if (traverseTopToBottom) {
        for (int i = mChildren.size() - 1; i >= 0; --i) {
            final DisplayChildWindowContainer child = mChildren.get(i);
            if (skipTraverseChild(child)) {
                continue;
            }

            if (child.forAllWindows(callback, traverseTopToBottom)) {
                return true;
            }
        }
    } else {
        final int count = mChildren.size();
        for (int i = 0; i < count; i++) {
            final DisplayChildWindowContainer child = mChildren.get(i);
            if (skipTraverseChild(child)) {
                continue;
            }

            if (child.forAllWindows(callback, traverseTopToBottom)) {
                return true;
            }
        }
    }
    return false;
}

系统通知程序端哪个window获得了焦点

final class H extends android.os.Handler {
    @Override
    public void handleMessage(Message msg) {

        switch (msg.what) {
            case REPORT_FOCUS_CHANGE: {
                WindowState lastFocus;
                WindowState newFocus;

                AccessibilityController accessibilityController = null;
                synchronized(mWindowMap) {
                    // TODO(multidisplay): Accessibility supported only of default desiplay.
                    if (mAccessibilityController != null && getDefaultDisplayContentLocked()
                            .getDisplayId() == DEFAULT_DISPLAY) {
                        accessibilityController = mAccessibilityController;
                    }

                    lastFocus = mLastFocus;
                    newFocus = mCurrentFocus;

                // First notify the accessibility manager for the change so it has
                // the windows before the newly focused one starts firing eventgs.
                if (accessibilityController != null) {
                    accessibilityController.onWindowFocusChangedNotLocked();
                }

                //System.out.println("Changing focus from " + lastFocus + " to " + newFocus);
                if (newFocus != null) {
                    if (DEBUG_FOCUS_LIGHT) Slog.i(TAG_WM, "Gaining focus: " + newFocus);
                    GORDON 通知新的焦点程序获得了焦点
                    newFocus.reportFocusChangedSerialized(true, mInTouchMode);
                    notifyFocusChanged();
                }

                if (lastFocus != null) {
                    if (DEBUG_FOCUS_LIGHT) Slog.i(TAG_WM, "Losing focus: " + lastFocus);
                    GORDON 通知旧的焦点程序失去了焦点
                    lastFocus.reportFocusChangedSerialized(false, mInTouchMode);
                }
            } break;

void reportFocusChangedSerialized(boolean focused, boolean inTouchMode) {
    try {
    GORDON 会调回到ViewRootImpl中的W实例
        mClient.windowFocusChanged(focused, inTouchMode);
    } catch (RemoteException e) {
    }
    if (mFocusCallbacks != null) {
        final int N = mFocusCallbacks.beginBroadcast();
        for (int i=0; i<N; i++) {
            IWindowFocusObserver obs = mFocusCallbacks.getBroadcastItem(i);
            try {
                if (focused) {
                    obs.focusGained(mWindowId.asBinder());
                } else {
                    obs.focusLost(mWindowId.asBinder());
                }
            } catch (RemoteException e) {
            }
        }
        mFocusCallbacks.finishBroadcast();
    }
}

Client程序获得焦点改变事件

public void windowFocusChanged(boolean hasFocus, boolean inTouchMode) {
    synchronized (this) {
        mWindowFocusChanged = true;
        mUpcomingWindowFocus = hasFocus;
        mUpcomingInTouchMode = inTouchMode;
    }
    Message msg = Message.obtain();
    msg.what = MSG_WINDOW_FOCUS_CHANGED;
    mHandler.sendMessage(msg);
}

@Override
public void handleMessage(Message msg) {
    switch (msg.what) {
    ...
      case MSG_WINDOW_FOCUS_CHANGED: {
          handleWindowFocusChanged();
      } break;

p
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值