android事件处理,对事件的接收处理,(6)

六,对事件的接收,从ViewRootImpl.java 说起。

       为什么要从ViewRootImpl说起呢?还记得前面说inputChannel通道的时候,是从添加一个窗口开始的,那么添加窗口就是从ViewRootImpl的setView开启的。

       ViewRootImpl.java

public void setView(View view,WindowManager.LayoutParams attrs, View panelParentView) {

       mInputChannel= new InputChannel();

       //这里是添加窗口的起点。

      res = mWindowSession.addToDisplay(mWindow, mSeq, mWindowAttributes,

                  getHostVisibility(),mDisplay.getDisplayId(),

                  mAttachInfo.mContentInsets,mAttachInfo.mStableInsets,

                  mAttachInfo.mOutsets,mInputChannel);

//这里是输入事件的接收端, mInputChannel是作为channel的客户端,这个前面分析过。同时注意第二个参数Looper.myLooper(),后面用调用Looper(cpp)的addFd方法,把mInputChannel对应的文件描述符fd添加到Looper的事件监控列表中。

       mInputEventReceiver= new WindowInputEventReceiver(mInputChannel,

              Looper.myLooper());

}    

需要说明的是setView的第一个参数mWindow,类型是W,即IWindow.Stub的实现类,表示IWindow的的实现端,这个参数作为WMS回调ViewRootImpl的桥梁,也即是WMS通过这个mWindow把事件、消息通知到ViewRootImpl,具体都有那些事件、消息可以参考IWindow.java类中的函数声明。具体是WMS通过IWindow的Bp端把相应事件通知到位于ViewRootImpl侧的IWindow的Bn端。


WindowInputEventReceiver会调用父类InputEventReceiver的构造函数,

InputEventReceiver.java

创建一个绑定到特定输入通道的输入事件的接受者。

public InputEventReceiver(InputChannelinputChannel, Looper looper) {

       mInputChannel = inputChannel;

       mMessageQueue = looper.getQueue();

       mReceiverPtr = nativeInit(new WeakReference<InputEventReceiver>(this),

                inputChannel, mMessageQueue);     

}

android_view_InputEventReceiver.cpp

static jlong nativeInit(JNIEnv* env, jclassclazz, jobject receiverWeak,

       jobject inputChannelObj, jobject messageQueueObj){

//先把java对象转成native对象。

          sp<InputChannel> inputChannel =android_view_InputChannel_getInputChannel(env,

           inputChannelObj);

       sp<MessageQueue>messageQueue = android_os_MessageQueue_getMessageQueue(env,

              messageQueueObj);  

//inputChannel ,messageQueue 赋给NativeInputEventReceiver中的变量,

          sp<NativeInputEventReceiver>receiver = new NativeInputEventReceiver(env,

           receiverWeak, inputChannel, messageQueue);

}

//当有event时,会调用 handleEvent方法。

intNativeInputEventReceiver::handleEvent(int receiveFd, int events, void* data){

       //consumeEvents消费这个事件。

       status_tstatus = consumeEvents(env, false /*consumeBatches*/, -1, NULL);

       setFdEvents(ALOOPER_EVENT_INPUT);

}

//通过 consumeEvents调用java中的方法 dispatchInputEvent。

status_t NativeInputEventReceiver::consumeEvents(JNIEnv*env,

       boolconsumeBatches, nsecs_t frameTime, bool* outConsumedBatch) {

               env->CallVoidMethod(receiverObj.get(),

                       gInputEventReceiverClassInfo.dispatchInputEvent, seq, inputEventObj); 

}

InputEventReceiver.java

//从native调用到java层。

private void dispatchInputEvent(int seq,InputEvent event) {

   mSeqMap.put(event.getSequenceNumber(), seq);

   onInputEvent(event);

}

进一步调用的是子类WindowInputEventReceiver的onInputEvent,

这样就回到了ViewRootImpl.java。

public void onInputEvent(InputEvent event) {

       enqueueInputEvent(event,this, 0, true);

}

void enqueueInputEvent(InputEvent event,InputEventReceiver receiver,

       intflags, boolean processImmediately)@ViewRootImpl.java{

       QueuedInputEventq = obtainQueuedInputEvent(event, receiver, flags);

//是立即处理事件,还是先入队顺序处理。

       if (processImmediately) {

           doProcessInputEvents();

       } else {

           scheduleProcessInputEvents();

       }

}

