前一篇文章文虏,我們講了Input ANR是怎么產(chǎn)生的[ANR]Input ANR是怎樣產(chǎn)生的,著重講的Input ANR的觸發(fā)和判定原理紧武。主要分析了system_server
進(jìn)程中的InputDispatcher
線程的運(yùn)行流程敏储。這個線程主要負(fù)責(zé)事件的分發(fā),通過socket
將事件發(fā)送給App端進(jìn)行處理妥箕。
system_server
進(jìn)程的InputDispatcher
線程更舞,與App端的主線程進(jìn)行通信,需要先建立socket連接宇葱。這篇文章刊头,我們講講socket連接的建立過程。
socketpair使用
socketpair方法印颤,主要用于創(chuàng)建一對無名的穿肄、相互連接的套接字际看。
#include <sys/types.h>
#include <sys/socket.h>
int socketpair(int domain, int type, int protocol, int sv[2]);
參數(shù):
- domain:協(xié)議家族
- AF_LOCAL
- AF_UNIX
- type:套接字類型
- SOCKET_STREAM:基于TCP
- SOCKET_DGRAM:基于UDP
- SOCKET_SEQPACKET:序列化包矢否,提供一個序列化的兴喂、可靠的、雙向的基本連接的數(shù)據(jù)傳輸通道衣迷,數(shù)據(jù)長度定長。
- protocol:協(xié)議類型云矫,只能是0
- sv:返回的套接字對
返回值:
- 0:成功
- 1:失敗
read方法
ssize_t read(int fd, void * buf, size_t count);
作用:將fd所指的文件傳送count個字節(jié)到buf指針?biāo)傅膬?nèi)存中
返回值:
- 返回實(shí)際讀到的字節(jié)數(shù)
- 0:表示讀到文件尾让禀,無刻度數(shù)據(jù)
- 小于0:讀取出錯陨界,需要看具體的errno
write方法
ssize_t write (int fd, const void * buf, size_t count);
作用:將buf所指的內(nèi)存寫入count個字節(jié)到參數(shù)fd所指的文件中。
返回值:
- 返回實(shí)際寫入的字節(jié)數(shù)
- -1:發(fā)生錯誤菌瘪,需要看具體的errno俏扩。
這里先簡單介紹一下read和write方法,之后再出文章詳細(xì)介紹UNIX域套接字录淡。
App跨進(jìn)程調(diào)用WMS
主線程創(chuàng)建Activity
的時候嫉戚,會調(diào)用setContentView
,最后會調(diào)用到ViewRootImpl
的setView
方法崔拥。
public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) {
synchronized (this) {
...
if ((mWindowAttributes.inputFeatures
& WindowManager.LayoutParams.INPUT_FEATURE_NO_INPUT_CHANNEL) == 0) {
mInputChannel = new InputChannel(); //創(chuàng)建InputChannel對象
}
//通過Binder調(diào)用凤覆,進(jìn)入system進(jìn)程的Session
res = mWindowSession.addToDisplay(mWindow, mSeq, mWindowAttributes,
getHostVisibility(), mDisplay.getDisplayId(),
mAttachInfo.mContentInsets, mAttachInfo.mStableInsets,
mAttachInfo.mOutsets, mInputChannel);
...
if (mInputChannel != null) {
if (mInputQueueCallback != null) {
mInputQueue = new InputQueue();
mInputQueueCallback.onInputQueueCreated(mInputQueue);
}
//創(chuàng)建WindowInputEventReceiver對象拆魏,并且傳入剛剛創(chuàng)建好的mInputChannel
mInputEventReceiver = new WindowInputEventReceiver(mInputChannel, Looper.myLooper());
}
}
}
這個方法,主要是一個跨進(jìn)程的Binder
調(diào)用拥峦,最后調(diào)用的WMS
中的addWinow
方法。
WMS生成兩個InputChannel
public int addWindow(Session session, IWindow client, int seq, WindowManager.LayoutParams attrs, int viewVisibility, int displayId, Rect outContentInsets, Rect outStableInsets, Rect outOutsets, InputChannel outInputChannel) {
//創(chuàng)建一對InputChannel
InputChannel[] inputChannels = InputChannel.openInputChannelPair(name);
//將socket服務(wù)端保存到WindowState的mInputChannel
win.setInputChannel(inputChannels[0]);
//socket客戶端傳遞給outInputChannel刑峡,最后會作為跨進(jìn)程調(diào)用的返回值突梦,傳遞給App端
inputChannels[1].transferTo(outInputChannel);
//利用socket服務(wù)端作為參數(shù)羽利,注冊到system_server的IMS中
mInputManager.registerInputChannel(win.mInputChannel, win.mInputWindowHandle);
}
//設(shè)置當(dāng)前聚焦窗口
mInputMonitor.updateInputWindowsLw(false /*force*/);
}
這個方法,主要調(diào)用了InputChannel
的openInputChannePair
娃闲,生成一對InputChannel
匾浪。
-
InputChannel[0]
保存到WindowState
的mInputChannel
-
InputChannel[1]
傳遞給客戶端,即ViewRootImpl
中的mInputChannel
玲献,最后會和WindowInputEventReceiver
關(guān)聯(lián)梯浪。InputChannel可以跨進(jìn)程通信。
status_t InputChannel::openInputChannelPair(const String8& name,
sp<InputChannel>& outServerChannel, sp<InputChannel>& outClientChannel) {
int sockets[2];
//真正創(chuàng)建socket對的地方【核心】
if (socketpair(AF_UNIX, SOCK_SEQPACKET, 0, sockets)) {
...
return result;
}
int bufferSize = SOCKET_BUFFER_SIZE; //32k
setsockopt(sockets[0], SOL_SOCKET, SO_SNDBUF, &bufferSize, sizeof(bufferSize));
setsockopt(sockets[0], SOL_SOCKET, SO_RCVBUF, &bufferSize, sizeof(bufferSize));
setsockopt(sockets[1], SOL_SOCKET, SO_SNDBUF, &bufferSize, sizeof(bufferSize));
setsockopt(sockets[1], SOL_SOCKET, SO_RCVBUF, &bufferSize, sizeof(bufferSize));
String8 serverChannelName = name;
serverChannelName.append(" (server)");
//創(chuàng)建InputChannel對象
outServerChannel = new InputChannel(serverChannelName, sockets[0]);
String8 clientChannelName = name;
clientChannelName.append(" (client)");
//創(chuàng)建InputChannel對象
outClientChannel = new InputChannel(clientChannelName, sockets[1]);
return OK;
}
在這個方法里礼预,主要做了以下事情:
- 創(chuàng)建了一個socket pair虏劲,生成一對無名的柒巫,相互連接的套接字
- 設(shè)兩個套接口的緩沖區(qū)大小為32kb
- 分別創(chuàng)建服務(wù)端和客戶端的InputChannel對象
-
socket[0]
對應(yīng)的是server -
socket[1]
對應(yīng)的是client
-
到這里,socket的創(chuàng)建就完成了堡掏。
總結(jié)
Android輸入系統(tǒng),system_server
和app之間socket的創(chuàng)建流程:
- 首先App端在初始化view的時候,會通過跨進(jìn)程
Binder
調(diào)用WMS的addWindow
方法 - WMS在
addWindow
中會創(chuàng)建socket
連接鹅龄,生成兩個inputChannel
對象扮休,一個設(shè)置給IMS,一個通過Binder傳回給App玷坠。