Android Input(六)-ViewRootImpl接收事件

原创内容,转载请注明出处,多谢配合。

上一篇讲到,客户端的主线程的Looper会监控socket pair的客户端fd,一旦服务端(InputDispatcher)发送Input Event到socket pair的服务端 ,则客户端的Looper就会被唤醒,并调用NaitveInputEventReceiver的handleEvent()函数。

frameworks/base/core/jni/android_view_InputEventReceiver.cpp

int NativeInputEventReceiver::handleEvent(int receiveFd, int events, void* data) {
   ...
   if (events & ALOOPER_EVENT_INPUT) {
       JNIEnv* env = AndroidRuntime::getJNIEnv();
       status_t status = consumeEvents(env, false /*consumeBatches*/, -1, NULL);
       mMessageQueue->raiseAndClearException(env, "handleReceiveCallback");
       return status == OK || status == NO_MEMORY ? 1 : 0;
   }
   ...
}

执行consumeEvents

status_t NativeInputEventReceiver::consumeEvents(JNIEnv* env, bool consumeBatches, nsecs_t frameTime, bool* outConsumedBatch) {
   ...
   ScopedLocalRef<jobject> receiverObj(env, NULL);
   bool skipCallbacks = false;
   for (;;) {
       uint32_t seq;
       InputEvent* inputEvent;
       // 读取InputEvent
       status_t status = mInputConsumer.consume(&mInputEventFactory, consumeBatches, frameTime, &seq, &inputEvent);
    ...
           switch (inputEvent->getType()) {
           case AINPUT_EVENT_TYPE_KEY:
               // 根据读取到的InputEvent,构建java层的KeyEvent
               inputEventObj = android_view_KeyEvent_fromNative(env, static_cast<KeyEvent*>(inputEvent));
               break;
           case AINPUT_EVENT_TYPE_MOTION: {
              ...
           }
           if (inputEventObj) {
               // 调用java层的InputEventReceiver.dispachInputEvent()
               env->CallVoidMethod(receiverObj.get(), gInputEventReceiverClassInfo.dispatchInputEvent, seq, inputEventObj);
               env->DeleteLocalRef(inputEventObj);
           }
       ...
}

这里有两个部分内容:

一、读取input事件并构建java层的KeyEvent
frameworks/native/libs/input/InputTransport.cpp

status_t InputConsumer::consume(InputEventFactoryInterface* factory, bool consumeBatches, nsecs_t frameTime, uint32_t* outSeq, InputEvent** outEvent)
   *outSeq = 0;
   *outEvent = NULL;
   while (!*outEvent) {
    ...
           // Receive a fresh message.
           status_t result = mChannel->receiveMessage(&mMsg);}
          if (result) {
               ...
               return result;
           }
       }
       switch (mMsg.header.type) {
       case InputMessage::TYPE_KEY: {this
           KeyEvent* keyEvent = factory->createKeyEvent();
           initializeKeyEvent(keyEvent, &mMsg);
           *outSeq = mMsg.body.key.seq;
           *outEvent = keyEvent;
           break;
       }
  ...
}

通过InputChannel获取到InputEvent 转为Native的KeyEvent,最终再转化为java层的KeyEvent,此处不铺开分析了。

二、分发事件

2.1 QueuedInputEvent构建

frameworks/base/core/java/android/view/InputEventReceiver.java

public abstract class InputEventReceiver {
   private void dispatchInputEvent(int seq, InputEvent event) {
       mSeqMap.put(event.getSequenceNumber(), seq);
       onInputEvent(event);
   }

这里的InputEventReceiver是ViewRootImpl的内部类WindowInputEventReceiver:

frameworks/base/core/java/android/view/ViewRootImpl.java

   final class WindowInputEventReceiver extends InputEventReceiver {
       public WindowInputEventReceiver(InputChannel inputChannel, Looper looper) {
           super(inputChannel, looper);
       }
       public void onInputEvent(InputEvent event) {
           enqueueInputEvent(event, this, 0, true);
       }
   }

子类的onInputEvent中执行enqueueInputEvent

   void enqueueInputEvent(InputEvent event, InputEventReceiver receiver, int flags, boolean processImmediately) {
       adjustInputEventForCompatibility(event);
       // 构建一个QueueInputEvent,插入到pending队列尾部
       QueuedInputEvent q = obtainQueuedInputEvent(event, receiver, flags);
      ...
       // 处理pending队列里的event
       if (processImmediately) {
           doProcessInputEvents();//立刻执行
       } else {
           scheduleProcessInputEvents();//延迟执行
       }
   }
   void doProcessInputEvents() {
       // Deliver all pending input events in the queue.
       while (mPendingInputEventHead != null) {
           QueuedInputEvent q = mPendingInputEventHead;
           ...
           deliverInputEvent(q);//分发事件
       }
       ...
   }

2.2 InputStage体系

   private void deliverInputEvent(QueuedInputEvent q) {
       ...
       InputStage stage;
       //这里是stage实现类
       if (q.shouldSendToSynthesizer()) {
           stage = mSyntheticInputStage;
       } else {
           stage = q.shouldSkipIme() ? mFirstPostImeInputStage : mFirstInputStage;
       }
       if (stage != null) {
           stage.deliver(q);
       } else {
           finishInputEvent(q);//完成事件分发处理
       }
   }

那么看看子类初始化的地方:

frameworks/base/core/java/android/view/ViewRootImpl.java

public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) {
  …
  mSyntheticInputStage = new SyntheticInputStage();
InputStage viewPostImeStage = new ViewPostImeInputStage(mSyntheticInputStage);
InputStage nativePostImeStage = new NativePostImeInputStage(viewPostImeStage,
       "aq:native-post-ime:" + counterSuffix);
InputStage earlyPostImeStage = new EarlyPostImeInputStage(nativePostImeStage);
InputStage imeStage = new ImeInputStage(earlyPostImeStage,
       "aq:ime:" + counterSuffix);
InputStage viewPreImeStage = new ViewPreImeInputStage(imeStage);
InputStage nativePreImeStage = new NativePreImeInputStage(viewPreImeStage,
       "aq:native-pre-ime:" + counterSuffix);
mFirstInputStage = nativePreImeStage;
mFirstPostImeInputStage = earlyPostImeStage;
}

构造了7个InputStage实现类,

  • NativePreImeInputStage: 主要是为了将消息放到NativeActivity中去处理, NativeActivity和普通Acitivty的功能区别不大,只是很多代码都在native层去实现,这样执行效率更高,并且NativeActivity在游戏开发中很实用。

  • ViewPreImeInputStage: 从名字中就可得知,最后会调用Acitivity的所有view的onkeyPreIme方法,这样就给View在输入法处理key事件之前先得到消息并处理的机会。

  • ImeInputStage: ImeInputStage的onProcess方法会调用InputMethodManager的dispatchInputEvent方法处理消息。

  • EarlyPostImeInputStage: 屏幕上有焦点的View会高亮显示,用来提示用户焦点所在。

  • NativePostImeInputStage: 为了让IME处理完消息后能先于普通的Activity处理消息。

  • ViewPostImeInputStage: Acitivity和view处理各种消息。

  • SyntheticInputStage: 流水线的最后一级,经过层层过滤之后,到达这里的消息已经不多了,例如手机上的虚拟按键消息。

那么Activity和View的事件处理主要对应的InputStage是ViewPostImeInputStage。

实现关系如下图所示:

关系图

InputStage体系是很明显的责任链模式,自己能处理的就处理,不能处理的就next下去交给下一个处理。

abstract class InputStage {
    private final InputStage mNext;
   protected static final int FORWARD = 0;
   protected static final int FINISH_HANDLED = 1;
   protected static final int FINISH_NOT_HANDLED = 2;
   /**
    * Creates an input stage.
    * @param next The next stage to which events should be forwarded.
    */
   public InputStage(InputStage next) {
        mNext = next;
   }
    /**
    * Delivers an event to be processed.
    */

   public final void deliver(QueuedInputEvent q) {
        if ((q.mFlags & QueuedInputEvent.FLAG_FINISHED) != 0) {
            forward(q);//分发给下一个InputStage对象来处理
       } else if (shouldDropInputEvent(q)) { //是否需要丢弃
            finish(q, false);
       } else {
            apply(q, onProcess(q));
       }
    }

    /**
    * Marks the the input event as finished then forwards it to the next stage.
    */
   protected void finish(QueuedInputEvent q, boolean handled) {
        q.mFlags |= QueuedInputEvent.FLAG_FINISHED;
       if (handled) {
       //Finish方法会给这个消息加上FLAG_FINISHED标志,这样后面的InputStage对象也不会处理了,一直到流水线的结尾。
            q.mFlags |= QueuedInputEvent.FLAG_FINISHED_HANDLED;
       }
        forward(q);
   }
    /**
    * Forwards the event to the next stage.
    */
   protected void forward(QueuedInputEvent q) {
        onDeliverToNext(q);
   }
    /**
    * Applies a result code from {@link #onProcess} to the specified event.
    */
   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);
       }
    }
    /**
    * Called when an event is ready to be processed.
    * @return A result code indicating how the event was handled.
    */
   protected int onProcess(QueuedInputEvent q) {
        return FORWARD;
   }
    /**
    * Called when an event is being delivered to the next stage.
    */
   protected void onDeliverToNext(QueuedInputEvent q) {
        if (DEBUG_INPUT_STAGES) {
            Log.v(mTag, "Done with " + getClass().getSimpleName() + ". " + q);
       }
        if (mNext != null) {
            mNext.deliver(q);//调用下一级来进行处理
       } else {
            finishInputEvent(q);//如果是最后一级,直接finish
       }
    }
    protected boolean shouldDropInputEvent(QueuedInputEvent q) {
        if (mView == null || !mAdded) {
            Slog.w(mTag, "Dropping event due to root view being removed: " + q.mEvent);
           return true;
       } else if ((!mAttachInfo.mHasWindowFocus
                && !q.mEvent.isFromSource(InputDevice.SOURCE_CLASS_POINTER)) || mStopped
                || (mIsAmbientMode && !q.mEvent.isFromSource(InputDevice.SOURCE_CLASS_BUTTON))
                || (mPausedForTransition && !isBack(q.mEvent))) {
            // This is a focus event and the window doesn't currently have input focus or
           // has stopped. This could be an event that came back from the previous stage
           // but the window has lost focus or stopped in the meantime.
           if (isTerminalInputEvent(q.mEvent)) {
                // Don't drop terminal input events, however mark them as canceled.
               q.mEvent.cancel();
               Slog.w(mTag, "Cancelling event due to no window focus: " + q.mEvent);
               return false;
           }
            // Drop non-terminal input events.
           Slog.w(mTag, "Dropping event due to no window focus: " + q.mEvent);
           return true;
       }
        return false;
   }
    void dump(String prefix, PrintWriter writer) {
        if (mNext != null) {
            mNext.dump(prefix, writer);
       }
    }
    private boolean isBack(InputEvent event) {
        if (event instanceof KeyEvent) {
            return ((KeyEvent) event).getKeyCode() == KeyEvent.KEYCODE_BACK;
       } else {
            return false;
       }
    }
}

几个主要操作:

forword(q): 分发给下一个InputStage对象来处理。

shouldDropInputEvent(q): 是否需要丢弃。

finish(q, false): 加FLAG_FINISHED_HANDLED标签,后面的InputStage对象不会处理了。

onProcess(q):实现类重写此方法,真正事件的分发操作是从这开始的。

apply(q, onProcess(q)): 根据result做出对应处理。
result类型:

  • FORWARD: 本InputStage对象未处理,调用forward方法给下一个InputStage对象处理。
  • FINISH_HANDLED: 本InputStage对象已处理,直接finish。
  • FINISH_NOT_HANDLED: 消息虽然没有处理,但是要结束,调用finish方法结束。

最后放一张流程图:

ViewRootImpl接收消息流程

下一篇文章:
Android Input(七)-ViewRootImpl处理事件

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
禁止转载,如需转载请通过简信或评论联系作者。
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 217,084评论 6 503
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,623评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 163,450评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,322评论 1 293
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,370评论 6 390
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,274评论 1 300
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,126评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,980评论 0 275
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,414评论 1 313
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,599评论 3 334
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,773评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,470评论 5 344
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,080评论 3 327
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,713评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,852评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,865评论 2 370
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,689评论 2 354