原计划 input 输入事件的学习分为两节内容学习并记录,经学习发现并远不止这些内容,所以决定重新写 input 输入事件番外篇,如需参考,请阅读 input 输入事件番外篇;造成的不便,深表抱歉。
1. Window 的创建和 WMS的绑定
上一节中讲到 dispatchMotionLocked() 向目标窗口分发事件,这里简单介绍一下目标窗口是如何获取和绑定的;在 Activity 的启动流程中(具体可以参考 笔记14):
(1)handleLaunchActivity() 调用了 performLaunchActivity() ->
(2)performLaunchActivity() 中调用了 activity.attach()方法,之前就说过这个是对 activity进行绑定,完成这一步activity才成为四大组件之一,未完成时都只能算一个对象(创建activity的第一步) ->
(3)handleLaunchActivity() 调用了 handleResumeActivity() 方法(第二步:onResume()并渲染);
窗口的获取和绑定就在 performLaunchActivity() 调用的 activity.attach() 中完成的,进入源码中看看:
final void attach(Context context, ActivityThread aThread,
Instrumentation instr, IBinder token, int ident,
Application application, Intent intent, ActivityInfo info,
CharSequence title, Activity parent, String id,
NonConfigurationInstances lastNonConfigurationInstances,
Configuration config, IVoiceInteractor voiceInteractor) {
attachBaseContext(context);
mFragments.attachActivity(this, mContainer, null);
// 创建 Window
// 这里的 mWindow 时 PhoneWindow,后续版本的 mWindow 初始化如下,更加直观:
// mWindow = new PhoneWindow(this, window, activityConfigCallback);
mWindow = PolicyManager.makeNewWindow(this);
mWindow.setCallback(this);
mWindow.setOnWindowDismissedCallback(this);
mWindow.getLayoutInflater().setPrivateFactory(this);
// ...
// 在window中创建时 mWindowManager 其实是 WindowManagerImpl,代码不复杂,可以跟进去看看
mWindow.setWindowManager( // 设置 WindowManager
(WindowManager)context.getSystemService(Context.WINDOW_SERVICE),
mToken, mComponent.flattenToString(),
(info.flags & ActivityInfo.FLAG_HARDWARE_ACCELERATED) != 0);
if (mParent != null) {
mWindow.setContainer(mParent.getWindow());
}
mWindowManager = mWindow.getWindowManager(); // 这里就可以获取 WindowManager 了
mCurrentConfig = config;
}
在这里记住三个知识点(后续讲setContentView时再详细分析):
a. Window 类是一个抽象类,它的唯一实现类是 PhoneWindow;
b. PhoneWindow 有一个内部类 DecorView,DecorView 是 Activity 的根 View;
c. DecorView 继承自 FramLayout;
关于创建Window对象:
PolicyManager为策略类,其实现类Policy 的makeNewWindow内部创建了window对象;
// mWindow = PolicyManager.makeNewWindow(this):
public final class PolicyManager {
private static final String POLICY_IMPL_CLASS_NAME =
"com.android.internal.policy.impl.Policy";
private static final IPolicy sPolicy;
static { // 通过反射创建 sPolicy
try {
Class policyClass = Class.forName(POLICY_IMPL_CLASS_NAME);
sPolicy = (IPolicy)policyClass.newInstance();
} catch (ClassNotFoundException ex) {
// ...
}
}
private PolicyManager() {}
// 创建PhoneWindow
public static Window makeNewWindow(Context context) {
return sPolicy.makeNewWindow(context);
}
public static LayoutInflater makeNewLayoutInflater(Context context) {
return sPolicy.makeNewLayoutInflater(context);
}
public static WindowManagerPolicy makeNewWindowManager() {
return sPolicy.makeNewWindowManager();
}
public static FallbackEventHandler makeNewFallbackEventHandler(Context context) {
return sPolicy.makeNewFallbackEventHandler(context);
}
}
// Policy.java 类中的 makeNewWindow() 方法:
public window makeNewWindow(Context context){
return new PhoneWindow(context);
}
2. ViewRootImpl 与 WMS 的通信
// 第二步:
final void handleResumeActivity(IBinder token,
boolean clearHide, boolean isForward, boolean reallyResume) {
// 主要作用是调用performResumeActivity()到activity的onResume状态,然后获取
// DecorView,创建一个关联的ViewRootImpl对象,用来配合WindowManagerService
// 服务来管理该Activity组件的窗口状态,最后addView
ActivityClientRecord r = performResumeActivity(token, clearHide);
if (r != null) {
final Activity a = r.activity;
// ...
//activity创建成功,window此时为空,进入此分支;
if (r.window == null && !a.mFinished && willBeVisible) {
r.window = r.activity.getWindow();
View decor = r.window.getDecorView();
decor.setVisibility(View.INVISIBLE);
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;
// 一层层的看最终调用的是:WindowManagerGlobal.java -> addView()
wm.addView(decor, l); // 这里就是测量,摆放,绘制
}
} else if (!willBeVisible) {
r.hideForNow = true;
}
// Get rid of anything left hanging around.
cleanUpPendingRemoveWindows(r);
// ...
}
}
2.1 wm.addView(decor, l):这里的 wm 是 WindowManagerImpl,查看一下 addView() 方法:
// WindowManagerImpl.addView():
// 单例获取 WindowManagerGlobal
private final WindowManagerGlobal mGlobal = WindowManagerGlobal.getInstance();
@Override
public void addView(View view, ViewGroup.LayoutParams params) {
mGlobal.addView(view, params, mDisplay, mParentWindow);
}
//WindowManagerGlobal.addView():
public void addView(View view, ViewGroup.LayoutParams params,
Display display, Window parentWindow) {
// ... 省略部分代码:参数的校验
final WindowManager.LayoutParams wparams = (WindowManager.LayoutParams)params;
// ...
ViewRootImpl root;
View panelParentView = null;
//...
// 实例化ViewRootImpl
root = new ViewRootImpl(view.getContext(), display);
view.setLayoutParams(wparams);
// 添加 view 到全局集合中
// 如果想 hook 全部的 view 时,可以通过反射获取 WindowManagerGlobal -> mViews;
mViews.add(view);
mRoots.add(root);
mParams.add(wparams);
}
// do this last because it fires off messages to start doing things
try { //将view添加到ViewRootImpl中去
root.setView(view, wparams, panelParentView);
} catch (RuntimeException e) {
// ...
}
}
ViewRootImpl.setView():
public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) {
synchronized (this) {
// 这里先将 mView 保存了 DecorView 的实例,
// 然后调用 requestLayout() 方法,以完成应用程序用户界面的初次布局。
if (mView == null) {
mView = view;
// mWindowAttributes保存了窗口所对应的LayoutParams
mWindowAttributes.copyFrom(attrs);
/**
* 在添加窗口之前,先通过requestLayout方法在主线程上安排一次“遍历”。
* 所谓“遍历”是指ViewRootImpl中的核心方法performTraversal()。
* 这个方法实现对控件树进行测量、布局、向WMS申请修改窗口属性以及重绘的所有工作。
*/
requestLayout();
/***初始化mInputChannel。InputChannel是窗口接受来自InputDispatcher 的输入事件的管道。
注意,仅当窗口的属性inputFeatures不含有 INPUT_FEATURE_NO_INPUT_CHANNEL时才
会创建 InputChannel,否则mInputChannel 为空,从而导致此窗口无法接受任何输入事件 */
if ((mWindowAttributes.inputFeatures
& WindowManager.LayoutParams.INPUT_FEATURE_NO_INPUT_CHANNEL) == 0) {
mInputChannel = new InputChannel(); // 见 2.2
}
try {
// ...
/* 将窗口添加到WMS中。完成这个操作之后,mWindow已经被添加到指定的Display中去
而且mInputChannel(如果不为空)已经准备好接受事件了。只是由于这个窗口没有进行
过relayout(),因此它还没有有效的Surface可以进行绘制 */
res = mWindowSession.addToDisplay(mWindow, mSeq, mWindowAttributes,
getHostVisibility(), mDisplay.getDisplayId(),
mAttachInfo.mContentInsets, mInputChannel); // 见2.3
// 这里的 mWindowSession 是向WMS跨进程请求获取的
} catch (RemoteException e) {
// ...
} finally {
// ...
}
}
}
2.2 InputChannel的构造函数:此时 mInputChannel = new InputChannel() 这里还是一个 Java 的对象;
public final class InputChannel implements Parcelable {
private static final String TAG = "InputChannel";
@SuppressWarnings("unused")
private long mPtr; // used by native code
private static native InputChannel[] nativeOpenInputChannelPair(String name);
private native void nativeDispose(boolean finalized);
private native void nativeTransferTo(InputChannel other);
private native void nativeReadFromParcel(Parcel parcel);
private native void nativeWriteToParcel(Parcel parcel);
private native void nativeDup(InputChannel target);
private native String nativeGetName();
// 构造函数中没有任何操作,此时 mInputChannel = new InputChannel() 只是一个普通的java对象;
// 那么要想具有 c++ 的属性,唯一的方法就是持有c++对象的指针,也就是将 mPtr 赋值;
public InputChannel() {
}
// ...
}
2.3 mWindowSession.addToDisplay():
首先,mWindowSession 是如何获取到的:
// mWindowSession 的初始化:ViewRootImpl 的构造函数中进行初始化的;
public ViewRootImpl(Context context, Display display) {
mContext = context;
mWindowSession = WindowManagerGlobal.getWindowSession(); // 通过 WMS 获取
}
// 通过 WMS 获取 mWindowSession:WindowManagerGlobal类中的 getWindowSession() 方法
public static IWindowSession getWindowSession() {
synchronized (WindowManagerGlobal.class) {
if (sWindowSession == null) {
try {
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());
ValueAnimator.setDurationScale(windowManager.getCurrentAnimatorScale());
} catch (RemoteException e) {
Log.e(TAG, "Failed to open window session", e);
}
}
return sWindowSession;
}
}
// WMS 中openSession():
//IWindowSession :一个aidl接口,它的真的实现类是Session,它是一个Binder对象,用来和
//WindowManagerService建立连接,在ViewRootImpl的setView中最终也是通过它和WindowManagerService
//通信完成了Window的添加的。这个Session是应用唯一的,它的创建时在WindowManagerGloable中通过getWindowSession获取的
@Override
public IWindowSession openSession(IWindowSessionCallback callback, IInputMethodClient client,
IInputContext inputContext) {
if (client == null) throw new IllegalArgumentException("null client");
if (inputContext == null) throw new IllegalArgumentException("null inputContext");
Session session = new Session(this, callback, client, inputContext);
return session;
}
这里创建了 Session 对象,其参数 this 就是 WMS;
mWindowSession.addToDisplay() 就是 调用了Session的 addToDisplay() 方法:
// Session 的 addToDisplay() 方法:又是跨进程通讯
@Override
public int addToDisplay(IWindow window, int seq, WindowManager.LayoutParams attrs,
int viewVisibility, int displayId, Rect outContentInsets,
InputChannel outInputChannel) {
// outInputChannel 是 2.2 中 mInputChannel = new InputChannel(),此时的指针还未赋值
// mService 就是 new Session 时传入的 WMS
return mService.addWindow(this, window, seq, attrs, viewVisibility, displayId,
outContentInsets, outInputChannel);
}
WMS的addWindow() 方法:
public int addWindow(Session session, IWindow client, int seq,
WindowManager.LayoutParams attrs, int viewVisibility, int displayId,
Rect outContentInsets, InputChannel outInputChannel) {
int[] appOp = new int[1];
int res = mPolicy.checkAddPermission(attrs, appOp);
if (res != WindowManagerGlobal.ADD_OKAY) {
return res;
}
boolean reportNewConfig = false;
WindowState attachedWindow = null;
WindowState win = null; // window 对象的信息
long origId;
final int type = attrs.type;
synchronized(mWindowMap) {
// ...
win = new WindowState(this, session, client, token,
attachedWindow, appOp[0], seq, attrs, viewVisibility, displayContent);
// ...
if (outInputChannel != null && (attrs.inputFeatures
& WindowManager.LayoutParams.INPUT_FEATURE_NO_INPUT_CHANNEL) == 0) {
String name = win.makeInputChannelName();
// 关键代码,打开一对 InputChannel,客户端和服务端 见 2.3.1
InputChannel[] inputChannels = InputChannel.openInputChannelPair(name);
// WMS 设置 Channel 为 inputChannels[0]
win.setInputChannel(inputChannels[0]);
// 客户端设置 Channel 为 inputChannels[1],此时给上面 Java层的 mInputChannel 中 mPtr 赋值
// 经过赋值后,Java 层 mInputChannel 才具有 c++ 的对象
inputChannels[1].transferTo(outInputChannel);
// 将服务端的socket注册到InputDispatcher中 见 2.3.2
// 这里的 win.mInputWindowHandle 是在 win 初始化的时候 new 出来的;
mInputManager.registerInputChannel(win.mInputChannel, win.mInputWindowHandle);
}
// ...
// 每次添加都会更新,见 2.3.3
mInputMonitor.updateInputWindowsLw(false /*force*/);
// ...
}
// ...
}
2.3.1 建立socket 通信:
InputChannel.openInputChannelPair(name) 是一个native方法,在 frameworks/base/core/jni/
android_view_InputChannel.cpp中:
static jobjectArray android_view_InputChannel_nativeOpenInputChannelPair(JNIEnv* env,
jclass clazz, jstring nameObj) {
const char* nameChars = env->GetStringUTFChars(nameObj, NULL);
String8 name(nameChars);
env->ReleaseStringUTFChars(nameObj, nameChars);
sp<InputChannel> serverChannel;
sp<InputChannel> clientChannel;
// 创建一对 socket 通信
status_t result = InputChannel::openInputChannelPair(name, serverChannel, clientChannel);
if (result) {
String8 message;
message.appendFormat("Could not open input channel pair. status=%d", result);
jniThrowRuntimeException(env, message.string());
return NULL;
}
// 封装成java对象
jobjectArray channelPair = env->NewObjectArray(2, gInputChannelClassInfo.clazz, NULL);
if (env->ExceptionCheck()) {
return NULL;
}
jobject serverChannelObj = android_view_InputChannel_createInputChannel(env,
new NativeInputChannel(serverChannel));
if (env->ExceptionCheck()) {
return NULL;
}
jobject clientChannelObj = android_view_InputChannel_createInputChannel(env,
new NativeInputChannel(clientChannel));
if (env->ExceptionCheck()) {
return NULL;
}
env->SetObjectArrayElement(channelPair, 0, serverChannelObj);
env->SetObjectArrayElement(channelPair, 1, clientChannelObj);
return channelPair;
}
InputChannel.cpp中:
status_t InputChannel::openInputChannelPair(const String8& name,
sp<InputChannel>& outServerChannel, sp<InputChannel>& outClientChannel) {
int sockets[2];
if (socketpair(AF_UNIX, SOCK_SEQPACKET, 0, sockets)) {
status_t result = -errno;
// ...
String8 serverChannelName = name;
serverChannelName.append(" (server)");
outServerChannel = new InputChannel(serverChannelName, sockets[0]);
String8 clientChannelName = name;
clientChannelName.append(" (client)");
outClientChannel = new InputChannel(clientChannelName, sockets[1]);
return OK;
}
socketpair()函数用于创建一对无名的、相互连接的套接字。
如果函数成功,则返回0,创建好的套接字分别是sv[0]和sv[1];否则返回-1,错误码保存于errno中。
用法:
(1) 这对套接字可以用于全双工通信,每一个套接字既可以读也可以写。例如,可以往sv[0]中写,从sv[1]中读;或者从sv[1]中写,从sv[0]中读;
(2) 如果往一个套接字(如sv[0])中写入后,再从该套接字读时会阻塞,只能在另一个套接字中(sv[1])上读成功;
(3)读、写操作可以位于同一个进程,也可以分别位于不同的进程,如父子进程。如果是父子进程时,一般会功能分离,一个进程用来读,一个用来写。因为文件描述符sv[0]和sv[1]是进程共享的,所以读的进程要关闭写描述符, 反之,写的进程关闭读描述符。
2.3.2 注册:frameworks/base/services/core/jni/
com_android_server_input_InputManagerService.cpp 中:
// nativeRegisterInputChannel():
static void nativeRegisterInputChannel(JNIEnv* env, jclass clazz,
jlong ptr, jobject inputChannelObj, jobject inputWindowHandleObj, jboolean monitor) {
NativeInputManager* im = reinterpret_cast<NativeInputManager*>(ptr);
sp<InputChannel> inputChannel = android_view_InputChannel_getInputChannel(env,
inputChannelObj);
if (inputChannel == NULL) {
throwInputChannelNotInitialized(env);
return;
}
// window 的一些信息
sp<InputWindowHandle> inputWindowHandle =
android_server_InputWindowHandle_getHandle(env, inputWindowHandleObj);
status_t status = im->registerInputChannel( // 注册的方法,如下 registerInputChannel()方法:
env, inputChannel, inputWindowHandle, monitor);
if (status) {
String8 message;
message.appendFormat("Failed to register input channel. status=%d", status);
jniThrowRuntimeException(env, message.string());
return;
}
if (! monitor) {
android_view_InputChannel_setDisposeCallback(env, inputChannelObj,
handleInputChannelDisposed, im);
}
}
// registerInputChannel()方法:
status_t NativeInputManager::registerInputChannel(JNIEnv* env,
const sp<InputChannel>& inputChannel,
const sp<InputWindowHandle>& inputWindowHandle, bool monitor) {
// 终于看到我们 InputDispatcher 了
return mInputManager->getDispatcher()->registerInputChannel(
inputChannel, inputWindowHandle, monitor);
}
InputDispatcher.cpp 中的注册方法:registerInputChannel()
status_t InputDispatcher::registerInputChannel(const sp<InputChannel>& inputChannel,
const sp<InputWindowHandle>& inputWindowHandle, bool monitor) {
{ // acquire lock
AutoMutex _l(mLock);
// 连接的媒介,只是一个对象,没有跨进程等操作;
sp<Connection> connection = new Connection(inputChannel, inputWindowHandle, monitor);
int fd = inputChannel->getFd();
// 传入 fd 和 connection,关联 fd 和 connection;
mConnectionsByFd.add(fd, connection);
if (monitor) {
mMonitoringChannels.push(inputChannel);
}
mLooper->addFd(fd, 0, ALOOPER_EVENT_INPUT, handleReceiveCallback, this);
} // release lock
// Wake the looper because some connections have changed.
mLooper->wake();
return OK;
}
2.3.3 InputMonitor
简介:实现了 WindowManagerCallbacks接口,在 WindowManagerService 的构造函数中创建了InputMonitor 对象,并以 mInputMonitor 作为参数创建 InputManagerService 的对象,在 InputManagerService 构造函数中,将 mInputMonitor 作为参数调用了 JNI 函数 nativeInit() ,将回调接口传到JNI层,在需要的时候,JNI 再回调 mInputMonitor中 的函数,实现数据才传递。
public interface WindowManagerCallbacks {
public void notifyConfigurationChanged();
// 输入设备的配置变更
public void notifyLidSwitchChanged(long whenNanos, boolean lidOpen);
public void notifyCameraLensCoverSwitchChanged(long whenNanos, boolean lensCovered);
// 连接InputDispatcher 与应用程序的 socket 通道
public void notifyInputChannelBroken(InputWindowHandle inputWindowHandle);
// ANR
public long notifyANR(InputApplicationHandle inputApplicationHandle,
InputWindowHandle inputWindowHandle, String reason);
// 以下三个回调,是WMS在消息处理中有优先权处理
public int interceptKeyBeforeQueueing(KeyEvent event, int policyFlags);
public int interceptMotionBeforeQueueingNonInteractive(long whenNanos, int policyFlags);
public long interceptKeyBeforeDispatching(InputWindowHandle focus,
KeyEvent event, int policyFlags);
// 按键事件在整个事件处理过程中没有任何处理时,发送给 WMS
public KeyEvent dispatchUnhandledKey(InputWindowHandle focus,
KeyEvent event, int policyFlags);
public int getPointerLayer();
}
// 简化代码:
final class InputMonitor implements InputManagerService.WindowManagerCallbacks {
private final WindowManagerService mService;
private WindowState mInputFocus;
private boolean mUpdateInputWindowsNeeded = true;
private InputWindowHandle[] mInputWindowHandles;
private final Object mInputDevicesReadyMonitor = new Object();
private boolean mInputDevicesReady;
Rect mTmpRect = new Rect();
public InputMonitor(WindowManagerService service) {
mService = service;
}
public void updateInputWindowsLw(boolean force) {
for (int displayNdx = 0; displayNdx < numDisplays; ++displayNdx) {
WindowList windows = mService.mDisplayContents.valueAt(displayNdx).getWindowList();
for (int winNdx = windows.size() - 1; winNdx >= 0; --winNdx) {
// ...
// 加入到 mInputWindowHandles 数组中
if (child.mWinAnimator != universeBackground) {
addInputWindowHandleLw(inputWindowHandle, child, flags, privateFlags, type,
isVisible, hasFocus, hasWallpaper);
}
}
// 发送窗口到本地方法;
mService.mInputManager.setInputWindows(mInputWindowHandles);
// Clear the list in preparation for the next round.
clearInputWindowHandlesLw();
}
}
// InputServiceManager.java中:调用的是 native 方法;
public void setInputWindows(InputWindowHandle[] windowHandles) {
nativeSetInputWindows(mPtr, windowHandles);
}
frameworks/base/services/core/jni/com_android_server_input_InputManagerService.cpp 中 nativeSetInputWindows() 方法:
// 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);
}
// com_android_server_input_InputManagerService.cpp 中:
void NativeInputManager::setInputWindows(JNIEnv* env, jobjectArray windowHandleObjArray) {
Vector<sp<InputWindowHandle> > windowHandles;
// ...
// 调用InputDispatcher 中 setInputWindows()
mInputManager->getDispatcher()->setInputWindows(windowHandles);
// ...
}
// InputDispatcher.cpp 中的 setInputWindows():
void InputDispatcher::setInputWindows(const Vector<sp<InputWindowHandle> >& inputWindowHandles) {
{ // acquire lock
Vector<sp<InputWindowHandle> > oldWindowHandles = mWindowHandles;
// 把 inputWindowHandles 赋值给 mWindowHandles,这里就知道分发的目标窗口是哪个窗口了;
// input 事件就是根据匹配 InputWindowHandle 来进行分发的;
mWindowHandles = inputWindowHandles;
}
// ... 一些窗口的更新等操作
// Wake up poll loop since it may need to make new input dispatching choices.
mLooper->wake();
}
网友评论