安卓框架,分析解决项目中出现的anr

对学习anr很好的一篇文章https://blog.csdn.net/qiujiwuhen00/article/details/47043477
先看下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/system@framework@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/system@framework@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 android.view.ViewRootImpl@1cfd7704
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产生的。

根据以上逻辑来看下源码,参考https://blog.csdn.net/jieqiong1/article/details/71262987
首先是新页面重新绑定输入法。

创建新的页面经过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 = sender->initialize();
    if (status) {
        String8 message;
        message.appendFormat("Failed to initialize input event sender.  status=%d", status);
        jniThrowRuntimeException(env, message.string());
        return 0;
    }

    sender->incStrong(gInputEventSenderClassInfo.clazz); // retain a reference for the object
    return reinterpret_cast<jlong>(sender.get());
}

status_t NativeInputEventSender::initialize() {
    int receiveFd = mInputPublisher.getChannel()->getFd();
    mMessageQueue->getLooper()->addFd(receiveFd, 0, ALOOPER_EVENT_INPUT, this, NULL);
    return OK;
}

//终于可以看到mMessageQueue->getLooper()->addFd(receiveFd, 0, ALOOPER_EVENT_INPUT, this, NULL);
//说明第一次有键值时才会加入fd
//和日志所看到的现象符合的,因为新页面出现,重新绑定输入法,又因为有键过来所以addFd且出现异常。
void NativeInputEventReceiver::setFdEvents(int events) {
    if (mFdEvents != events) {
        mFdEvents = events;
        int fd = mInputConsumer.getChannel()->getFd();
        if (events) {
            mMessageQueue->getLooper()->addFd(fd, 0, events, this, NULL);
        } else {
            mMessageQueue->getLooper()->removeFd(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

tatus_t NativeInputEventSender::sendKeyEvent(uint32_t seq, const KeyEvent* event) {
#if DEBUG_DISPATCH_CYCLE
    ALOGD("channel '%s' ~ Sending key event, seq=%u.", getInputChannelName(), seq);
#endif

    uint32_t publishedSeq = mNextPublishedSeq++;
    status_t status = mInputPublisher.publishKeyEvent(publishedSeq,
            event->getDeviceId(), event->getSource(), event->getAction(), event->getFlags(),
            event->getKeyCode(), event->getScanCode(), event->getMetaState(),
            event->getRepeatCount(), event->getDownTime(), event->getEventTime());
    if (status) {
        ALOGW("Failed to send key event on channel '%s'.  status=%d",
                getInputChannelName(), status);
        return status;
    }
    mPublishedSeqMap.add(publishedSeq, seq);
    return OK;
}

因为发送失败result == InputMethodManager.DISPATCH_NOT_HANDLED
所以return FORWARD;交给下一个inputstage,如果没有下一个就结束当前的event 且调用finishinput

        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;
        }

        protected void apply(QueuedInputEvent q, int result) {
            if (result == FORWARD) {
                forward(q);
            } else if (result == FINISH_HANDLED) {
                finish(q, true);
            } else if (result == FINISH_NOT_HANDLED) {
                finish(q, false);
            } else {
                throw new IllegalArgumentException("Invalid result: " + result);
            }
        }

跟着日志看 key没有传递给输入法,倒是传递给了activity。
07-16 15:31:36.236 I/当前activity( 2115): onKeyDown. keyCode:165
最后通过finishInputEvent通知inputdispatcher.cpp

    private void finishInputEvent(QueuedInputEvent q) {
        Trace.asyncTraceEnd(Trace.TRACE_TAG_VIEW, "deliverInputEvent",
                q.mEvent.getSequenceNumber());

        if (q.mReceiver != null) {
            boolean handled = (q.mFlags & QueuedInputEvent.FLAG_FINISHED_HANDLED) != 0;
            q.mReceiver.finishInputEvent(q.mEvent, handled);
        } else {
            q.mEvent.recycleIfNeededAfterDispatch();
        }

        recycleQueuedInputEvent(q);
    }

接着日志
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!

虽然之前addfd出错了 但是接下来发送key给输入法是成功,最后通过finishInputEvent通知inputdispatcher.cpp,因为没有之前报错日志,但却是冒出新的错误,是Inputdispatcher.cpp处理其它进程消费了键值后出现的。

//Inputdispatcher.cpp

int InputDispatcher::handleReceiveCallback(int fd, int events, void* data) {
    InputDispatcher* d = static_cast<InputDispatcher*>(data);

    { // acquire lock
        AutoMutex _l(d->mLock);

        ssize_t connectionIndex = d->mConnectionsByFd.indexOfKey(fd);
        if (connectionIndex < 0) {
            ALOGE("Received spurious receive callback for unknown input channel.  "
                    "fd=%d, events=0x%x", fd, events);
            return 0; // remove the callback
        }

        bool notify;
        sp<Connection> connection = d->mConnectionsByFd.valueAt(connectionIndex);
        if (!(events & (ALOOPER_EVENT_ERROR | ALOOPER_EVENT_HANGUP))) {
            if (!(events & ALOOPER_EVENT_INPUT)) {
                ALOGW("channel '%s' ~ Received spurious callback for unhandled poll event.  "
                        "events=0x%x", connection->getInputChannelName(), events);
                return 1;
            }

            nsecs_t currentTime = now();
            bool gotOne = false;
            status_t status;
            for (;;) {
                uint32_t seq;
                bool handled;
                status = connection->inputPublisher.receiveFinishedSignal(&seq, &handled);
                if (status) {
                    break;
                }
                d->finishDispatchCycleLocked(currentTime, connection, seq, handled);
                gotOne = true;
            }
            if (gotOne) {
                d->runCommandsLockedInterruptible();
                if (status == WOULD_BLOCK) {
                    return 1;
                }
            }

            notify = status != DEAD_OBJECT || !connection->monitor;
            if (notify) {
                ALOGE("channel '%s' ~ Failed to receive finished signal.  status=%d",
                        connection->getInputChannelName(), status);
            }
        } else {
            // Monitor channels are never explicitly unregistered.
            // We do it automatically when the remote endpoint is closed so don't warn
            // about them.
            notify = !connection->monitor;
            if (notify) {
                ALOGW("channel '%s' ~ Consumer closed input channel or an error occurred.  "
                        "events=0x%x", connection->getInputChannelName(), events);
            }
        }

        // Unregister the channel.
        d->unregisterInputChannelLocked(connection->inputChannel, notify);
        return 0; // remove the callback
    } // release lock
}


//07-16 15:31:36.577 E/InputTransport( 1775): channel '2d826e61 你的apk和包名 (server)' publisher ~ Received unexpected message of type 1 from consumer
//这条日志出现在 status = connection->inputPublisher.receiveFinishedSignal(&seq, &handled);

status_t InputPublisher::receiveFinishedSignal(uint32_t* outSeq, bool* outHandled) {
#if DEBUG_TRANSPORT_ACTIONS
    ALOGD("channel '%s' publisher ~ receiveFinishedSignal",
            mChannel->getName().string());
#endif

    InputMessage msg;
    status_t result = mChannel->receiveMessage(&msg);
    if (result) {
        *outSeq = 0;
        *outHandled = false;
        return result;
    }
    if (msg.header.type != InputMessage::TYPE_FINISHED) {
        ALOGE("channel '%s' publisher ~ Received unexpected message of type %d from consumer",
                mChannel->getName().string(), msg.header.type);
        return UNKNOWN_ERROR;
    }
    *outSeq = msg.body.finished.seq;
    *outHandled = msg.body.finished.handled;
    return OK;
}

//可以看出来当msg.header.type != InputMessage::TYPE_FINISHED时就会报UNKNOWN_ERROR,且根据type = 1,我们知道是键盘类型。
//这样问题就很奇怪了,因为所有调用finishInputEvent的最后会设置msg.header.type = InputMessage::TYPE_FINISHED

跟踪finishInputEvent的代码,最后是进入sendUnchainedFinishedSignal,根本不可能type的类型是键盘,而且搜索有可能为msg.header.type 赋值为键盘类型的地方只有publishKeyEvent,而publishKeyEvent是服务端发送键值给客户端的,因为日志消息较小且这种anr难复现,在这里只能猜测是这个时段只有InputDispatcher 向 当前页面发送键值 和 当前页面向输入法发送键值 这两只情况是用到了publishKeyEvent,而第一种情况是不可能的,所以推论是第二次传递的key的socket fd用的是当前页面通知inputdispatcher的fd。

status_t InputConsumer::sendUnchainedFinishedSignal(uint32_t seq, bool handled) {
    InputMessage msg;
    msg.header.type = InputMessage::TYPE_FINISHED;
    msg.body.finished.seq = seq;
    msg.body.finished.handled = handled;
    return mChannel->sendMessage(&msg);
}

//只有发布键值时才会赋值为InputMessage::TYPE_KEY
status_t InputPublisher::publishKeyEvent(
        uint32_t seq,
        int32_t deviceId,
        int32_t source,
        int32_t action,
        int32_t flags,
        int32_t keyCode,
        int32_t scanCode,
        int32_t metaState,
        int32_t repeatCount,
        nsecs_t downTime,
        nsecs_t eventTime) {
#if DEBUG_TRANSPORT_ACTIONS
    ALOGD("channel '%s' publisher ~ publishKeyEvent: seq=%u, deviceId=%d, source=0x%x, "
            "action=0x%x, flags=0x%x, keyCode=%d, scanCode=%d, metaState=0x%x, repeatCount=%d,"
            "downTime=%lld, eventTime=%lld",
            mChannel->getName().string(), seq,
            deviceId, source, action, flags, keyCode, scanCode, metaState, repeatCount,
            downTime, eventTime);
#endif

    if (!seq) {
        ALOGE("Attempted to publish a key event with sequence number 0.");
        return BAD_VALUE;
    }

    InputMessage msg;
    msg.header.type = InputMessage::TYPE_KEY;
    msg.body.key.seq = seq;
    msg.body.key.deviceId = deviceId;
    msg.body.key.source = source;
    msg.body.key.action = action;
    msg.body.key.flags = flags;
    msg.body.key.keyCode = keyCode;
    msg.body.key.scanCode = scanCode;
    msg.body.key.metaState = metaState;
    msg.body.key.repeatCount = repeatCount;
    msg.body.key.downTime = downTime;
    msg.body.key.eventTime = eventTime;
    return mChannel->sendMessage(&msg);
}

其实到这里已经发现问题和linux内核bug有很大关系,因为fd都是系统分配的,为何错乱这里应该交给驱动的同事去排查下socket fd的问题,但是关于出现anr的直接原因还没分析完,出于不整明白不舒服的心情,还是硬着头皮继续分析下去。

—————————-接下来是分析发生anr的直接原因

接着看日志
07-16 15:31:36.577 E/InputDispatcher( 1775): channel ‘2d826e61 你的apk和包名 (server)’ ~ Channel is unrecoverably broken and will be disposed!

        //handleReceiveCallback
        //Unregister the channel.
        //如果接受出错就要销毁注册
        d->unregisterInputChannelLocked(connection->inputChannel, notify);


//InputDispatcher.cpp
status_t InputDispatcher::unregisterInputChannelLocked(const sp<InputChannel>& inputChannel,
        bool notify) {
    ssize_t connectionIndex = getConnectionIndexLocked(inputChannel);
    if (connectionIndex < 0) {
        ALOGW("Attempted to unregister already unregistered input channel '%s'",
                inputChannel->getName().string());
        return BAD_VALUE;
    }

    //标记下 这里是问题关键 移除connection 
    sp<Connection> connection = mConnectionsByFd.valueAt(connectionIndex);
    mConnectionsByFd.removeItemsAt(connectionIndex);

    if (connection->monitor) {
        removeMonitorChannelLocked(inputChannel);
    }

    //标记下 这里是问题关键 移除fd
    mLooper->removeFd(inputChannel->getFd());

    nsecs_t currentTime = now();
    abortBrokenDispatchCycleLocked(currentTime, connection, notify);

    connection->status = Connection::STATUS_ZOMBIE;
    return OK;
}



void InputDispatcher::abortBrokenDispatchCycleLocked(nsecs_t currentTime,
        const sp<Connection>& connection, bool notify) {
#if DEBUG_DISPATCH_CYCLE
    ALOGD("channel '%s' ~ abortBrokenDispatchCycle - notify=%s",
            connection->getInputChannelName(), toString(notify));
#endif

    // Clear the dispatch queues.
    drainDispatchQueueLocked(&connection->outboundQueue);
    traceOutboundQueueLengthLocked(connection);
    drainDispatchQueueLocked(&connection->waitQueue);
    traceWaitQueueLengthLocked(connection);

    // The connection appears to be unrecoverably broken.
    // Ignore already broken or zombie connections.
    if (connection->status == Connection::STATUS_NORMAL) {
        connection->status = Connection::STATUS_BROKEN;

        if (notify) {
            // Notify other system components.
            onDispatchCycleBrokenLocked(currentTime, connection);
        }
    }
}



void InputDispatcher::onDispatchCycleBrokenLocked(
        nsecs_t currentTime, const sp<Connection>& connection) {
    ALOGE("channel '%s' ~ Channel is unrecoverably broken and will be disposed!",
            connection->getInputChannelName());

    CommandEntry* commandEntry = postCommandLocked(
            & InputDispatcher::doNotifyInputChannelBrokenLockedInterruptible);
    commandEntry->connection = connection;
}


void InputDispatcher::doNotifyInputChannelBrokenLockedInterruptible(
        CommandEntry* commandEntry) {
    sp<Connection> connection = commandEntry->connection;

    if (connection->status != Connection::STATUS_ZOMBIE) {
        mLock.unlock();

    //com_android_server_input_InputManagerService.cpp中传进来的mpolicy
        mPolicy->notifyInputChannelBroken(connection->inputWindowHandle);

        mLock.lock();
    }
}


//com_android_server_input_InputManagerService.cpp
void NativeInputManager::notifyInputChannelBroken(const sp<InputWindowHandle>& inputWindowHandle) {
#if DEBUG_INPUT_DISPATCHER_POLICY
    ALOGD("notifyInputChannelBroken");
#endif

    JNIEnv* env = jniEnv();

    jobject inputWindowHandleObj =
            getInputWindowHandleObjLocalRef(env, inputWindowHandle);
    if (inputWindowHandleObj) {
    //jni 调用 java
        env->CallVoidMethod(mServiceObj, gServiceClassInfo.notifyInputChannelBroken,
                inputWindowHandleObj);
        checkAndClearExceptionFromCallback(env, "notifyInputChannelBroken");

        env->DeleteLocalRef(inputWindowHandleObj);
    }
}


//InputManagerService.java
    // Native callback.
    private void notifyInputChannelBroken(InputWindowHandle inputWindowHandle) {
        mWindowManagerCallbacks.notifyInputChannelBroken(inputWindowHandle);
    }

//mWindowManagerCallbacks 是由systemserver.java设置

inputManager.setWindowManagerCallbacks(wm.getInputMonitor());

//WindowManagerService.java
  public InputMonitor getInputMonitor() {
        return mInputMonitor;
    }

//最终调用的是
//InputMonitor.java
    /* Notifies the window manager about a broken input channel.
     * 
     * Called by the InputManager.
     */
    @Override
    public void notifyInputChannelBroken(InputWindowHandle inputWindowHandle) {
        if (inputWindowHandle == null) {
            return;
        }

        synchronized (mService.mWindowMap) {
            WindowState windowState = (WindowState) inputWindowHandle.windowState;
            if (windowState != null) {
                Slog.i(WindowManagerService.TAG, "WINDOW DIED " + windowState);
                mService.removeWindowLocked(windowState.mSession, windowState);
            }
        }
    }


//WindowManagerService.java

public void removeWindowLocked(Session session, WindowState win) {
        if (win.mAttrs.type == TYPE_APPLICATION_STARTING) {
            if (DEBUG_STARTING_WINDOW) Slog.d(TAG, "Starting window removed " + win);
        }

        if (localLOGV || DEBUG_FOCUS || DEBUG_FOCUS_LIGHT && win==mCurrentFocus) Slog.v(
                TAG, "Remove " + win + " client="
                + Integer.toHexString(System.identityHashCode(win.mClient.asBinder()))
                + ", surface=" + win.mWinAnimator.mSurfaceControl + " Callers="
                + Debug.getCallers(4));

        final long origId = Binder.clearCallingIdentity();

        win.disposeInputChannel();//销毁inputchanel,且销毁注册,这里还是会调用到Inputdispatcher,之前inputdispatcher已经移除过该inputchanel,再次移除应当报错才对,但很奇怪没有报错

        if (DEBUG_APP_TRANSITIONS) Slog.v(
                TAG, "Remove " + win + ": mSurface=" + win.mWinAnimator.mSurfaceControl
                + " mExiting=" + win.mExiting
                + " isAnimating=" + win.mWinAnimator.isAnimating()
                + " app-animation="
                + (win.mAppToken != null ? win.mAppToken.mAppAnimator.animation : null)
                + " inPendingTransaction="
                + (win.mAppToken != null ? win.mAppToken.inPendingTransaction : false)
                + " mDisplayFrozen=" + mDisplayFrozen);
        // Visibility of the removed window. Will be used later to update orientation later on.
        boolean wasVisible = false;
        // First, see if we need to run an animation.  If we do, we have
        // to hold off on removing the window until the animation is done.
        // If the display is frozen, just remove immediately, since the
        // animation wouldn't be seen.
        if (win.mHasSurface && okToDisplay()) {
            // If we are not currently running the exit animation, we
            // need to see about starting one.
            wasVisible = win.isWinVisibleLw();
            if (wasVisible) {

                int transit = WindowManagerPolicy.TRANSIT_EXIT;
                if (win.mAttrs.type == TYPE_APPLICATION_STARTING) {
                    transit = WindowManagerPolicy.TRANSIT_PREVIEW_DONE;
                }
                // Try starting an animation.
                if (win.mWinAnimator.applyAnimationLocked(transit, false)) {
                    win.mExiting = true;
                }
                //TODO (multidisplay): Magnification is supported only for the default display.
                if (mAccessibilityController != null
                        && win.getDisplayId() == Display.DEFAULT_DISPLAY) {
                    mAccessibilityController.onWindowTransitionLocked(win, transit);
                }
            }
            if (win.mExiting || win.mWinAnimator.isAnimating()) {
                // The exit animation is running... wait for it!
                //Slog.i(TAG, "*** Running exit animation...");
                win.mExiting = true;
                win.mRemoveOnExit = true;
                final DisplayContent displayContent = win.getDisplayContent();
                if (displayContent != null) {
                    displayContent.layoutNeeded = true;
                }
                //也是更新窗口不包括更新inputdispatcher
                final boolean focusChanged = updateFocusedWindowLocked(
                        UPDATE_FOCUS_WILL_PLACE_SURFACES, false /*updateInputWindows*/);
                performLayoutAndPlaceSurfacesLocked();
                if (win.mAppToken != null) {
                    win.mAppToken.updateReportedVisibilityLocked();
                }
                if (focusChanged) {
                //更新inputdispatcher的窗口
                    mInputMonitor.updateInputWindowsLw(false /*force*/);
                }
                //dump();
                Binder.restoreCallingIdentity(origId);
                return;
            }
        }

        removeWindowInnerLocked(session, win);
        // Removing a visible window will effect the computed orientation
        // So just update orientation if needed.
        if (wasVisible && updateOrientationFromAppTokensLocked(false)) {
            mH.sendEmptyMessage(H.SEND_NEW_CONFIGURATION);
        }
        updateFocusedWindowLocked(UPDATE_FOCUS_NORMAL, true /*updateInputWindows*/);
        Binder.restoreCallingIdentity(origId);
    }


    void removeWindowInnerLocked(Session session, WindowState win) {
        if (win.mRemoved) {
            // Nothing to do.
            return;
        }

        for (int i=win.mChildWindows.size()-1; i>=0; i--) {
            WindowState cwin = win.mChildWindows.get(i);
            Slog.w(TAG, "Force-removing child win " + cwin + " from container "
                    + win);
            removeWindowInnerLocked(cwin.mSession, cwin);
        }

        win.mRemoved = true;

        if (mInputMethodTarget == win) {
            moveInputMethodWindowsIfNeededLocked(false);
        }

        if (false) {
            RuntimeException e = new RuntimeException("here");
            e.fillInStackTrace();
            Slog.w(TAG, "Removing window " + win, e);
        }

        mPolicy.removeWindowLw(win);
        win.removeLocked();

        if (DEBUG_ADD_REMOVE) Slog.v(TAG, "removeWindowInnerLocked: " + win);
        mWindowMap.remove(win.mClient.asBinder());
        if (win.mAppOp != AppOpsManager.OP_NONE) {
            mAppOps.finishOp(win.mAppOp, win.getOwningUid(), win.getOwningPackage());
        }

        mPendingRemove.remove(win);
        mResizingWindows.remove(win);
        mWindowsChanged = true;
        if (DEBUG_WINDOW_MOVEMENT) Slog.v(TAG, "Final remove of window: " + win);

        if (mInputMethodWindow == win) {
            mInputMethodWindow = null;
        } else if (win.mAttrs.type == TYPE_INPUT_METHOD_DIALOG) {
            mInputMethodDialogs.remove(win);
        }

        final WindowToken token = win.mToken;
        final AppWindowToken atoken = win.mAppToken;
        if (DEBUG_ADD_REMOVE) Slog.v(TAG, "Removing " + win + " from " + token);
        token.windows.remove(win);
        if (atoken != null) {
            atoken.allAppWindows.remove(win);
        }
        if (localLOGV) Slog.v(
                TAG, "**** Removing window " + win + ": count="
                + token.windows.size());
        if (token.windows.size() == 0) {
            if (!token.explicit) {
                mTokenMap.remove(token.token);
            } else if (atoken != null) {
                atoken.firstWindowDrawn = false;
            }
        }

        if (atoken != null) {
            if (atoken.startingWindow == win) {
                if (DEBUG_STARTING_WINDOW) Slog.v(TAG, "Notify removed startingWindow " + win);
                scheduleRemoveStartingWindowLocked(atoken);
            } else
            if (atoken.allAppWindows.size() == 0 && atoken.startingData != null) {
                // If this is the last window and we had requested a starting
                // transition window, well there is no point now.
                if (DEBUG_STARTING_WINDOW) Slog.v(TAG, "Nulling last startingWindow");
                atoken.startingData = null;
            } else if (atoken.allAppWindows.size() == 1 && atoken.startingView != null) {
                // If this is the last window except for a starting transition
                // window, we need to get rid of the starting transition.
                scheduleRemoveStartingWindowLocked(atoken);
            }
        }

        if (win.mAttrs.type == TYPE_WALLPAPER) {
            mLastWallpaperTimeoutTime = 0;
            getDefaultDisplayContentLocked().pendingLayoutChanges |=
                    WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER;
        } else if ((win.mAttrs.flags&FLAG_SHOW_WALLPAPER) != 0) {
            getDefaultDisplayContentLocked().pendingLayoutChanges |=
                    WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER;
        }

        final WindowList windows = win.getWindowList();
        if (windows != null) {
            windows.remove(win);
            if (!mInLayout) {
                assignLayersLocked(windows);
                final DisplayContent displayContent = win.getDisplayContent();
                if (displayContent != null) {
                    displayContent.layoutNeeded = true;
                }
                performLayoutAndPlaceSurfacesLocked();
                if (win.mAppToken != null) {
                    win.mAppToken.updateReportedVisibilityLocked();
                }
            }
        }
    //更新inputdispatcher的窗口
        mInputMonitor.updateInputWindowsLw(true /*force*/);
    }

小结下,就是说当inputdispatcher接收的message msg.header.type != InputMessage::TYPE_FINISHED时会触发销毁inputchannel的注册流程,且更新dispatcher的当前聚焦窗口。

然后对比anr错误日志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.
机翻下:
等待,因为聚焦窗口的输入通道没有注册在inputdispatcher。窗口可能在被移除的过程中。

这就奇怪了,说明是有聚焦的窗口但是没有注册,问题很可能就是销毁inputchannel的注册流程中,我们先看下这段日志的流程

//InputDispatcher.cpp
    // Check whether the window is ready for more input.
    reason = checkWindowReadyForMoreInputLocked(currentTime,
            mFocusedWindowHandle, entry, "focused");

//mFocusedWindowHandle 是当前聚焦窗口

String8 InputDispatcher::checkWindowReadyForMoreInputLocked(nsecs_t currentTime,
        const sp<InputWindowHandle>& windowHandle, const EventEntry* eventEntry,
        const char* targetType) {

        // If the window's connection is not registered then keep waiting.
    ssize_t connectionIndex = getConnectionIndexLocked(windowHandle->getInputChannel());
    if (connectionIndex < 0) {
        return String8::format("Waiting because the %s window's input channel is not "
                "registered with the input dispatcher.  The window may be in the process "
                "of being removed.", targetType);
    }
}


ssize_t InputDispatcher::getConnectionIndexLocked(const sp<InputChannel>& inputChannel) {
    ssize_t connectionIndex = mConnectionsByFd.indexOfKey(inputChannel->getFd());
    if (connectionIndex >= 0) {
        sp<Connection> connection = mConnectionsByFd.valueAt(connectionIndex);
        if (connection->inputChannel.get() == inputChannel.get()) {
            return connectionIndex;
        }
    }

    return -1;
}

//先看看mConnectionsByFd是什么

 // All registered connections mapped by channel file descriptor.
 //已channel fd为key 的所有注册connections
  KeyedVector<int, sp<Connection> > mConnectionsByFd;

status_t InputDispatcher::registerInputChannel(const sp<InputChannel>& inputChannel,
        const sp<InputWindowHandle>& inputWindowHandle, bool monitor) {
#if DEBUG_REGISTRATION
    ALOGD("channel '%s' ~ registerInputChannel - monitor=%s", inputChannel->getName().string(),
            toString(monitor));
#endif

    { // acquire lock
        AutoMutex _l(mLock);

        if (getConnectionIndexLocked(inputChannel) >= 0) {
            ALOGW("Attempted to register already registered input channel '%s'",
                    inputChannel->getName().string());
            return BAD_VALUE;
        }

        sp<Connection> connection = new Connection(inputChannel, inputWindowHandle, monitor);

    //每次服务端在inputdispatcher注册时,mConnectionsByFd的都会新一个connection
    //可以知道inputdispatcher已经保存了所有的channel,只需上层应用调用 
    //mInputMonitor.updateInputWindowsLw(true /*force*/);
    //通知inputdispatcher更新当前聚焦的窗口以便传递键值     
        int fd = inputChannel->getFd();
        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;
}


anr原因:等待,因为聚焦窗口的输入通道没有注册在inputdispatcher。窗口可能在被移除的过程中。

这样我们可以分析anr的真实原因
因为apk的所有新的窗口都必须调用InputDispatcher::registerInputChannel与inputdispatcher建立连接,当inputdispatcher因为异常调用unregisterInputChannelLocked后会销毁链接和移除fd,后续不会有重新注册且通知更新inputdispatcher的聚焦窗口,这样也出问题只能说明当前页面没有销毁但却取消了注册在inputdispatcher的inputchanel。

  • 4
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
ANR(Application Not Responding)指的是Android应用在主线程上执行耗时操作时出现的无响应现象,这可能导致应用无法响应用户输入,给用户带来不良体验。下面是几种常见的解决ANR问题的方法: 1. 将耗时操作放在子线程:将耗时的操作(如网络请求、数据库查询等)放在子线程执行,避免阻塞主线程。可以使用Thread、AsyncTask、Handler等方式创建子线程,确保主线程保持响应。 2. 使用异步操作:对于一些需要等待结果的操作,例如网络请求或数据库查询,使用异步方式执行,如使用AsyncTask、Handler、RxJava等框架来处理。 3. 使用多线程技术:合理使用多线程技术,将一些繁重的计算或IO操作放在后台线程执行,以减轻主线程的负担。 4. 避免在主线程进行耗时的IO操作:例如文件读写、数据库操作等,这些操作可以使用异步方式或将其放在子线程执行。 5. 使用合适的数据结构和算法:优化代码逻辑,使用高效的数据结构和算法,减少不必要的计算和迭代。 6. 避免频繁的UI更新:减少UI更新的次数,尽量批量更新UI,避免频繁调用UI更新方法。 7. 使用定时器避免阻塞主线程:通过使用定时器(Timer)或者Handler的postDelayed()方法,将一些可能引起阻塞的操作延后执行,避免在主线程长时间执行。 8. 使用线程池:使用线程池来管理线程,避免频繁创建和销毁线程的开销。 9. 检查和优化代码:定期检查代码,查找和消除潜在的性能问题,如内存泄漏、死锁等。 10. 使用性能分析工具:使用Android Studio提供的性能分析工具,如Profiler、Traceview等,来分析应用的性能瓶颈,并优化相应的代码。 综上所述,通过合理的线程管理、使用异步操作、优化代码逻辑等方法,可以有效解决Android应用ANR问题,提升应用的响应性能。
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值