Android 源码 输入系统之窗口关联

在前一节中,InputDispatcher 的事件派发工作调用 InputChannel sendMessage 方法标志着 InputDispatcher 一个周期的工作结束。但意味着事件找到对应的窗口处理的开始。

上一节中 mFocusedWindowHandle 是 InputDispatcher 的成员变量,它是确定发送事件消息的关键。我们以它为突破口寻找“出路”。mFocusedWindowHandle 在 setInputWindows 方法中进行了赋值,我们需要反推一下。

frameworks/native/services/inputflinger/InputDispatcher.cpp

void InputDispatcher::setInputWindows(const Vector<sp<InputWindowHandle> >& inputWindowHandles) {
    { // acquire lock
        AutoMutex _l(mLock);

        Vector<sp<InputWindowHandle> > oldWindowHandles = mWindowHandles;
        mWindowHandles = inputWindowHandles;

        sp<InputWindowHandle> newFocusedWindowHandle;
        bool foundHoveredWindow = false;
        for (size_t i = 0; i < mWindowHandles.size(); i++) {
            const sp<InputWindowHandle>& windowHandle = mWindowHandles.itemAt(i);
            if (!windowHandle->updateInfo() || windowHandle->getInputChannel() == NULL) {
                mWindowHandles.removeAt(i--);
                continue;
            }
            if (windowHandle->getInfo()->hasFocus) {
                // 找到焦点窗口句柄
                newFocusedWindowHandle = windowHandle;
            }
            ......
        }

        ......

        if (mFocusedWindowHandle != newFocusedWindowHandle) {
            if (mFocusedWindowHandle != NULL) {
                sp<InputChannel> focusedInputChannel = mFocusedWindowHandle->getInputChannel();
                if (focusedInputChannel != NULL) {
                    CancelationOptions options(CancelationOptions::CANCEL_NON_POINTER_EVENTS,
                            "focus left window");
                    synthesizeCancelationEventsForInputChannelLocked(
                            focusedInputChannel, options);
                }
            }
            // mFocusedWindowHandle 赋值焦点窗口句柄
            mFocusedWindowHandle = newFocusedWindowHandle;
        }

        ......
    } // release lock

    // 唤醒 Looper 循环,因为它可能需要作出新的输入派发选择。
    mLooper->wake();
}
  1. 将 windowHandleObjArray java 数组对象的每个元素转化为 InputWindowHandle native 对象;
  2. 调用 InputDispatcher 类的 setInputWindows 方法。

frameworks/base/services/core/jni/com_android_server_input_InputManagerService.cpp

void NativeInputManager::setInputWindows(JNIEnv* env, jobjectArray windowHandleObjArray) {
    Vector<sp<InputWindowHandle> > windowHandles;

    if (windowHandleObjArray) {
        jsize length = env->GetArrayLength(windowHandleObjArray);
        for (jsize i = 0; i < length; i++) {
            jobject windowHandleObj = env->GetObjectArrayElement(windowHandleObjArray, i);
            if (! windowHandleObj) {
                break; // found null element indicating end of used portion of the array
            }
            // 将数组中的 java 对象转化为 native 对象
            sp<InputWindowHandle> windowHandle =
                    android_server_InputWindowHandle_getHandle(env, windowHandleObj);
            if (windowHandle != NULL) {
                //push 到容器中
                windowHandles.push(windowHandle);
            }
            env->DeleteLocalRef(windowHandleObj);
        }
    }
    // 调用 InputDispatcher setInputWindows 方法
    mInputManager->getDispatcher()->setInputWindows(windowHandles);
    ......
}

setInputWindows 有是在 nativeSetInputWindows 中调用的。

frameworks/base/services/core/jni/com_android_server_input_InputManagerService.cpp

static void nativeSetInputWindows(JNIEnv* env, jclass /* clazz */,
        jlong ptr, jobjectArray windowHandleObjArray) {
    NativeInputManager* im = reinterpret_cast<NativeInputManager*>(ptr);

    im->setInputWindows(env, windowHandleObjArray);
}

frameworks/base/services/core/jni/com_android_server_input_InputManagerService.cpp

static JNINativeMethod gInputManagerMethods[] = {
    /* name, signature, funcPtr */
    ......
    { "nativeSetInputWindows", "(J[Lcom/android/server/input/InputWindowHandle;)V",
            (void*) nativeSetInputWindows },
    ......
};

不难知道 nativeSetInputWindows 对应 jni 中的 java 层的方法是在 InputManagerService 类中。

frameworks/base/services/core/java/com/android/server/input/InputManagerService.java

public class InputManagerService extends IInputManager.Stub
        implements Watchdog.Monitor {
    ......
    private static native void nativeSetInputWindows(long ptr, InputWindowHandle[] windowHandles);
    ......
    public void setInputWindows(InputWindowHandle[] windowHandles) {
        nativeSetInputWindows(mPtr, windowHandles);
    }
    ......
}

