文章参考:http://gityuan.com/2016/12/24/input-ui/
概述 前面文章都是介绍了两个线程InputReader和InputDispatcher的工作过程。
在InputDispatcher的过程讲到 调用InputChannel通过socket与远程进程通信,本文便展开讲解这个socket是如何建立的。
对于InputReader和InputDispatcher都是运行在system_server进程; 用户点击的界面往往可能是某一个app,而每个app一般地都运行在自己的业务进程,这里就涉及到跨进程通信,app进程是如何与system进程建立通信。
要解答这些问题,从Activity最基本的创建过程开始说起。我们都知道一般地Activity对应一个应用窗口, 每一个窗口对应一个ViewRootImpl。窗口是如何添加到Activity的,从Activity.onCreate()为起点讲解。
众所周知,Activity的生命周期的回调方法都是运行在主线程,也称之为UI线程,所有UI相关的操作都需要运行在该线程。
Activity.onCreate 1 2 3 4 5 protected void onCreate (Bundle savedInstanceState) { super .onCreate(savedInstanceState); setContentView(R.layout.activity_account_bind); ... }
Activity启动是由system进程控制:
handleLaunchActivity():会调用Activity.onCreate(), 该方法内再调用setContentView(),经过AMS与WMS的各种交互,层层调用后,进入step2
handleResumeActivity():会调用Activity.makeVisible(),该方法继续调用便会执行到WindowManagerImpl.addView(), 该方法内部再调用WindowManagerGlobal.addView(),
ViewRootImpl 我们在建立应用和WMS的联系”中,我们介绍过ViewRootImpl的setView()方法,下面我们再看看这个方法:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 public void setView (View view, WindowManager.LayoutParams attrs, View panelParentView, int userId) { synchronized (this ) { if (mView == null ) { mView = view; requestLayout(); InputChannel inputChannel = null ; if ((mWindowAttributes.inputFeatures & WindowManager.LayoutParams.INPUT_FEATURE_NO_INPUT_CHANNEL) == 0 ) { inputChannel = new InputChannel (); } mForceDecorViewVisibility = (mWindowAttributes.privateFlags & PRIVATE_FLAG_FORCE_DECOR_VIEW_VISIBILITY) != 0 ; if (mView instanceof RootViewSurfaceTaker) { PendingInsetsController pendingInsetsController = ((RootViewSurfaceTaker) mView).providePendingInsetsController(); if (pendingInsetsController != null ) { pendingInsetsController.replayAndAttach(mInsetsController); } } try { mOrigWindowType = mWindowAttributes.type; mAttachInfo.mRecomputeGlobalAttributes = true ; collectViewAttributes(); adjustLayoutParamsForCompatibility(mWindowAttributes); controlInsetsForCompatibility(mWindowAttributes); Rect attachedFrame = new Rect (); final float [] sizeCompatScale = { 1f }; res = mWindowSession.addToDisplayAsUser(mWindow, mWindowAttributes, getHostVisibility(), mDisplay.getDisplayId(), userId, mInsetsController.getRequestedVisibilities(), inputChannel, mTempInsets, mTempControls, attachedFrame, sizeCompatScale); } } }
setView()方法中,如果Window的属性能接收输入并且还没有创建InputChannle,则会新创建一个InputChannel对象。这个对象会通过mWindowSession的addToDisplay()方法传递到WindowManagerService中。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 @Override public int addToDisplayAsUser (IWindow window, WindowManager.LayoutParams attrs, int viewVisibility, int displayId, int userId, InsetsVisibilities requestedVisibilities, InputChannel outInputChannel, InsetsState outInsetsState, InsetsSourceControl[] outActiveControls, Rect outAttachedFrame, float [] outSizeCompatScale) { return mService.addWindow(this , window, attrs, viewVisibility, displayId, userId, requestedVisibilities, outInputChannel, outInsetsState, outActiveControls, outAttachedFrame, outSizeCompatScale); }
这个过程我们前面已经详细介绍过了,知道这最后会调用WMS的addWindow()方法,addWindow()方法中有下面的代码片段:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 public int addWindow (Session session, IWindow client, int seq, WindowManager.LayoutParams attrs, int viewVisibility, int displayId, Rect outContentInsets, InputChannel outInputChannel) { ...... if (outInputChannel ! = null && (attrs.inputFeatures & WindowManager.LayoutParams.INPUT_FEATURE_NO_INPUT_CHANNEL) == 0 ) { String name = win.makeInputChannelName (); InputChannel[] inputChannels = InputChannel.openInputChannelPair (name); win.setInputChannel (inputChannels[0 ]); inputChannels[1 ].transferTo (outInputChannel); mInputManager.registerInputChannel (win.mInputChannel, win.mInputWindowHandle); } ...... }
在addWindow()方法中先调用InputChannel的openInputChannelPair来返回一个InputChannel对象的数组,然后将inputChannels[0]设置到win对象中。接着将inputChannels[1]转换成和客户端传递过来的outInputChannel对象。最后调用了InputManagerService中的registerInputChannel()方法(现在我们知道registerInputChannel()方法从哪里调用的了),调用该方法的参数是win的mInputChannel,实际上就是inputChannels[0]。这儿的代码有点不好理解,我们仔细看看这些函数的实现。
先看看openInputChannelPair()方法,代码如下:
1 2 3 4 public static InputChannel[] openInputChannelPair (String name) { ...... return nativeOpenInputChannelPair (name); }
openInputChannelPair()方法调用了nativeOpenInputChannelPair()方法,其对应的本地函数如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 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; status_t result = InputChannel::openInputChannelPair (name, serverChannel, clientChannel); ...... 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; }
android_view_InputChannel_nativeOpenInputChannelPair()函数中调用openInputChannelPair()函数创建了两个InputChannel对象,serverChannel和clientChannel。然后又以它们作为参数创建了两个NativeInputChannel对象,再以它们为参数调用android_view_InputChannel_createInputChannel()函数,最后把函数的返回值放到数组channelPair中。先看看openInputChannelPair()函数做了些什么:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 status_t InputChannel::openInputChannelPair (const String8& name, sp<InputChannel>& outServerChannel, sp<InputChannel>& outClientChannel) { int sockets[2 ]; if (socketpair (AF_UNIX, SOCK_SEQPACKET, 0 , sockets)) { ...... return result; } ..... 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; }
openInputChannelPair()函数首先调用socketpair()函数创建了一对socket,我们知道这是用来相互通信的。接下来函数中用这两个socket句柄作为参数创建了两个InputChannel对象。
1 2 3 4 5 6 7 8 9 10 11 static jobject android_view_InputChannel_createInputChannel (JNIEnv* env, NativeInputChannel* nativeInputChannel) { jobject inputChannelObj = env->NewObject (gInputChannelClassInfo.clazz, gInputChannelClassInfo.ctor); if (inputChannelObj) { android_view_InputChannel_setNativeInputChannel (env, inputChannelObj, nativeInputChannel); } return inputChannelObj; }
android_view_InputChannel_createInputChannel()函数中创建了Java层的InputChannel对象,并且调用了android_view_InputChannel_setNativeInputChannel()函数,代码如下:
1 2 3 4 5 6 static void android_view_InputChannel_setNativeInputChannel (JNIEnv* env, jobject inputChannelObj, NativeInputChannel* nativeInputChannel) { env->SetIntField (inputChannelObj, gInputChannelClassInfo.mPtr, reinterpret_cast <jint>(nativeInputChannel)); }
android_view_InputChannel_setNativeInputChannel()函数把native层的NativeInputChannel对象的指针放到了Jave层的InputChannel对象的mPtr成员变量中。
android_view_InputChannel_setNativeInputChannel()函数把native层的NativeInputChannel对象的指针放到了Jave层的InputChannel对象的mPtr成员变量中。
看到这里我们就明白了,WindowMangerService中调用InputChannel中的openInputChannel Pair()方法将会创建两个Java的InputChannel对象,而它们对应的native层的NativeInputChannel对象中会通过两个socket关联在一起。
前面我们看到,WMS的addWindow()方法中会调用inputChannels[1]的transferTo()方法,而且以客户端传递的outInputChannel作为参数。我们看看transferTo()方法,代码如下:
1 2 3 4 public void transferTo (InputChannel outParameter) { ...... nativeTransferTo (outParameter); }
transferTo()又调用了nativeTransferTo()方法,它对应的本地函数如下:
1 2 3 4 5 6 7 8 9 10 11 12 static void android_view_InputChannel_nativeTransferTo (JNIEnv* env, jobject obj, jobject otherObj) { if (android_view_InputChannel_getNativeInputChannel (env, otherObj) ! = NULL ) { ...... return ; } NativeInputChannel* nativeInputChannel = android_view_InputChannel_getNativeInputChannel (env, obj); android_view_InputChannel_setNativeInputChannel (env, otherObj, nativeInputChannel); android_view_InputChannel_setNativeInputChannel (env, obj, NULL ); }
这个函数的功能就是把调用对象的mPtr中的值放到参数对象的mPtr中,并将调用对象的mPtr设置为NULL。
因此,addWindow()方法中transferTo()方法执行完后,outInputChannel的mPtr指针保存的就是native层创建的InputChannel对象的指针。
Session对象的addToDisplay()方法中参数outInputChannel是一个返回参数,也就是说客户端调用完addToDisplay()之后,作为参数传递的outInputChannel将会被赋值。我们看看它的readFromParcel()方法的实现。
1 2 3 4 public void readFromParcel (Parcel in) { ...... nativeReadFromParcel (in); }
nativeReadFromParcel()方法对应的native层的函数如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 static void android_view_InputChannel_nativeReadFromParcel (JNIEnv* env, jobject obj, jobject parcelObj) { if (android_view_InputChannel_getNativeInputChannel (env, obj) ! = NULL ) { ...... return ; } Parcel* parcel = parcelForJavaObject (env, parcelObj); if (parcel) { bool isInitialized = parcel->readInt32 (); if (isInitialized) { String8 name = parcel->readString8 (); int rawFd = parcel->readFileDescriptor (); int dupFd = dup (rawFd); if (dupFd < 0 ) { return ; } InputChannel* inputChannel = new InputChannel (name, dupFd); NativeInputChannel* nativeInputChannel = new NativeInputChannel (inputChannel); android_view_InputChannel_setNativeInputChannel (env, obj, nativeInputChannel); } } }
nativeReadFromParcel()方法中将使用从WMS中传递回来的socket句柄创建InputChannel对象。这样InputManagerService中的InputChannel对象和应用进程中的InputChannel对象通过socket就连接在了一起,如图16.4所示。
理解了InputChannel之后,让我们再回到InputManagerService中,前面己经介绍了,输入消息会传递到InputChannel的sendMessage()函数中,它的代码如下:
1 2 3 4 5 6 7 8 9 status_t InputChannel::sendMessage (const InputMessage* msg) { size_t msgLength = msg->size (); ssize_t nWrite; do { nWrite = ::send (mFd, msg, msgLength, MSG_DONTWAIT | MSG_NOSIGNAL); } while (nWrite == -1 && errno == EINTR); ...... return OK; }
sendMessage()函数调用系统的send()函数来发送输入消息,这样输入消息通过socket就传递到用户进程中。
用户进程又在哪里接收socket传递过来的消息呢?我们再看看ViewRootImpl的setView()函数,函数中调用addToDisplay()得到InputChannel对象后,接下来是创建WindowInputEventReceiver对象,如下所示:
1 2 3 4 5 6 7 8 if (mInputChannel ! = null) { if (mInputQueueCallback ! = null) { mInputQueue = new InputQueue (); mInputQueueCallback.onInputQueueCreated (mInputQueue); } mInputEventReceiver = new WindowInputEventReceiver (mInputChannel, Looper.myLooper ()); }
注意,上面的代码中创建WindowInputEventReceiver对象时使用了InputChannel对象和当前线程的Looper对象作为参数。下面是它的构造方法:
1 2 3 4 5 6 7 8 public InputEventReceiver (InputChannel inputChannel, Looper looper) { ...... mInputChannel = inputChannel; mMessageQueue = looper.getQueue (); mReceiverPtr = nativeInit (new WeakReference <InputEventReceiver>(this ), inputChannel, mMessageQueue); mCloseGuard.open ("dispose" ); }
InputEventReceiver()调用了nativeInit()方法,同时把从Looper对象中取到的MessageQueue对象作为参数传递到底层,nativeInit()方法对应的native层的函数如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 static jint nativeInit (JNIEnv* env, jclass clazz, jobject receiverWeak, jobject inputChannelObj, jobject messageQueueObj) { sp<InputChannel> inputChannel = android_view_InputChannel_getInputChannel (env, inputChannelObj); ...... sp<MessageQueue> messageQueue = android_os_MessageQueue_getMessageQueue (env, messageQueueObj); ...... sp<NativeInputEventReceiver> receiver = new NativeInputEventReceiver (env, receiverWeak, inputChannel, messageQueue); status_t status = receiver->initialize (); ...... receiver->incStrong (gInputEventReceiverClassInfo.clazz); return reinterpret_cast <jint>(receiver.get ()); }
nativeInit()函数首先取得了Jave层的InputChannel对象和MessageQueue在native层的对应对象的指针,然后使用它们作为参数创建了NativeInputEventReceiver对象,并调用它的initialize()函数。函数代码如下:
1 2 3 4 status_t NativeInputEventReceiver::initialize () { setFdEvents (ALOOPER_EVENT_INPUT); return OK; }
归纳总结
InputDispatcher
中会先将事件放到InboundQueue
也就是“iq”队列中,然后寻找具体处理input
事件的目标应用窗口,并将事件放入对应的目标窗口OutboundQueue
也就是“oq”队列中等待通过SocketPair
双工信道发送到应用目标窗口中。
事件发送给具体的应用目标窗口后,会将事件移动到WaitQueue
也就是“wq”中等待目标应用处理事件完成,并开启倒计时,如果目标应用窗口在5S内没有处理完成此次触控事件,就会向system_server
报应用ANR异常事件 。