我们看入队顺序处理的情况。

privatevoid deliverInputEvent(QueuedInputEvent q) {

//接下来的事件传递,使用的是职责链模式,有多个InputStage的子类依次传递,

       InputStage stage = q.shouldSkipIme() ?mFirstPostImeInputStage : mFirstInputStage;

       stage.deliver(q);

}

这个职责链关系可以参考setView方法中的设置。

ViewRootImpl.java

publicvoid setView(View view, WindowManager.LayoutParams attrs, ViewpanelParentView){

//定义了事件的传递步骤。

       // Set up the input pipeline.

       InputStage syntheticInputStage = new SyntheticInputStage();

       InputStage viewPostImeStage = new ViewPostImeInputStage(syntheticInputStage);    InputStagenativePostImeStage = 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;

}

值得注意的是这里的ImeInputStage会把事件传给输入法,如果输入法进程被异常杀掉了,可能导致事件的传递中断。相关代码如下:

     final class ImeInputStage extends AsyncInputStage
            implements InputMethodManager.FinishedInputEventCallback {

        protected int onProcess(QueuedInputEvent q) {

            InputMethodManager imm = InputMethodManager.peekInstance();

            final InputEvent event = q.mEvent;

            int result = imm.dispatchInputEvent(event, q, this, mHandler);

             if (result == InputMethodManager.DISPATCH_NOT_HANDLED) {

//关键点在这里,如果输入法没有处理,记得返回FORWARD要传到下一个inputstage, 早期的android版本中,是返回的FINISH_NOT_HANDLED,就导致事件没能继续分发,被丢弃了。

                 return FORWARD;

             }

             return FORWARD;

        }

    }

也看下输入法这边的代码:

   public int dispatchInputEvent(InputEvent event, Object token,
            FinishedInputEventCallback callback, Handler handler) @InputMethodManager.java{

            if (mCurMethod != null) {

//这里还有一些判断条件省略了,如:是keyevent,是actionDown,是KEYCODE_SYM...

                  showInputMethodPickerLocked();

                  return DISPATCH_HANDLED;

           }

           return DISPATCH_NOT_HANDLED;

   }

这个 mCurMethod是IInputMethodSession类型的,如果输入法异常退出,在执行解绑定时,会把mCurMethod置null,那么上面的dispatchInputEvent就返回了DISPATCH_NOT_HANDLED,也就是ImeInputStage收到的返回值。

 

我们直接看最后按键事件是由那个InputStage处理的。

finalclass ViewPostImeInputStage extends InputStage {

       protected int onProcess(QueuedInputEventq) {

       if (q.mEvent instanceofKeyEvent) {

       returnprocessKeyEvent(q);     //按键事件

}

}

       private int processKeyEvent(QueuedInputEventq) {

              final KeyEvent event =(KeyEvent)q.mEvent;

       //这里把事件传给ViewTree,view树的根mView是DecorView,

              if (mView.dispatchKeyEvent(event)){

       returnFINISH_HANDLED;

}

}

}

早期的Android版本,DecorView是PhoneWindow.java的子类,

PhoneWindow.java

privatefinal class DecorView extends FrameLayout implements RootViewSurfaceTaker {

       public boolean dispatchKeyEvent(KeyEventevent) {

              final Callback cb = getCallback();

              boolean handled =cb.dispatchKeyEvent(event);

}

}    

Android7.1版本,DecorView.java是单独的文件,在事件处理上区别不大。

DecorView.java

publicboolean dispatchKeyEvent(KeyEvent event) {

       final Window.Callback cb =mWindow.getCallback();

       final boolean handled = cb != null&& mFeatureId < 0 ? cb.dispatchKeyEvent(event)

              : super.dispatchKeyEvent(event);

       return isDown ?mWindow.onKeyDown(mFeatureId, event.getKeyCode(), event)

              : mWindow.onKeyUp(mFeatureId,event.getKeyCode(), event);

}

这里mWindow是phoneWindow,它的getCallback()返回的Window.java中的接口Callback,既然是接口,就要具体的实现类,这个场景中实现类就是Activity,我们来确认Activity是不是实现了这个接口。

Activity.java