到这里还没有关联到对应的窗口,但可以确信的一点是,窗口和 InputDispatcher 关联,必须要调用 InputManagerService 类中的 setInputWindows 方法。下面我们正向推理,先得从 Activity setContentView 说起。

getWindow() 返回的是一个 PhoneWindow 对象,现在转入 PhoneWindow 类的 setContentView 方法。

frameworks/base/core/java/android/app/Activity.java

public class Activity extends ContextThemeWrapper
        implements LayoutInflater.Factory2,
        Window.Callback, KeyEvent.Callback,
        OnCreateContextMenuListener, ComponentCallbacks2,
        Window.OnWindowDismissedCallback {
    ......
    public void setContentView(@LayoutRes int layoutResID) {
        getWindow().setContentView(layoutResID);
        initWindowDecorActionBar();
    }
    ......
}

PhoneWindow 类的 setContentView 会调用 installDecor() 来生成 DecorView,方便后续纳入 WindowManager 管理。

frameworks/base/core/java/com/android/internal/policy/PhoneWindow.java

public class PhoneWindow extends Window implements MenuBuilder.Callback {
    ......
    @Override
    public void setContentView(int layoutResID) {
        if (mContentParent == null) {
            installDecor();
        } else if (!hasFeature(FEATURE_CONTENT_TRANSITIONS)) {
            ......
        }

        ......
    }

    ......
}

installDecor() 方法调用了 generateDecor() 生成 DecorView。

frameworks/base/core/java/com/android/internal/policy/PhoneWindow.java

public class PhoneWindow extends Window implements MenuBuilder.Callback {
    ......
    private void installDecor() {
        if (mDecor == null) {
            mDecor = generateDecor();
            ......
        }
        ......
    }
    ......
}

在 Activity 启动流程分析中,我们知道启动一个 Activity 当调用了 ActivityThread 中 handleLaunchActivity 方法后,马上就会调用 handleResumeActivity 方法,奥秘就在这里!

  1. 获取 Window 和 DecorView 对象;
  2. 设置 DecorView 为不可见;
  3. 获取 WindowManager 对象;
  4. 将 DecorView 添加到 ViewManager。

frameworks/base/core/java/android/app/ActivityThread.java

public final class ActivityThread {
    ......
    final void handleResumeActivity(IBinder token,
            boolean clearHide, boolean isForward, boolean reallyResume) {
        ......
        // TODO Push resumeArgs into the activity for consideration
        ActivityClientRecord r = performResumeActivity(token, clearHide);

        if (r != null) {
            final Activity a = r.activity;
            ......
            if (r.window == null && !a.mFinished && willBeVisible) {
                r.window = r.activity.getWindow();
                View decor = r.window.getDecorView();
                // 设置 DecorView 为不可见
                decor.setVisibility(View.INVISIBLE);
                // 获取 WindowManager 对象
                ViewManager wm = a.getWindowManager();
                WindowManager.LayoutParams l = r.window.getAttributes();
                a.mDecor = decor;
                l.type = WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
                l.softInputMode |= forwardBit;
                if (a.mVisibleFromClient) {
                    a.mWindowAdded = true;
                    // 将 DecorView 添加到 ViewManager
                    wm.addView(decor, l);
                }

            } else if (!willBeVisible) {
                ......
            }

            ......

            // 告诉活动管理器我们已经恢复。
            if (reallyResume) {
                try {
                    ActivityManagerNative.getDefault().activityResumed(token);
                } catch (RemoteException ex) {
                }
            }

        } else {
            ......
        }
    }
    ......
}

上一步 wm 局部变量实际指向 WindowManagerImpl 对象,所以实际调用了 WindowManagerImpl 类的 addView 方法。此处有调用了 WindowManagerGlobal 单例的 addView 方法。

frameworks/base/core/java/android/view/WindowManagerImpl.java

public final class WindowManagerImpl implements WindowManager {
    private final WindowManagerGlobal mGlobal = WindowManagerGlobal.getInstance();
    ......
    @Override
    public void addView(@NonNull View view, @NonNull ViewGroup.LayoutParams params) {
        applyDefaultToken(params);
        mGlobal.addView(view, params, mDisplay, mParentWindow);
    }
    ......
}
  1. 创建 ViewRootImpl 对象;
  2. 调用 ViewRootImpl 类 setView 方法。

frameworks/base/core/java/android/view/WindowManagerImpl.java

public final class WindowManagerGlobal {
    ......
    public void addView(View view, ViewGroup.LayoutParams params,
            Display display, Window parentWindow) {
        ......
        ViewRootImpl root;
        View panelParentView = null;

        synchronized (mLock) {
            ......
            root = new ViewRootImpl(view.getContext(), display);
            ......
        }

        try {
            root.setView(view, wparams, panelParentView);
        } catch (RuntimeException e) {
            ......
        }
    }
    ......
}
  1. 创建 InputChannel 对象;
  2. 远程调用 Session addToDisplay 方法;
  3. 创建接收输入事件者 WindowInputEventReceiver。

