安卓框架,分析解決專案中出現的anr
07-16 15:31:47.551 E/ActivityManager( 1775): Reason: Input dispatching timed out (Waiting because the focused window's input channel is not registered with the input dispatcher. The window may be in the process of being removed.)
07-16 15:31:47.551 E/ActivityManager( 1775): 17% TOTAL: 12% user + 4.7% kernel + 0 % iowait + 0% softirq
07-16 15:31:47.551 E/ActivityManager( 1775): 16% TOTAL: 14% user + 2.2% kernel
07-16 15:31:47.555 W/ActivityManager( 1775): Force finishing activity 1 包名/activity
cpu佔用率不高可以,io流也沒堵塞,提示是分配鍵超時(等待因為當前聚焦window的通訊渠道沒有註冊在Inputdispatcher中。這個window有可能正被移除中)
再看下trace檔案
DALVIK THREADS (40 ):
"main" prio=5 tid=1 Native
| group="main" sCount=1 dsCount=0 obj=0x71775000 self=0xf5027800
| sysTid=2115 nice=0 cgrp=default sched=0/0 handle=0xf7707bec
| state=S schedstat=( 0 0 0 ) utm=2600 stm=85 core=2 HZ=100
| stack=0xff3cc000-0xff3ce000 stackSize=8MB
| held mutexes=
kernel: __switch_to+0x74/0x8c
kernel: SyS_epoll_wait+0x3b4 /0x4ac
kernel: compat_SyS_epoll_pwait+0x148/0x150
kernel: el0_svc_naked+0x24/0x28
native: #00 pc 0003ada8 /system/lib/libc.so (__epoll_pwait+20)
native: #01 pc 00015c25 /system/lib/libc.so (epoll_pwait+26)
native: #02 pc 00015c33 /system/lib/libc.so (epoll_wait+6)
native: #03 pc 0001203b /system/lib/libutils.so (android::Looper::pollInner(int)+98)
native: #04 pc 0001226d /system/lib/libutils.so (android::Looper::pollOnce(int, int*, int*, void**)+100)
native: #05 pc 00080435 /system/lib/libandroid_runtime.so (android::NativeMessageQueue::pollOnce(_JNIEnv*, int)+22)
native: #06 pc 00002c2b /data/dalvik-cache/arm/[email protected]@boot.oat (Java_android_os_MessageQueue_nativePollOnce__JI+102)
at android.os.MessageQueue.nativePollOnce(Native method)
at android.os.MessageQueue.next(MessageQueue.java:143)
at android.os.Looper.loop(Looper.java:122)
at android.app.ActivityThread.main(ActivityThread.java:5254)
at java.lang.reflect.Method.invoke!(Native method)
at java.lang.reflect.Method.invoke(Method.java:372)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:938)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:733)
at android.os.MessageQueue.next(MessageQueue.java:143) 代表從訊息佇列中獲取下一個要處理的訊息
native: #06 pc 00002c2b /data/dalvik-cache/arm/[email protected]@boot.oat (Java_android_os_MessageQueue_nativePollOnce__JI+102) 代表進入了本地方法
native: #02 pc 00015c33 /system/lib/libc.so (epoll_wait+6)
native: #03 pc 0001203b /system/lib/libutils.so (android::Looper::pollInner(int)+98)
說明執行緒進入了等待,直到epoll_wait返回資訊
從以上現象實在是看不出什麼問題,只好走上最笨辦法,跟蹤日誌和框架原始碼。。。。
貼出關鍵日誌
07-16 15:31:36.232 I/InputReader( 1775): checkCombinedKey keyCode = 165, s_CombKeyInfo.eCheckStat = 0
07-16 15:31:36.232 D/InputDispatcher( 1775): ====add keyevent to queue======= type=2
07-16 15:31:36.234 E/Looper ( 2115): Error adding epoll events for fd 41, errno=9
07-16 15:31:36.234 W/InputEventSender( 2115): Failed to send key event on channel ‘ClientState{24ed15b0 uid 1000 pid 2115} (server)’. status=-9
07-16 15:31:36.235 W/InputMethodManager( 2115): Unable to send input event to IME: com.zxic.inputmethod.remote/.RemoteIME dropping: KeyEvent { action=ACTION_DOWN, keyCode=KEYCODE_INFO, scanCode=225, metaState=0, flags=0x8, repeatCount=0, eventTime=15693903, downTime=15693903, deviceId=1, source=0x101 }
07-16 15:31:36.236 I/當前activity( 2115): onKeyDown. keyCode:165
07-16 15:31:36.551 I/InputReader( 1775): checkCombinedKey keyCode = 165, s_CombKeyInfo.eCheckStat = 0
07-16 15:31:36.552 D/InputDispatcher( 1775): ====add keyevent to queue======= type=2
07-16 15:31:36.577 E/InputTransport( 1775): channel ‘2d826e61 你的apk和包名 (server)’ publisher ~ Received unexpected message of type 1 from consumer
07-16 15:31:36.577 E/InputDispatcher( 1775): channel ‘2d826e61 你的apk和包名 (server)’ ~ Failed to receive finished signal. status=-2147483648
07-16 15:31:36.577 E/InputDispatcher( 1775): channel ‘2d826e61 你的apk和包名 (server)’ ~ Channel is unrecoverably broken and will be disposed!
07-16 15:31:36.580 D/InputEventConsistencyVerifier( 2115): KeyEvent: ACTION_UP but key was not down.
07-16 15:31:36.580 D/InputEventConsistencyVerifier( 2115): in [email protected]
07-16 15:31:36.580 D/InputEventConsistencyVerifier( 2115): 0: sent at 15694223000000, KeyEvent { action=ACTION_UP, keyCode=KEYCODE_INFO, scanCode=225, metaState=0, flags=0x8, repeatCount=0, eventTime=15694223, downTime=15693903, deviceId=1, source=0x101 }
07-16 15:31:36.866 D/mali_winsys( 2115): new_window_surface returns 0x3000
07-16 15:31:36.904 D/RemoteIME( 2026): onFinishInput.
07-16 15:31:36.904 D/RemoteIME( 2026): onStartInput ccontentType: 0 Restarting:false
07-16 15:31:36.904 I/RemoteIME( 2026): Default language!
07-16 15:31:38.855 I/InputReader( 1775): checkCombinedKey keyCode = 165, s_CombKeyInfo.eCheckStat = 0
07-16 15:31:38.856 D/InputDispatcher( 1775): ====add keyevent to queue======= type=2
07-16 15:31:39.175 I/InputReader( 1775): checkCombinedKey keyCode = 165, s_CombKeyInfo.eCheckStat = 0
07-16 15:31:39.176 D/InputDispatcher( 1775): ====add keyevent to queue======= type=2
07-16 15:31:43.861 I/InputDispatcher( 1775): Application is not responding: AppWindowToken{1d23d307 token=Token{20605546 ActivityRecord{24568d21 u0 你的apk和包名 t1}}} - Window{2d826e61 u0 你的apk和包名}. It has been 5005.9ms since event, 5004.3ms since wait started. Reason: Waiting because the focused window’s input channel is not registered with the input dispatcher. The window may be in the process of being removed.
07-16 15:31:43.864 I/WindowManager( 1775): Input event dispatching timed out sending to 你的apk和包名. Reason: Waiting because the focused window’s input channel is not registered with the input dispatcher. The window may be in the process of being removed.
貼出來最後的日誌才是anr真實發生的時間,根據5秒規則,儘量找出5秒前的日誌。
接下來就根據日誌分析
一。
07-16 15:31:36.232 I/InputReader( 1775): checkCombinedKey keyCode = 165, s_CombKeyInfo.eCheckStat = 0
07-16 15:31:36.232 D/InputDispatcher( 1775): ====add keyevent to queue======= type=2
type==2代表是鍵盤型別
InputDispatcher.cpp
bool InputDispatcher::enqueueInboundEventLocked(EventEntry* entry) {
bool needWake = mInboundQueue.isEmpty();
ALOGD("====add keyevent to queue======= type=%d", entry->type);
if (mInboundQueue.count() > 0 && (entry->type == EventEntry::TYPE_KEY)) {
KeyEntry* keyBeforeEntry = static_cast<KeyEntry*>(entry);
if (keyBeforeEntry->deviceId == -1) {
mInboundQueue.dequeueAtHead();
}
}
mInboundQueue.enqueueAtTail(entry);
traceInboundQueueLengthLocked();
switch (entry->type) {
case EventEntry::TYPE_KEY: {
// Optimize app switch latency.
// If the application takes too long to catch up then we drop all events preceding
// the app switch key.
KeyEntry* keyEntry = static_cast<KeyEntry*>(entry);
if (isAppSwitchKeyEventLocked(keyEntry)) {
if (keyEntry->action == AKEY_EVENT_ACTION_DOWN) {
mAppSwitchSawKeyDown = true;
} else if (keyEntry->action == AKEY_EVENT_ACTION_UP) {
if (mAppSwitchSawKeyDown) {
#if DEBUG_APP_SWITCH
ALOGD("App switch is pending!");
#endif
mAppSwitchDueTime = keyEntry->eventTime + APP_SWITCH_TIMEOUT;
mAppSwitchSawKeyDown = false;
needWake = true;
}
}
}
break;
}
case EventEntry::TYPE_MOTION: {
// Optimize case where the current application is unresponsive and the user
// decides to touch a window in a different application.
// If the application takes too long to catch up then we drop all events preceding
// the touch into the other window.
MotionEntry* motionEntry = static_cast<MotionEntry*>(entry);
if (motionEntry->action == AMOTION_EVENT_ACTION_DOWN
&& (motionEntry->source & AINPUT_SOURCE_CLASS_POINTER)
&& mInputTargetWaitCause == INPUT_TARGET_WAIT_CAUSE_APPLICATION_NOT_READY
&& mInputTargetWaitApplicationHandle != NULL) {
int32_t displayId = motionEntry->displayId;
int32_t x = int32_t(motionEntry->pointerCoords[0].
getAxisValue(AMOTION_EVENT_AXIS_X));
int32_t y = int32_t(motionEntry->pointerCoords[0].
getAxisValue(AMOTION_EVENT_AXIS_Y));
sp<InputWindowHandle> touchedWindowHandle = findTouchedWindowAtLocked(displayId, x, y);
if (touchedWindowHandle != NULL
&& touchedWindowHandle->inputApplicationHandle
!= mInputTargetWaitApplicationHandle) {
// User touched a different application than the one we are waiting on.
// Flag the event, and start pruning the input queue.
mNextUnblockedEvent = motionEntry;
needWake = true;
}
}
break;
}
}
return needWake;
}
needWake = true;喚醒dispatcher的執行緒
接下來
07-16 15:31:36.234 E/Looper ( 2115): Error adding epoll events for fd 41, errno=9
Looper.cpp
int Looper::addFd(int fd, int ident, int events, const sp<LooperCallback>& callback, void* data) {
#if DEBUG_CALLBACKS
ALOGD("%p ~ addFd - fd=%d, ident=%d, events=0x%x, callback=%p, data=%p", this, fd, ident,
events, callback.get(), data);
#endif
if (!callback.get()) {
if (! mAllowNonCallbacks) {
ALOGE("Invalid attempt to set NULL callback but not allowed for this looper.");
return -1;
}
if (ident < 0) {
ALOGE("Invalid attempt to set NULL callback with ident < 0.");
return -1;
}
} else {
ident = POLL_CALLBACK;
}
int epollEvents = 0;
if (events & EVENT_INPUT) epollEvents |= EPOLLIN;
if (events & EVENT_OUTPUT) epollEvents |= EPOLLOUT;
{ // acquire lock
AutoMutex _l(mLock);
Request request;
request.fd = fd;
request.ident = ident;
request.callback = callback;
request.data = data;
struct epoll_event eventItem;
memset(& eventItem, 0, sizeof(epoll_event)); // zero out unused members of data field union
eventItem.events = epollEvents;
eventItem.data.fd = fd;
ssize_t requestIndex = mRequests.indexOfKey(fd);
if (requestIndex < 0) {
int epollResult = epoll_ctl(mEpollFd, EPOLL_CTL_ADD, fd, & eventItem);
if (epollResult < 0) {
ALOGE("Error adding epoll events for fd %d, errno=%d", fd, errno);
return -1;
}
mRequests.add(fd, request);
} else {
int epollResult = epoll_ctl(mEpollFd, EPOLL_CTL_MOD, fd, & eventItem);
if (epollResult < 0) {
ALOGE("Error modifying epoll events for fd %d, errno=%d", fd, errno);
return -1;
}
mRequests.replaceValueAt(requestIndex, request);
}
} // release lock
return 1;
}
再分配鍵值的過程中,有新的監聽裝置。
究竟是誰加入fd失敗啊??? errno = 9,代表EBADF 對比下面
#define EPERM 1
#define ENOENT 2
#define ESRCH 3
#define EINTR 4
#define EIO 5
#define ENXIO 6
#define E2BIG 7
#define ENOEXEC 8
#define EBADF 9
EBADF epfd or fd is not a valid file descriptor,只能看出來這個fd是無效的
目前還看不出什麼來
接下來的日誌
07-16 15:31:36.234 W/InputEventSender( 2115): Failed to send key event on channel ‘ClientState{24ed15b0 uid 1000 pid 2115} (server)’. status=-9
07-16 15:31:36.235 W/InputMethodManager( 2115): Unable to send input event to IME: com.zxic.inputmethod.remote/.RemoteIME dropping: KeyEvent { action=ACTION_DOWN, keyCode=KEYCODE_INFO, scanCode=225, metaState=0, flags=0x8, repeatCount=0, eventTime=15693903, downTime=15693903, deviceId=1, source=0x101 }
之前懷疑不知道是哪裡發給哪裡 現在確認了 肯定傳送給輸入法失敗了,也和前面type==2對的上了。
因為新的頁面必定會觸發更新焦點的邏輯,也會通知輸入法程式重新生成inputchanel與當前的頁面建立socket通訊。
新的頁面和 inputdispatcher建立連線,新的頁面也會重新繫結輸入法,當inputdispatcher傳送鍵值時,重新註冊fd,其中輸入法是客戶端。上面出現Failed to send key event on channel ‘ClientState{24ed15b0 uid 1000 pid 2115} (server)’. status=-9幾乎可以確定是因為新的頁面和輸入法建立socket產生的。
建立新的頁面經過ViewrootImpl.java
public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) {
res = mWindowSession.addToDisplay(mWindow, mSeq, mWindowAttributes,
getHostVisibility(), mDisplay.getDisplayId(),
mAttachInfo.mContentInsets, mAttachInfo.mStableInsets, mInputChannel);}
mWindowSession.addToDisplay最終跳到WindowManagerService.java
updateFocusedWindowLocked 更新聚焦視窗
public int addWindow(Session session, IWindow client, int seq,
WindowManager.LayoutParams attrs, int viewVisibility, int displayId,
Rect outContentInsets, Rect outStableInsets, InputChannel outInputChannel) {
if (win.canReceiveKeys()) {
focusChanged = updateFocusedWindowLocked(UPDATE_FOCUS_WILL_ASSIGN_LAYERS,
false /*updateInputWindows*/);
if (focusChanged) {
imMayMove = false;
}
}
}
computeFocusedWindowLocked
findFocusedWindowLocked
mH.sendEmptyMessage(H.REPORT_FOCUS_CHANGE);
public void handleMessage(Message msg) {
switch (msg.what) {
case REPORT_FOCUS_CHANGE: {
if (newFocus != null) {
if (DEBUG_FOCUS_LIGHT) Slog.i(TAG, "Gaining focus: " + newFocus);
newFocus.reportFocusChangedSerialized(true, mInTouchMode);
notifyFocusChanged();
}
if (lastFocus != null) {
if (DEBUG_FOCUS_LIGHT) Slog.i(TAG, "Losing focus: " + lastFocus);
lastFocus.reportFocusChangedSerialized(false, mInTouchMode);
}
}
//注意mClient是在ViewrootImpl.java中建立的代理並且通過setView中ipc程序間通訊傳送到Windowmanagerservice
//關於binder作為引數的程序間通訊可以參考https://www.cnblogs.com/hrhguanli/p/4593041.html
//WindowState.java
public void reportFocusChangedSerialized(boolean focused, boolean inTouchMode) {
try {
mClient.windowFocusChanged(focused, inTouchMode);
} catch (RemoteException e) {
}
if (mFocusCallbacks != null) {
final int N = mFocusCallbacks.beginBroadcast();
for (int i=0; i<N; i++) {
IWindowFocusObserver obs = mFocusCallbacks.getBroadcastItem(i);
try {
if (focused) {
obs.focusGained(mWindowId.asBinder());
} else {
obs.focusLost(mWindowId.asBinder());
}
} catch (RemoteException e) {
}
}
mFocusCallbacks.finishBroadcast();
}
}
//ViewRootImpl.java
@Override
public void windowFocusChanged(boolean hasFocus, boolean inTouchMode) {
final ViewRootImpl viewAncestor = mViewAncestor.get();
if (viewAncestor != null) {
viewAncestor.windowFocusChanged(hasFocus, inTouchMode);
}
}
public void windowFocusChanged(boolean hasFocus, boolean inTouchMode) {
Message msg = Message.obtain();
msg.what = MSG_WINDOW_FOCUS_CHANGED;
msg.arg1 = hasFocus ? 1 : 0;
msg.arg2 = inTouchMode ? 1 : 0;
mHandler.sendMessage(msg);
}
@Override
public void handleMessage(Message msg) {
case MSG_WINDOW_FOCUS_CHANGED: {
// Note: must be done after the focus change callbacks,
// so all of the view state is set up correctly.
if (hasWindowFocus) {
if (imm != null && mLastWasImTarget && !isInLocalFocusMode()) {
imm.onWindowFocus(mView, mView.findFocus(),
mWindowAttributes.softInputMode,
!mHasHadWindowFocus, mWindowAttributes.flags);
}
// Clear the forward bit. We can just do this directly, since
// the window manager doesn't care about it.
mWindowAttributes.softInputMode &=
~WindowManager.LayoutParams.SOFT_INPUT_IS_FORWARD_NAVIGATION;
((WindowManager.LayoutParams)mView.getLayoutParams())
.softInputMode &=
~WindowManager.LayoutParams.SOFT_INPUT_IS_FORWARD_NAVIGATION;
mHasHadWindowFocus = true;
}
}
}
//InputMethodManager.java
public void onWindowFocus(View rootView, View focusedView, int softInputMode,
boolean first, int windowFlags) {
boolean forceNewFocus = false;
synchronized (mH) {
if (DEBUG) Log.v(TAG, "onWindowFocus: " + focusedView
+ " softInputMode=" + softInputMode
+ " first=" + first + " flags=#"
+ Integer.toHexString(windowFlags));
if (mHasBeenInactive) {
if (DEBUG) Log.v(TAG, "Has been inactive! Starting fresh");
mHasBeenInactive = false;
forceNewFocus = true;
}
focusInLocked(focusedView != null ? focusedView : rootView);
}
int controlFlags = 0;
if (focusedView != null) {
controlFlags |= CONTROL_WINDOW_VIEW_HAS_FOCUS;
if (focusedView.onCheckIsTextEditor()) {
controlFlags |= CONTROL_WINDOW_IS_TEXT_EDITOR;
}
}
if (first) {
controlFlags |= CONTROL_WINDOW_FIRST;
}
if (checkFocusNoStartInput(forceNewFocus, true)) {
// We need to restart input on the current focus view. This
// should be done in conjunction with telling the system service
// about the window gaining focus, to help make the transition
// smooth.
if (startInputInner(rootView.getWindowToken(),
controlFlags, softInputMode, windowFlags)) {
return;
}
}
// For some reason we didn't do a startInput + windowFocusGain, so
// we'll just do a window focus gain and call it a day.
synchronized (mH) {
try {
if (DEBUG) Log.v(TAG, "Reporting focus gain, without startInput");
mService.windowGainedFocus(mClient, rootView.getWindowToken(),
controlFlags, softInputMode, windowFlags, null, null);
} catch (RemoteException e) {
}
}
}
focusInLocked
static void scheduleCheckFocusLocked(View view) {
ViewRootImpl viewRootImpl = view.getViewRootImpl();
if (viewRootImpl != null) {
viewRootImpl.dispatchCheckFocus();
}
}
//viewRootImpl.java
public void dispatchCheckFocus() {
if (!mHandler.hasMessages(MSG_CHECK_FOCUS)) {
// This will result in a call to checkFocus() below.
mHandler.sendEmptyMessage(MSG_CHECK_FOCUS);
}
}
case MSG_CHECK_FOCUS: {
InputMethodManager imm = InputMethodManager.peekInstance();
if (imm != null) {
imm.checkFocus();
}
} break;
//InputMethodManager.java
/**
* @hide
*/
public void checkFocus() {
if (checkFocusNoStartInput(false, true)) {
startInputInner(null, 0, 0, 0);
}
}
boolean startInputInner(IBinder windowGainingFocus, int controlFlags, int softInputMode,
int windowFlags) {
try {
if (DEBUG) Log.v(TAG, "START INPUT: " + view + " ic="
+ ic + " tba=" + tba + " controlFlags=#"
+ Integer.toHexString(controlFlags));
InputBindResult res;
if (windowGainingFocus != null) {
res = mService.windowGainedFocus(mClient, windowGainingFocus,
controlFlags, softInputMode, windowFlags,
tba, servedContext);
} else {
res = mService.startInput(mClient,
servedContext, tba, controlFlags);
}
if (DEBUG) Log.v(TAG, "Starting input: Bind result=" + res);
if (res != null) {
if (res.id != null) {
setInputChannelLocked(res.channel);
mBindSequence = res.sequence;
mCurMethod = res.method;
mCurId = res.id;
mNextUserActionNotificationSequenceNumber =
res.userActionNotificationSequenceNumber;
} else {
if (res.channel != null && res.channel != mCurChannel) {
res.channel.dispose();
}
if (mCurMethod == null) {
// This means there is no input method available.
if (DEBUG) Log.v(TAG, "ABORT input: no input method!");
return true;
}
}
}
if (mCurMethod != null && mCompletions != null) {
try {
mCurMethod.displayCompletions(mCompletions);
} catch (RemoteException e) {
}
}
} catch (RemoteException e) {
Log.w(TAG, "IME died: " + mCurId, e);
}
}
//startInputInner() 來繫結輸入法, startInputInner 中的setInputChannelLocked(res.channel);出現了channel,有了channel 就有socket的fd
//其中res 是由
if (windowGainingFocus != null) {
res = mService.windowGainedFocus(mClient, windowGainingFocus,
controlFlags, softInputMode, windowFlags,
tba, servedContext);
}
//或者
res = mService.startInput(mClient,
servedContext, tba, controlFlags);
//看下res的生成
//InputMethodManagerService.java
@Override
public InputBindResult startInput(IInputMethodClient client,
IInputContext inputContext, EditorInfo attribute, int controlFlags) {
if (!calledFromValidUser()) {
return null;
}
synchronized (mMethodMap) {
final long ident = Binder.clearCallingIdentity();
try {
return startInputLocked(client, inputContext, attribute, controlFlags);
} finally {
Binder.restoreCallingIdentity(ident);
}
}
}
startInputUncheckedLocked
InputBindResult startInputUncheckedLocked(ClientState cs,
IInputContext inputContext, EditorInfo attribute, int controlFlags) {
if (mHaveConnection) {
if (mCurMethod != null) {
// Return to client, and we will get back with it when
// we have had a session made for it.
requestClientSessionLocked(cs);
return new InputBindResult(null, null, mCurId, mCurSeq,
mCurUserActionNotificationSequenceNumber);
} else if (SystemClock.uptimeMillis()
< (mLastBindTime+TIME_TO_RECONNECT)) {
// In this case we have connected to the service, but
// don't yet have its interface. If it hasn't been too
// long since we did the connection, we'll return to
// the client and wait to get the service interface so
// we can report back. If it has been too long, we want
// to fall through so we can try a disconnect/reconnect
// to see if we can get back in touch with the service.
return new InputBindResult(null, null, mCurId, mCurSeq,
mCurUserActionNotificationSequenceNumber);
} else {
EventLog.writeEvent(EventLogTags.IMF_FORCE_RECONNECT_IME,
mCurMethodId, SystemClock.uptimeMillis()-mLastBindTime, 0);
}
}
return startInputInnerLocked();
}
//requestClientSessionLocked 最終這裡生成inputchannel 打開了socket
//看怎麼處理channnel的 為什麼兩個channel都傳?其中服務端的channel塞進MethodCallback
void requestClientSessionLocked(ClientState cs) {
if (!cs.sessionRequested) {
if (DEBUG) Slog.v(TAG, "Creating new session for client " + cs);
InputChannel[] channels = InputChannel.openInputChannelPair(cs.toString());
cs.sessionRequested = true;
executeOrSendMessage(mCurMethod, mCaller.obtainMessageOOO(
MSG_CREATE_SESSION, mCurMethod, channels[1],
new MethodCallback(this, mCurMethod, channels[0])));
}
}
case MSG_CREATE_SESSION: {
args = (SomeArgs)msg.obj;
IInputMethod method = (IInputMethod)args.arg1;
InputChannel channel = (InputChannel)args.arg2;
try {
method.createSession(channel, (IInputSessionCallback)args.arg3);
} catch (RemoteException e) {
} finally {
// Dispose the channel if the input method is not local to this process
// because the remote proxy will get its own copy when unparceled.
if (channel != null && Binder.isProxy(method)) {
channel.dispose();
}
}
args.recycle();
return true;
}
//上面是IMMS端,下面就看IMS輸入法端的處理
// IInputMethodWrapper.java
@Override
public void createSession(InputChannel channel, IInputSessionCallback callback) {
mCaller.executeOrSendMessage(mCaller.obtainMessageOO(DO_CREATE_SESSION,
channel, callback));
}
case DO_CREATE_SESSION: {
SomeArgs args = (SomeArgs)msg.obj;
inputMethod.createSession(new InputMethodSessionCallbackWrapper(
mContext, (InputChannel)args.arg1,
(IInputSessionCallback)args.arg2));
args.recycle();
return;
}
// AbstractInputMethodService.java
//有個內部類 實現了InputMethod
public abstract class AbstractInputMethodImpl implements InputMethod {
/**
* Instantiate a new client session for the input method, by calling
* back to {@link AbstractInputMethodService#onCreateInputMethodSessionInterface()
* AbstractInputMethodService.onCreateInputMethodSessionInterface()}.
*/
public void createSession(SessionCallback callback) {
callback.sessionCreated(onCreateInputMethodSessionInterface());
}
//InputMethodService.java:
@Override
public AbstractInputMethodSessionImpl onCreateInputMethodSessionInterface() {
return new InputMethodSessionImpl();
}
// InputMethodManagerService.java
@Override
public void sessionCreated(IInputMethodSession session) {
long ident = Binder.clearCallingIdentity();
try {
//mChannel服務端
mParentIMMS.onSessionCreated(mMethod, session, mChannel);
} finally {
Binder.restoreCallingIdentity(ident);
}
void onSessionCreated(IInputMethod method, IInputMethodSession session,
InputChannel channel) {
synchronized (mMethodMap) {
if (mCurMethod != null && method != null
&& mCurMethod.asBinder() == method.asBinder()) {
if (mCurClient != null) {
clearClientSessionLocked(mCurClient);
mCurClient.curSession = new SessionState(mCurClient,
method, session, channel);
InputBindResult res = attachNewInputLocked(true);
if (res.method != null) {
executeOrSendMessage(mCurClient.client, mCaller.obtainMessageOO(
MSG_BIND_METHOD, mCurClient.client, res));
}
return;
}
}
}
// Session abandoned. Close its associated input channel.
channel.dispose();
}
// 輸入法和view繫結 完全不懂 為什麼之前建立了渠道了還重新new一個channel 且fd和之前一樣
InputBindResult attachNewInputLocked(boolean initial) {
if (!mBoundToMethod) {
executeOrSendMessage(mCurMethod, mCaller.obtainMessageOO(
MSG_BIND_INPUT, mCurMethod, mCurClient.binding));
mBoundToMethod = true;
}
final SessionState session = mCurClient.curSession;
if (initial) {
executeOrSendMessage(session.method, mCaller.obtainMessageOOO(
MSG_START_INPUT, session, mCurInputContext, mCurAttribute));
} else {
executeOrSendMessage(session.method, mCaller.obtainMessageOOO(
MSG_RESTART_INPUT, session, mCurInputContext, mCurAttribute));
}
if (mShowRequested) {
if (DEBUG) Slog.v(TAG, "Attach new input asks to show input");
showCurrentInputLocked(getAppShowFlags(), null);
}
return new InputBindResult(session.session,
(session.channel != null ? session.channel.dup() : null),
mCurId, mCurSeq, mCurUserActionNotificationSequenceNumber);
}
/* Returns a new object that has a duplicate of this channel's fd. */返回一個新的物件,該物件具有該通道的FD的副本。
sp<InputChannel> dup() const;
//回到 startinputinner setInputChannelLocked(res.channel); 這裡獲取channel 且設定為mCurChannel
//目前為止都只是生成fd還沒加入looper中,那fd是時候加入???前面提到是新的頁面的key事件傳給輸入法的時候。
//接下來看key事件傳遞
viewrootimpl.java
//鍵值回撥物件,在setview的時候生成
mInputEventReceiver = new WindowInputEventReceiver(mInputChannel,
Looper.myLooper());
@Override
public void onInputEvent(InputEvent event) {
enqueueInputEvent(event, this, 0, true);
}
void enqueueInputEvent(InputEvent event,
InputEventReceiver receiver, int flags, boolean processImmediately) {
QueuedInputEvent q = obtainQueuedInputEvent(event, receiver, flags);
// Always enqueue the input event in order, regardless of its time stamp.
// We do this because the application or the IME may inject key events
// in response to touch events and we want to ensure that the injected keys
// are processed in the order they were received and we cannot trust that
// the time stamp of injected events are monotonic.
QueuedInputEvent last = mPendingInputEventTail;
if (last == null) {
mPendingInputEventHead = q;
mPendingInputEventTail = q;
} else {
last.mNext = q;
mPendingInputEventTail = q;
}
mPendingInputEventCount += 1;
Trace.traceCounter(Trace.TRACE_TAG_INPUT, mPendingInputEventQueueLengthCounterName,
mPendingInputEventCount);
if (processImmediately) {
doProcessInputEvents();
} else {
scheduleProcessInputEvents();
}
}
void doProcessInputEvents() {
// Deliver all pending input events in the queue.
while (mPendingInputEventHead != null) {
QueuedInputEvent q = mPendingInputEventHead;
mPendingInputEventHead = q.mNext;
if (mPendingInputEventHead == null) {
mPendingInputEventTail = null;
}
q.mNext = null;
mPendingInputEventCount -= 1;
Trace.traceCounter(Trace.TRACE_TAG_INPUT, mPendingInputEventQueueLengthCounterName,
mPendingInputEventCount);
deliverInputEvent(q);
}
// We are done processing all input events that we can process right now
// so we can clear the pending flag immediately.
if (mProcessInputEventsScheduled) {
mProcessInputEventsScheduled = false;
mHandler.removeMessages(MSG_PROCESS_INPUT_EVENTS);
}
}
private void deliverInputEvent(QueuedInputEvent q) {
Trace.asyncTraceBegin(Trace.TRACE_TAG_VIEW, "deliverInputEvent",
q.mEvent.getSequenceNumber());
if (mInputEventConsistencyVerifier != null) {
mInputEventConsistencyVerifier.onInputEvent(q.mEvent, 0);
}
InputStage stage;
if (q.shouldSendToSynthesizer()) {
stage = mSyntheticInputStage;
} else {
stage = q.shouldSkipIme() ? mFirstPostImeInputStage : mFirstInputStage;
}
if (stage != null) {
stage.deliver(q);
} else {
finishInputEvent(q);
}
}
/**
* Delivers an event to be processed.
*/
public final void deliver(QueuedInputEvent q) {
if ((q.mFlags & QueuedInputEvent.FLAG_FINISHED) != 0) {
forward(q);
} else if (shouldDropInputEvent(q)) {
finish(q, false);
} else {
apply(q, onProcess(q));
}
}
@Override
protected int onProcess(QueuedInputEvent q) {
if (mLastWasImTarget && !isInLocalFocusMode()) {
InputMethodManager imm = InputMethodManager.peekInstance();
if (imm != null) {
final InputEvent event = q.mEvent;
if (DEBUG_IMF) Log.v(TAG, "Sending input event to IME: " + event);
int result = imm.dispatchInputEvent(event, q, this, mHandler);
if (result == InputMethodManager.DISPATCH_HANDLED) {
return FINISH_HANDLED;
} else if (result == InputMethodManager.DISPATCH_NOT_HANDLED) {
// The IME could not handle it, so skip along to the next InputStage
return FORWARD;
} else {
return DEFER; // callback will be invoked later
}
}
}
return FORWARD;
}
//InputMethodManager.java
/**
* Dispatches an input event to the IME.
*
* Returns {@link #DISPATCH_HANDLED} if the event was handled.
* Returns {@link #DISPATCH_NOT_HANDLED} if the event was not handled.
* Returns {@link #DISPATCH_IN_PROGRESS} if the event is in progress and the
* callback will be invoked later.
*
* @hide
*/
public int dispatchInputEvent(InputEvent event, Object token,
FinishedInputEventCallback callback, Handler handler) {
synchronized (mH) {
if (mCurMethod != null) {
if (event instanceof KeyEvent) {
KeyEvent keyEvent = (KeyEvent)event;
if (keyEvent.getAction() == KeyEvent.ACTION_DOWN
&& keyEvent.getKeyCode() == KeyEvent.KEYCODE_SYM
&& keyEvent.getRepeatCount() == 0) {
showInputMethodPickerLocked();
return DISPATCH_HANDLED;
}
}
if (DEBUG) Log.v(TAG, "DISPATCH INPUT EVENT: " + mCurMethod);
PendingEvent p = obtainPendingEventLocked(
event, token, mCurId, callback, handler);
if (mMainLooper.isCurrentThread()) {
// Already running on the IMM thread so we can send the event immediately.
return sendInputEventOnMainLooperLocked(p);
}
// Post the event to the IMM thread.
Message msg = mH.obtainMessage(MSG_SEND_INPUT_EVENT, p);
msg.setAsynchronous(true);
mH.sendMessage(msg);
return DISPATCH_IN_PROGRESS;
}
}
return DISPATCH_NOT_HANDLED;
}
// Must be called on the main looper
int sendInputEventOnMainLooperLocked(PendingEvent p) {
if (mCurChannel != null) {
if (mCurSender == null) {
mCurSender = new ImeInputEventSender(mCurChannel, mH.getLooper());
}
final InputEvent event = p.mEvent;
final int seq = event.getSequenceNumber();
if (mCurSender.sendInputEvent(seq, event)) {
mPendingEvents.put(seq, p);
Trace.traceCounter(Trace.TRACE_TAG_INPUT, PENDING_EVENT_COUNTER,
mPendingEvents.size());
Message msg = mH.obtainMessage(MSG_TIMEOUT_INPUT_EVENT, p);
msg.setAsynchronous(true);
mH.sendMessageDelayed(msg, INPUT_METHOD_NOT_RESPONDING_TIMEOUT);
return DISPATCH_IN_PROGRESS;
}
Log.w(TAG, "Unable to send input event to IME: "
+ mCurId + " dropping: " + event);
}
return DISPATCH_NOT_HANDLED;
}
// mCurSender = new ImeInputEventSender(mCurChannel, mH.getLooper());
//ImeInputEventSender 繼承與InputEventSender
//當new一個InputEventSender時會呼叫到native方法 nativeInit
mSenderPtr = nativeInit(new WeakReference<InputEventSender>(this),
inputChannel, mMessageQueue);
//android_view_InputEventReceiver.cpp
static jlong nativeInit(JNIEnv* env, jclass clazz, jobject senderWeak,
jobject inputChannelObj, jobject messageQueueObj) {
sp<InputChannel> inputChannel = android_view_InputChannel_getInputChannel(env,
inputChannelObj);
if (inputChannel == NULL) {
jniThrowRuntimeException(env, "InputChannel is not initialized.");
return 0;
}
sp<MessageQueue> messageQueue = android_os_MessageQueue_getMessageQueue(env, messageQueueObj);
if (messageQueue == NULL) {
jniThrowRuntimeException(env, "MessageQueue is not initialized.");
return 0;
}
sp<NativeInputEventSender> sender = new NativeInputEventSender(env,
senderWeak, inputChannel, messageQueue);
status_t status =