publicclass Activity extends ContextThemeWrapper

       implements LayoutInflater.Factory2, Window.Callback,KeyEvent.Callback,

Activity的继承关系,可以看到Activity实现了Window.Callback, KeyEvent.Callback两个接口。

其中dispatchKeyEvent,dispatchTouchEvent等拦截事件分发的函数是属于Window.Callback接口的;onKeyDown,onKeyLongPress,onKeyUp等最后一级的处理函数是属于KeyEvent.Callback接口的。

所以,接着就从DecorView到了Activity的dispatchKeyEvent函数中。

Activity.java

publicboolean dispatchKeyEvent(KeyEvent event) {

       View decor = mDecor;

       return event.dispatch(this, decor != null

              ? decor.getKeyDispatcherState() :null, this);

}

从这里可以看出,应用中Activity可以通过复写dispatchKeyEvent来实现拦截,如果返回true,就说明已经消费了这个事件,不再往下传递,否侧就传给下一个接收者。注意在return中调用了KeyEvent的dispatch方法。

KeyEvent.java

publicfinal boolean dispatch(Callback receiver, DispatcherState state, Objecttarget){

       switch (mAction) {

              case ACTION_DOWN: {

       booleanres = receiver.onKeyDown(mKeyCode, this);

       returnres;

}

case ACTION_UP:

       returnreceiver.onKeyUp(mKeyCode, this);

}

}

这里通过receiver传给了具体的接收者,可能是应用中的Activity实例,或者更确切的说是实现了KeyEvent.Callback接口的对象。其中的onKeyDown,onKeyUp跟应用中的Activity重写的方法是一致的。

       对按键事件的处理相对简单,除了窗口策略phoneWindowManager先行做拦截处理外,就是当前应用本身的处理了。应用本身只要复写一些跟key相关的方法即接受到事件,比如:

public boolean onKeyDown(intkeyCode, KeyEvent event) {

    throw new RuntimeException("Stub!");

}

public boolean onKeyLongPress(intkeyCode, KeyEvent event) {

    throw new RuntimeException("Stub!");

}

public boolean onKeyUp(intkeyCode, KeyEvent event) {

    throw new RuntimeException("Stub!");

}

窗口策略这边的拦截就是通过如下函数:

PhoneWindowManager.java

当应用程序没有处理,就把事件分发到dispatchUnhandledKey,这是由InputDispatch线程调用的。允许你针对这些没有被应用处理的keys,定义默认的行为,比如丢弃,也可以用一个替代的keycode再次分发作为反馈。

public KeyEventdispatchUnhandledKey(WindowState win, KeyEvent event, int policyFlags);

这个方法是在一个key分发给目标窗口强,被inputDispatch线程调用,你可以针对一些keys定义一个处理行为,这个处理方法是不会被应用覆盖的。

public longinterceptKeyBeforeDispatching(WindowState win, KeyEvent event, intpolicyFlags);

这个方法是在key入队前,被input reader线程调用,有些行为需要在这个时刻处理因为它可能影响到设备的电源状态,比如powerkeys,所以,通常情况下在队列中应尽可能少的保存这个key。

public intinterceptKeyBeforeQueueing(KeyEvent event, int policyFlags);

 

 

到这里就分析完了按键的接收过程。

 

下面看下触屏事件的接收处理。

前面的处理过程跟按键是类似的,我们还是从InputStage职责链的最后开始:

finalclass ViewPostImeInputStage extends InputStage {

       protected int onProcess(QueuedInputEventq) {

              if (q.mEvent instanceof KeyEvent){

              //按键事件处理。

                     return processKeyEvent(q);

}else{

       finalint source = q.mEvent.getSource();

       if((source & InputDevice.SOURCE_CLASS_POINTER) != 0) {

//pointer事件,触屏事件是从这里开始处理的

                     returnprocessPointerEvent(q);

}

}

}

}

ViewRootImpl.java

privateint processPointerEvent(QueuedInputEvent q) {

       final View eventTarget =

              (event.isFromSource(InputDevice.SOURCE_MOUSE)&& mCapturingView != null) ?

              mCapturingView : mView;

//把处理权交给Viewtree的根元素mView,

       boolean handled =eventTarget.dispatchPointerEvent(event);

}

View.java