frameworks/base/core/java/android/view/ViewRootImpl.java

public final class ViewRootImpl implements ViewParent,
        View.AttachInfo.Callbacks, HardwareRenderer.HardwareDrawCallbacks {
    ......
    public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) {
        synchronized (this) {
            if (mView == null) {
                mView = view;
                ......
                if ((mWindowAttributes.inputFeatures
                        & WindowManager.LayoutParams.INPUT_FEATURE_NO_INPUT_CHANNEL) == 0) {
                    mInputChannel = new InputChannel();
                }
                try {
                    ......
                    res = mWindowSession.addToDisplay(mWindow, mSeq, mWindowAttributes,
                            getHostVisibility(), mDisplay.getDisplayId(),
                            mAttachInfo.mContentInsets, mAttachInfo.mStableInsets,
                            mAttachInfo.mOutsets, mInputChannel);
                } catch (RemoteException e) {
                    ......
                } finally {
                    ......
                }
                ......
                if (mInputChannel != null) {
                    if (mInputQueueCallback != null) {
                        mInputQueue = new InputQueue();
                        mInputQueueCallback.onInputQueueCreated(mInputQueue);
                    }
                    // 接收输入事件
                    mInputEventReceiver = new WindowInputEventReceiver(mInputChannel,
                            Looper.myLooper());
                }
                ......
            }
        }
    }
    ......
}

我们重点关注 Session 类 addToDisplay 方法。

Session 类表示活动的客户端会话。通常,每个与窗口管理器交互的进程都有一个 Session 对象。

addToDisplay 方法实际工作由 WindowManagerService 类 addWindow 方法完成。

frameworks/base/services/core/java/com/android/server/wm/Session.java

final class Session extends IWindowSession.Stub
        implements IBinder.DeathRecipient {
    final WindowManagerService mService;
    ......
    @Override
    public int addToDisplay(IWindow window, int seq, WindowManager.LayoutParams attrs,
            int viewVisibility, int displayId, Rect outContentInsets, Rect outStableInsets,
            Rect outOutsets, InputChannel outInputChannel) {
        return mService.addWindow(this, window, seq, attrs, viewVisibility, displayId,
                outContentInsets, outStableInsets, outOutsets, outInputChannel);
    }
    ......
}

addWindow 方法首先调用 setUpdateInputWindowsNeededLw() 方法将 InputMonitor 类中的成员变量 mUpdateInputWindowsNeeded 置为 true,然后调用其 updateInputWindowsLw 方法。

frameworks/base/services/core/java/com/android/server/wm/WindowManagerService.java

public class WindowManagerService extends IWindowManager.Stub
        implements Watchdog.Monitor, WindowManagerPolicy.WindowManagerFuncs {
    ......
    final InputMonitor mInputMonitor = new InputMonitor(this);
    ......
    public int addWindow(Session session, IWindow client, int seq,
            WindowManager.LayoutParams attrs, int viewVisibility, int displayId,
            Rect outContentInsets, Rect outStableInsets, Rect outOutsets,
            InputChannel outInputChannel) {
        ......
        synchronized(mWindowMap) {
            ......
            mInputMonitor.setUpdateInputWindowsNeededLw();
            ......
            // 更新当前焦点窗口
            if (focusChanged) {
                mInputMonitor.setInputFocusLw(mCurrentFocus, false /*updateInputWindows*/);
            }
            mInputMonitor.updateInputWindowsLw(false /*force*/);
            ......
        }
        ......
    }
    ......
}

updateInputWindowsLw 中只要 force 和 mUpdateInputWindowsNeeded 有一个为 true,就会继续执行下面的代码,由于上一步设置了 mUpdateInputWindowsNeeded 为 true,因此一定会向下执行,所以接着就会运行 WindowManagerService 类成员变量 mInputManager 的 setInputWindows 方法。实际上就是调用 InputManagerService 类 setInputWindows 方法。这就回到了我们起初逆向分析的地方了,打通了整个窗口关联的过程。

frameworks/base/services/core/java/com/android/server/wm/InputMonitor.java

final class InputMonitor implements InputManagerService.WindowManagerCallbacks {
    private final WindowManagerService mService;
    ......
    public void updateInputWindowsLw(boolean force) {
        if (!force && !mUpdateInputWindowsNeeded) {
            return;
        }
        mUpdateInputWindowsNeeded = false;
        ......
        mService.mInputManager.setInputWindows(mInputWindowHandles);
        ......
    }
    ......
}

下面是整个流程的时序图。
在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

TYYJ-洪伟

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值