上一篇文章分析到InputDispatcher將Input事件做了一些列處理之后,會將事件發送到APP行程,InputDispatcher和APP屬于兩個不同行程,他們之間是如何通信的呢?答案就是InputChannel,我們看看InputChannel的注釋:
/**
* An input channel specifies the file descriptors used to send input events to
* a window in another process. It is Parcelable so that it can be sent
* to the process that is to receive events. Only one thread should be reading
* from an InputChannel at a time.
* @hide
*/
public final class InputChannel implements Parcelable {
.....
}
說它是用來將輸入事件發送到其他行程的視窗的一個通道,并且它實作了Parcelable,可以將其發送到需要接收輸入事件的行程,從注釋從我們能清楚知道InputChannel的作用,
首先我們從APP的啟動說起,我們知道不管什么應用,啟動之后都會呼叫addView方法將自己的Window添加到WMS:
WindowManagerGlobal.addView
public void addView(View view, ViewGroup.LayoutParams params,
Display display, Window parentWindow, int userId) {
...
root = new ViewRootImpl(view.getContext(), display);
......
root.setView(view, wparams, panelParentView);
......
...
}
ViewRootImpl.setView
public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView,
int userId) {
......
InputChannel inputChannel = null;
if ((mWindowAttributes.inputFeatures
& WindowManager.LayoutParams.INPUT_FEATURE_NO_INPUT_CHANNEL) == 0) {
inputChannel = new InputChannel();
}
try {
...
res = mWindowSession.addToDisplayAsUser(mWindow, mSeq, mWindowAttributes,
getHostVisibility(), mDisplay.getDisplayId(), userId, mTmpFrame,
mAttachInfo.mContentInsets, mAttachInfo.mStableInsets,
mAttachInfo.mDisplayCutout, inputChannel,
mTempInsets, mTempControls);
...
} catch (RemoteException e) {
inputChannel = null;
}
......
}
我們只關注和InputChanel相關的代碼,應用首次啟動,會創建一個InputChanel并將其傳到WMS,注意這里僅僅是new了一個InputChanel,并未對其成員變數填充任何資料,也就是說這只是個空的InputChanel,它的具體賦值是在WMS中完成的,
WindowManagerService.addWindow
addToDisplayAsUser會通過Binder調到WMS中:
public int addWindow(Session session, IWindow client, int seq,
LayoutParams attrs, int viewVisibility, int displayId, Rect outFrame,
Rect outContentInsets, Rect outStableInsets,
DisplayCutout.ParcelableWrapper outDisplayCutout, InputChannel outInputChannel,
InsetsState outInsetsState, InsetsSourceControl[] outActiveControls,
int requestUserId) {
......
final boolean openInputChannels = (outInputChannel != null
&& (attrs.inputFeatures & INPUT_FEATURE_NO_INPUT_CHANNEL) == 0);
if (openInputChannels) {
win.openInputChannel(outInputChannel);
}
......
}
}
WindowState.openInputChannel
void openInputChannel(InputChannel outInputChannel) {
if (mInputChannel != null) {
throw new IllegalStateException("Window already has an input channel.");
}
//視窗名稱
String name = getName();
//創建InputChannelPair
InputChannel[] inputChannels = InputChannel.openInputChannelPair(name);
//服務端InputChannel
mInputChannel = inputChannels[0];
//客戶端InputChannel
mClientChannel = inputChannels[1];
//將服務端InputChannel注冊到InputDispatcher
mWmService.mInputManager.registerInputChannel(mInputChannel);
//token唯一標識了接收input事件的視窗
mInputWindowHandle.token = mInputChannel.getToken();
if (outInputChannel != null) {
/將客戶端InputChannel設定到ViewRootImpl的outInputChannel中
mClientChannel.transferTo(outInputChannel);
mClientChannel.dispose();
mClientChannel = null;
} else {
mDeadWindowEventReceiver = new DeadWindowEventReceiver(mClientChannel);
}
mWmService.mInputToWindowMap.put(mInputWindowHandle.token, this);
}
這個方法是重點,主要構造一對InputChannel,分為“server”和“client”,“server”最后會注冊到InputDispatcher,“client”會回傳給APP行程的ViewRootImpl,
我們首先來看看openInputChannelPair:
InputChannel.openInputChannelPair
public static InputChannel[] openInputChannelPair(String name) {
if (name == null) {
throw new IllegalArgumentException("name must not be null");
}
if (DEBUG) {
Slog.d(TAG, "Opening input channel pair '" + name + "'");
}
return nativeOpenInputChannelPair(name);
}
這里會調到native層:
android_view_InputChannel_nativeOpenInputChannelPair
static jobjectArray android_view_InputChannel_nativeOpenInputChannelPair(JNIEnv* env,
jclass clazz, jstring nameObj) {
ScopedUtfChars nameChars(env, nameObj);
std::string name = nameChars.c_str();
sp<InputChannel> serverChannel;
sp<InputChannel> clientChannel;
//構造一對native層InputChannel
status_t result = InputChannel::openInputChannelPair(name, serverChannel, clientChannel);
if (result) {
std::string message = android::base::StringPrintf(
"Could not open input channel pair : %s", strerror(-result));
jniThrowRuntimeException(env, message.c_str());
return nullptr;
}
//構造一個java層InputChannel型別陣列
jobjectArray channelPair = env->NewObjectArray(2, gInputChannelClassInfo.clazz, nullptr);
if (env->ExceptionCheck()) {
return nullptr;
}
//將native層InputChannel轉換為java層InputChannel
jobject serverChannelObj = android_view_InputChannel_createInputChannel(env, serverChannel);
if (env->ExceptionCheck()) {
return nullptr;
}
//將native層InputChannel轉換為java層InputChannel
jobject clientChannelObj = android_view_InputChannel_createInputChannel(env, clientChannel);
if (env->ExceptionCheck()) {
return nullptr;
}
//將轉換的java層InputChannel存到前面構造的陣列
env->SetObjectArrayElement(channelPair, 0, serverChannelObj);
env->SetObjectArrayElement(channelPair, 1, clientChannelObj);
//回傳給java層
return channelPair;
}
這個函式很簡單,主要是通過openInputChannelPair構造了一對native層InputChannel,然后再根據其創建java層InputChannel,最后回傳給java層,
InputChannel::openInputChannelPair
status_t InputChannel::openInputChannelPair(const std::string& name,
sp<InputChannel>& outServerChannel, sp<InputChannel>& outClientChannel) {
int sockets[2];
if (socketpair(AF_UNIX, SOCK_SEQPACKET, 0, sockets)) {
...
//例外
return result;
}
//32KB
int bufferSize = SOCKET_BUFFER_SIZE;
//設定socket發送和接識訓沖區大小為bufferSize
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));
//創建BBinder,用于標識APP行程的視窗
sp<IBinder> token = new BBinder();
std::string serverChannelName = name + " (server)";
android::base::unique_fd serverFd(sockets[0]);
//server端InputChannel保存了server端socket的fd
outServerChannel = InputChannel::create(serverChannelName, std::move(serverFd), token);
std::string clientChannelName = name + " (client)";
android::base::unique_fd clientFd(sockets[1]);
//client端InputChannel保存了client端socket的fd
outClientChannel = InputChannel::create(clientChannelName, std::move(clientFd), token);
return OK;
}
這個函式中創建了一對"socket"和一對native層InputChannel,并將他們都區分為"server"端和“client”端,"server"端InputChannel保存了"server"端"socket"的fd,"client"端InputChannel保存了"client"端socket的fd,
再來看看InputChannel::create:
sp<InputChannel> InputChannel::create(const std::string& name, android::base::unique_fd fd,
sp<IBinder> token) {
const int result = fcntl(fd, F_SETFL, O_NONBLOCK);
if (result != 0) {
LOG_ALWAYS_FATAL("channel '%s' ~ Could not make socket non-blocking: %s", name.c_str(),
strerror(errno));
return nullptr;
}
return new InputChannel(name, std::move(fd), token);
}
//InputChannel建構式
InputChannel::InputChannel(const std::string& name, android::base::unique_fd fd, sp<IBinder> token)
: mName(name), mFd(std::move(fd)), mToken(token) {
if (DEBUG_CHANNEL_LIFECYCLE) {
ALOGD("Input channel constructed: name='%s', fd=%d", mName.c_str(), mFd.get());
}
}
這里并沒有做注冊"socket"之類的操作,我們暫且記住"socket"的fd保存在了InputChannel中,
一對native層InputChannel創建好了之后,接著通過android_view_InputChannel_createInputChannel函式創建出一對java層InputChannel,并放入陣列,回傳給java層,這里面代碼非常簡單,不必細說,
再回到WindowState的openInputChannel方法,接著來看"server"端InputChannel向InputDispatcher注冊的程序:
InputManagerService.registerInputChannel
public void registerInputChannel(InputChannel inputChannel) {
if (inputChannel == null) {
throw new IllegalArgumentException("inputChannel must not be null.");
}
nativeRegisterInputChannel(mPtr, inputChannel);
}
nativeRegisterInputChannel
static void nativeRegisterInputChannel(JNIEnv* env, jclass /* clazz */,
jlong ptr, jobject inputChannelObj) {
NativeInputManager* im = reinterpret_cast<NativeInputManager*>(ptr);
//獲取native層InputChannel
sp<InputChannel> inputChannel = android_view_InputChannel_getInputChannel(env,
inputChannelObj);
...
status_t status = im->registerInputChannel(env, inputChannel);
if (status) {
//例外情況
return;
}
//給mDisposeCallback賦值為handleInputChannelDisposed
android_view_InputChannel_setDisposeCallback(env, inputChannelObj,
handleInputChannelDisposed, im);
}
這個函式我們重點關注注冊InputChannel:
status_t NativeInputManager::registerInputChannel(JNIEnv* /* env */,
const sp<InputChannel>& inputChannel) {
ATRACE_CALL();
return mInputManager->getDispatcher()->registerInputChannel(inputChannel);
}
這里直接調到InputDispatcher中去了:
InputDispatcher::registerInputChannel
status_t InputDispatcher::registerInputChannel(const sp<InputChannel>& inputChannel) {
#if DEBUG_REGISTRATION
ALOGD("channel '%s' ~ registerInputChannel", inputChannel->getName().c_str());
#endif
{ // acquire lock
std::scoped_lock _l(mLock);
//如果已經存在Connection,則不必重復注冊
sp<Connection> existingConnection = getConnectionLocked(inputChannel->getConnectionToken());
if (existingConnection != nullptr) {
ALOGW("Attempted to register already registered input channel '%s'",
inputChannel->getName().c_str());
return BAD_VALUE;
}
//創建Connection,將“server”端inputChannel傳進去
sp<Connection> connection = new Connection(inputChannel, false /*monitor*/, mIdGenerator);
//server端inputChannel的"socket" fd
int fd = inputChannel->getFd();
//以"socket"的fd為key,connection為value,保存到map中
mConnectionsByFd[fd] = connection;
//創建inputChannel時,接收了一個BBinder的引數,就是其Token,用于標識APP行程的視窗
//的唯一標識
mInputChannelsByToken[inputChannel->getConnectionToken()] = inputChannel;
//重點:將”server端“socket的fd添加到looper監聽
mLooper->addFd(fd, 0, ALOOPER_EVENT_INPUT, handleReceiveCallback, this);
}
//喚醒InputDispatcher
mLooper->wake();
return OK;
}
上面函式做的事情如下:
- 如果是首次注冊
InputChannel,則會創建一個Connection連接,并以InputChannel的"socket"的fd為key,此連接為value,存盤到InputDispatcher的mConnectionsByFd中,InputChannel的token物件則保證了注冊InputChannel的視窗的唯一性,token為key,InputChannel為value保存在了InputDispatcher的mInputChannelsByToken中, - 將”server端“socket的fd添加到
InputDispatcher內部的looper進行監聽,待事件發生之后回呼其handleReceiveCallback函式,這里的事件發生即”client端“socket寫入了資料,
到此InputChannel”server端“的注冊程序已經完成,
我們再回到WindowState的openInputChannel方法:
void openInputChannel(InputChannel outInputChannel) {
if (mInputChannel != null) {
throw new IllegalStateException("Window already has an input channel.");
}
//視窗名稱
String name = getName();
//創建InputChannelPair
InputChannel[] inputChannels = InputChannel.openInputChannelPair(name);
//服務端InputChannel
mInputChannel = inputChannels[0];
//客戶端InputChannel
mClientChannel = inputChannels[1];
//將服務端InputChannel注冊到InputDispatcher
mWmService.mInputManager.registerInputChannel(mInputChannel);
//token唯一標識了接收input事件的視窗
mInputWindowHandle.token = mInputChannel.getToken();
if (outInputChannel != null) {
//將客戶端InputChannel設定到ViewRootImpl的outInputChannel中
mClientChannel.transferTo(outInputChannel);
mClientChannel.dispose();
mClientChannel = null;
} else {
mDeadWindowEventReceiver = new DeadWindowEventReceiver(mClientChannel);
}
//以token為key,windowstate為value保存到WMS
mWmService.mInputToWindowMap.put(mInputWindowHandle.token, this);
}
服務端InputChannel注冊完成之后,客戶端mClientChannel 會通過transferTo方法設定到APP行程的ViewRootImpl的outInputChannel中,具體實作在native層,原理很簡單,就是將mClientChannel 對應的native層NativeInputChannel再保存到ViewRootImpl的outInputChannel的mPtr變數中,不去細看了,
我們需要再回到ViewRootImpl中去看看客戶端InputChannel被保存到outInputChannel之后的后續處理:
```java
public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView,
int userId) {
......
InputChannel inputChannel = null;
if ((mWindowAttributes.inputFeatures
& WindowManager.LayoutParams.INPUT_FEATURE_NO_INPUT_CHANNEL) == 0) {
inputChannel = new InputChannel();
}
try {
...
res = mWindowSession.addToDisplayAsUser(mWindow, mSeq, mWindowAttributes,
getHostVisibility(), mDisplay.getDisplayId(), userId, mTmpFrame,
mAttachInfo.mContentInsets, mAttachInfo.mStableInsets,
mAttachInfo.mDisplayCutout, inputChannel,
mTempInsets, mTempControls);
...
} catch (RemoteException e) {
inputChannel = null;
}
......
if (inputChannel != null) {
....
mInputEventReceiver = new WindowInputEventReceiver(inputChannel,
Looper.myLooper());
}
......
}
后續會創建一個WindowInputEventReceiver物件,其構造方法接收客戶端的InputChannel和一個Looper物件,重點注意這里的Looper其實就是APP行程的UI執行緒的Looper,
final class WindowInputEventReceiver extends InputEventReceiver {
public WindowInputEventReceiver(InputChannel inputChannel, Looper looper) {
super(inputChannel, looper);
}
}
這里呼叫WindowInputEventReceiver父類的構造方法:
InputEventReceiver構造方法
public InputEventReceiver(InputChannel inputChannel, Looper looper) {
if (inputChannel == null) {
throw new IllegalArgumentException("inputChannel must not be null");
}
if (looper == null) {
throw new IllegalArgumentException("looper must not be null");
}
mInputChannel = inputChannel;
//UI執行緒的MessageQueue
mMessageQueue = looper.getQueue();
mReceiverPtr = nativeInit(new WeakReference<InputEventReceiver>(this),
inputChannel, mMessageQueue);
mCloseGuard.open("dispose");
}
這里通過nativeInit完成初始化:
android_view_InputEventReceiver::nativeInit
static jlong nativeInit(JNIEnv* env, jclass clazz, jobject receiverWeak,
jobject inputChannelObj, jobject messageQueueObj) {
//java層InputChannel轉換得到native層InputChannel,client端
sp<InputChannel> inputChannel = android_view_InputChannel_getInputChannel(env,
inputChannelObj);
if (inputChannel == nullptr) {
jniThrowRuntimeException(env, "InputChannel is not initialized.");
return 0;
}
//APP UI執行緒對應native層的MessageQueue
sp<MessageQueue> messageQueue = android_os_MessageQueue_getMessageQueue(env, messageQueueObj);
if (messageQueue == nullptr) {
jniThrowRuntimeException(env, "MessageQueue is not initialized.");
return 0;
}
//創建NativeInputEventReceiver,將inputChannel,messageQueue保存到其內部成員變數
sp<NativeInputEventReceiver> receiver = new NativeInputEventReceiver(env,
receiverWeak, inputChannel, messageQueue);
status_t status = receiver->initialize();
if (status) {
String8 message;
message.appendFormat("Failed to initialize input event receiver. status=%d", status);
jniThrowRuntimeException(env, message.string());
return 0;
}
receiver->incStrong(gInputEventReceiverClassInfo.clazz); // retain a reference for the object
return reinterpret_cast<jlong>(receiver.get());
}
此函式首先將java層傳遞下來的物件轉換為對應native層物件之后,創建了一個NativeInputEventReceiver,并呼叫其initialize函式進行初始化:
NativeInputEventReceiver::initialize
status_t NativeInputEventReceiver::initialize() {
setFdEvents(ALOOPER_EVENT_INPUT);
return OK;
}
NativeInputEventReceiver::setFdEvents
void NativeInputEventReceiver::setFdEvents(int events) {
if (mFdEvents != events) {
mFdEvents = events;
//mInputConsumer就是保存到其內部的client端InputChannel,
//fd指向client端InputChannel內部的client端"socket"的fd
int fd = mInputConsumer.getChannel()->getFd();
if (events) {
//將client端"socket"的fd添加到Looper進行監聽
mMessageQueue->getLooper()->addFd(fd, 0, events, this, nullptr);
} else {
//例外情況
mMessageQueue->getLooper()->removeFd(fd);
}
}
}
此函式取出client端InputChannel的"socket"的fd添加到APP行程的UI執行緒的Looper進行監聽事件型別為ALOOPER_EVENT_INPUT,并在接收到事件之后("server端"socket寫入資料)回呼NativeInputEventReceiver的handleEvent函式,
到此“server端”和“client”端InputChannel的“socket”注冊已經分析完成,“server端”被注冊到InputDispatcher的Looper執行緒,“client”端被注冊到了APP的UI執行緒,這樣他們就可以完成通信了,
有了這篇文章基礎,我們在AndroidR Input子系統(7)InputDispatcher執行緒分發輸入事件最后講到InputDispatcher會通過:
nWrite = ::send(mFd.get(), &cleanMsg, msgLength, MSG_DONTWAIT | MSG_NOSIGNAL);
向“server”端socket寫入資料的方式將輸入事件發送到APP UI執行緒就非常好理解了,
我們對整個程序進行一個總結:
- 首先當一個APP啟動時,會將自己的
Window添加到WMS,并傳遞一個空InputChannel過去, - WMS端,通過
openInputChannel方法會創建一對InputChannel,是在native層完成的,這對InputChannel被分為“client”端和“server”端,其內部又會創建一對socket,和這對InputChannel一一對應, - “server”端
InputChannel會被注冊到InputDispatcher中去,注冊的原理就是將InputChannel內部的socket添加到其Looper進行監聽,注冊程序中還會創建一個Connection物件,Connection用來描述InputDispatcher與此次注冊InputChannel的視窗的連接, - "client"端
InputChannel會被設定到APP行程中,接著通過InputEventReceiver注冊到APP UI執行緒,同樣是將InputChannel內部的socket添加到UI執行緒的Looper進行監聽, - 對于
InputDispatcher執行緒,在接收到"client"端socket的訊息時會回呼其handleReceiveCallback函式,對于APP UI執行緒,在接收到"server"端socket的訊息時會回呼InputEventReceiver對應的native層物件NativeInputEventReceiver的handleEvent函式,
轉載請註明出處,本文鏈接:https://www.uj5u.com/yidong/238095.html
標籤:其他
上一篇:二級橫向listview