publicfinal boolean dispatchPointerEvent(MotionEvent event) {

       if (event.isTouchEvent()) {

              returndispatchTouchEvent(event);//触屏事件

} else {

       returndispatchGenericMotionEvent(event);// GenericMotionEvent事件

}

}

从这里开始,应该把事件传给目标View,这个目标view可能就是View.java,在应用场景下,更多的时候是DecorView,所以进入到DecorView.java中

DecorView.java

publicboolean dispatchTouchEvent(MotionEvent ev) {

       final Window.Callback cb =mWindow.getCallback();

       return cb != null &&!mWindow.isDestroyed() && mFeatureId < 0

              ? cb.dispatchTouchEvent(ev) :super.dispatchTouchEvent(ev);

}

根据前面按键事件的分析,这里的mWindow.getCallback();的具体实例是Activity实例。

Activity.java

//子类可以重写这个方法,在分发给窗口前拦截所有的触屏事件。

publicboolean dispatchTouchEvent(MotionEvent ev) {

//把事件分发给窗口。

       if(getWindow().superDispatchTouchEvent(ev)) {

              return true;

       }

}

从窗口还是回到ViewTree,Viewtree中处理分ViewGroup,View,

PhoneWindow.java

publicboolean superDispatchTouchEvent(MotionEvent event) {

    returnmDecor.superDispatchTouchEvent(event);

}

先看ViewGroup中的处理,DecorView是继承ViewGroup的,

DecorView.java

publicboolean superDispatchTouchEvent(MotionEvent event) {

   return super.dispatchTouchEvent(event);

}

接着调用的ViewGroup中的方法dispatchTouchEvent。

ViewGroup.java

publicboolean dispatchTouchEvent(MotionEvent ev) {

       boolean handled = false;//判断事件是否被处理

//判断是否是Down事件,down是后续事件的起点,收到Down事件,先清除以前的touch目标,重置所有的状态,准备进入一个新的循环。

       if (actionMasked ==MotionEvent.ACTION_DOWN) {

              cancelAndClearTouchTargets(ev);
              resetTouchState();

       }

 

//是否拦截这个事件,ViewGroup优先考虑拦截的可能行。

       final boolean intercepted;

       if (actionMasked == MotionEvent.ACTION_DOWN

              || mFirstTouchTarget != null) {

       final boolean disallowIntercept =(mGroupFlags & FLAG_DISALLOW_INTERCEPT) != 0;

//disallowIntercept为false,就是允许拦截,进一步调用 onInterceptTouchEvent来确定是否真正执行拦截,ViewGroup的子类只要重载这个 onInterceptTouchEvent这个函数就可以改变ViewGroup的拦截策略。

              if (!disallowIntercept) {

                     intercepted =onInterceptTouchEvent(ev);

              }else{

                     intercepted = false;

              }

       }else{

              intercepted = true;      //不允许拦截

       }

 

//不拦截的情况

       if (!canceled && !intercepted) {

//从前往后查找一个可以接收这个事件的子View。

              for (int i = childrenCount - 1; i>= 0; i--) {

                     final View child =getAndVerifyPreorderedView(

                            preorderedList,children, childIndex);

//判断标准:canViewReceivePointerEvents,这个child是否能接收这个事件,比如其是否可见; isTransformedTouchPointInView,触摸点是否落在了这个child的范围内。

                     if (!canViewReceivePointerEvents(child)

                            ||!isTransformedTouchPointInView(x, y, child, null)) {

                            ev.setTargetAccessibilityFocus(false);

                            continue;

                     }

//找到了能接收的子View,就把事件投递给他,如果子View是ViewGroup就会继续调用ViewGroup的dispatchTouchEvent,如果是View,将调用View的dispatchTouchEvent。

                     if(dispatchTransformedTouchEvent(ev, false, child, idBitsToAssign)) {}

              }

       }

}

从上看出,ViewGroup对事件的处理,可能自身处理这个事件,也可能继续传递事件给子View。

什么情况下ViewGroup会拦截处理这个事件,起决定作用的是onInterceptTouchEvent,也就是在继承ViewGroup时,只要重载onInterceptTouchEvent,就可以实现拦截,当然也可以重载dispatchTouchEvent,但是dispatchTouchEvent是所有ViewGroup共性的一个表现,还是重载onInterceptTouchEvent这个体现特性的方法好些。

如果onInterceptTouchEvent返回false,那就是不拦截,继续分发给子View,后面的move,up等事件还是会调用onInterceptTouchEvent做判断。

如果onInterceptTouchEvent返回true,当前viewGroup拦截这个事件,那么从这个down事件开启,直到up事件都会投递到这个ViewGroup的onTouchEvent方法中,不再往子View传递,这也意味着你要重载onTouchEvent方法,它是属于View类的。

       最后看下View对事件的处理。

View.java

publicboolean dispatchTouchEvent(MotionEvent event) {

       if (onFilterTouchEventForSecurity(event)){

//View可以通过setOnTouchListener设置一个事件监听,mOnTouchListener就是那个监听对象,当事件来时,就不调用mOnTouchListener来通知,这种方式的优先级比onTouchEvent要高。

              ListenerInfo li = mListenerInfo;

              if (li != null &&li.mOnTouchListener != null

                     && (mViewFlags& ENABLED_MASK) == ENABLED

                     &&li.mOnTouchListener.onTouch(this, event)) {

       result= true;

}

//如果没有指定OnTouchListener监听,或者View被disabled,或者onTouch返回了false,都会把事件传给onTouchEvent,onTouchEvent也是需要继承类重载的。

if (!result &&onTouchEvent(event)) {

       result= true;

}

}

}

//具体处理这个touch事件

publicboolean onTouchEvent(MotionEvent event) {

       case MotionEvent.ACTION_DOWN:

//将View对象设置为pressed状态,检查是否会形成一个长按事件,默认一个长按事件的时间是0.5秒,DEFAULT_LONG_PRESS_TIMEOUT = 500;这是通过postDelayed一个Runable来确定的,在这个事件之前如果有up,move时间产生,就会移除这个Runable,否则就需要进行长按事件的处理。

              setPressed(true,x, y);

              checkForLongClick(0,x, y);

 

// pointInView判断手势是否超出了view的范围。如果超出了,移除长按检测,View的状态也不再是pressed,

       case MotionEvent.ACTION_MOVE:

              if (!pointInView(x, y,mTouchSlop)) {

                     removeLongPressCallback();

                     setPressed(false);

}

 

//UP是手势的结束点,这里重点判断是否产生onClick,即调用setOnClickListener设置的监听,要产生一个click事件,一是当前要是Pressed状态,二前面没有执行长按事件。

       case MotionEvent.ACTION_UP:

              if ((mPrivateFlags &PFLAG_PRESSED) != 0 || prepressed) {

                     if (!mHasPerformedLongPress&& !mIgnoreNextUpEvent) {

                            performClick();

}

}

 

 

//cancel,这个不是用户主动产生的,是系统自行做出的处理,作为手势的结束,完成一些清理工作。

case MotionEvent.ACTION_CANCEL:

}

到这里事件的处理就分析完了。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Java的事件处理机制是通过事件分发和事件监听器来实现的。事件分发是指将事件传递给相应的处理对象,而事件监听器则是用于接收处理事件的对象。 在Java中,事件分发的对象通常是指用户界面组件,例如按钮、文本框等。当用户与界面组件进行交互时,会产生相应的事件,例如点击事件、鼠标移动事件等。这些事件的相关细节会被封装成事件对象。 事件分发的本质是将事件传递到某个具体的处理对象,这个过程通常是从顶层容器开始,逐级向下传递,直到找到能够处理事件的对象为止。在Android中,事件的传递顺序通常是从Activity开始,然后传递给ViewGroup,最后传递给具体的View。 事件分发过程涉及到几个核心方法,包括dispatchTouchEvent、onInterceptTouchEvent和onTouchEvent。其中,dispatchTouchEvent方法用于分发事件,onInterceptTouchEvent方法用于拦截事件,onTouchEvent方法用于处理事件。 在Java中,可以通过设置事件监听器来监听和处理事件。常见的事件监听器包括OnClickListener、OnTouchListener等。通过设置相应的监听器,可以在事件发生时执行相应的操作,例如点击按钮时执行某个方法。 总结起来,Java的事件处理机制通过事件分发和事件监听器来实现。事件分发将事件传递给相应的处理对象,而事件监听器则用于接收处理事件。这种机制可以实现用户界面的交互和响应。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值