View事件机制源码分析
目录介绍
- 01.Android中事件分发顺序
- 02.Activity的事件分发机制
- 2.1 源码分析
- 2.2 点击事件调用顺序
- 2.3 得出结论
- 03.ViewGroup事件的分发机制
- 3.1 看一下这个案例
- 3.2 源码分析
- 3.3 得出结论
- 04.View事件的分发机制
- 4.1 源码分析
- 4.2 得出结论
- 4.3 验证结论
- 05.思考一下
- 5.1 onTouch()和onTouchEvent()的区别
- 5.2 Touch事件的后续事件传递
好消息
- 博客笔记大汇总【16年3月到至今】,包括Java基础及深入知识点,Android技术博客,Python学习笔记等等,还包括平时开发中遇到的bug汇总,当然也在工作之余收集了大量的面试题,长期更新维护并且修正,持续完善……开源的文件是markdown格式的!同时也开源了生活博客,从12年起,积累共计N篇[近100万字,陆续搬到网上],转载请注明出处,谢谢!
- 链接地址:https://github.com/yangchong211/YCBlogs
- 如果觉得好,可以star一下,谢谢!当然也欢迎提出建议,万事起于忽微,量变引起质变!
View事件系列博客
- 01.View事件基础
- 事件分发的对象是谁?事件在哪些对象间进行传递?事件分发过程涉及方法?事件分发机制方法说明?
- 02.View事件分发场景
- 事件分发背景描述,事件传递情况
- 03.View事件机制源码分析
- Android中事件分发顺序?Activity的事件分发机制?ViewGroup事件的分发机制 ?View事件的分发机制?onTouch()和onTouchEvent()的区别?
- 04.View事件机制
- 触摸事件,分发事件,拦截事件,三个事件机制怎么向其调用者传递处理结果,滑动冲突的思路及方法 ,以及具体的滑动冲突解决方案案例
- 05.View的滑动冲突
- 通过一个滑动冲突的案例,来讲解外部拦截法解决滑动冲突,内部拦截法解决滑动冲突
- 07.View事件总结1
- Android事件分发机制,View和ViewGroup分发事件,onTouch()、onTouchEvent()和onClick()执行顺序,View处理事件的优先级,点击事件传递过程,事件传递规则要点
- 08.View事件总结2
- View滑动有哪些方法,Activity事件分发过程,Window事件分发过程,DecorView的事件分发,根View的事件分发
01.Android中事件分发顺序
- Android中事件分发顺序:
- Activity(Window) -> ViewGroup -> View
- 其中:
- super:调用父类方法
- true:消费事件,即事件不继续往下传递
- false:不消费事件,事件继续往下传递 / 交由给父控件onTouchEvent()处理
- 充分理解Android分发机制,本质上是要理解:
- Activity对点击事件的分发机制
- ViewGroup对点击事件的分发机制
- View对点击事件的分发机制
02.Activity的事件分发机制
2.1 源码分析
- 当一个点击事件发生时,事件最先传到Activity的dispatchTouchEvent()进行事件分发
- 具体是由Activity的Window来完成
- 我们来看下Activity的dispatchTouchEvent()的源码
public boolean dispatchTouchEvent(MotionEvent ev) {
//第一步
//一般事件列开始都是DOWN,所以这里基本是true
if (ev.getAction() == MotionEvent.ACTION_DOWN) {
//第二步
onUserInteraction();
}
//第三步
if (getWindow().superDispatchTouchEvent(ev)) {
return true;
}
return onTouchEvent(ev);
}
- 第一步
- 一般事件列开始都是DOWN(按下按钮),所以这里返回true,执行onUserInteraction()
- 第二步
- 先来看下onUserInteraction()源码
public void onUserInteraction() {
}
- 从源码可以看出:
- 该方法为空方法
- 从注释得知:当此activity在栈顶时,触屏点击按home,back,menu键等都会触发此方法
- 所以onUserInteraction()主要用于屏保
- 第三步
- Window类是抽象类,且PhoneWindow是Window类的唯一实现类
- superDispatchTouchEvent(ev)是抽象方法
- 通过PhoneWindow类中看一下superDispatchTouchEvent()的作用
@Override
public boolean superDispatchTouchEvent(MotionEvent event) {
return mDecor.superDispatchTouchEvent(event);
//mDecor是DecorView的实例
//DecorView是视图的顶层view,继承自FrameLayout,是所有界面的父类
}
- 接下来我们看mDecor.superDispatchTouchEvent(event):
public boolean superDispatchTouchEvent(MotionEvent event) {
return super.dispatchTouchEvent(event);
//DecorView继承自FrameLayout
//那么它的父类就是ViewGroup
而super.dispatchTouchEvent(event)方法,其实就应该是ViewGroup的dispatchTouchEvent() }
- 得出结果
- 执行getWindow().superDispatchTouchEvent(ev)实际上是执行了ViewGroup.dispatchTouchEvent(event)
- 这样事件就从 Activity 传递到了 ViewGroup
2.2 点击事件调用顺序
- 当一个点击事件发生时,调用顺序如下
- 1.事件最先传到Activity的dispatchTouchEvent()进行事件分发
- 2.调用Window类实现类PhoneWindow的superDispatchTouchEvent()
- 3.调用DecorView的superDispatchTouchEvent()
- 4.最终调用DecorView父类的dispatchTouchEvent(),即ViewGroup的dispatchTouchEvent()
2.3 得出结论
- 当一个点击事件发生时,事件最先传到Activity的dispatchTouchEvent()进行事件分发,最终是调用了ViewGroup的dispatchTouchEvent()方法
- 这样事件就从 Activity 传递到了 ViewGroup
03.ViewGroup事件的分发机制
3.1 看一下这个案例
- 布局如下:
- 结果测试
- 只点击Button,发现执行顺序:btn1,btn2
- 再点击空白处,发现执行顺序:btn1,btn2,viewGroup
- 从上面的测试结果发现:
- 当点击Button时,执行Button的onClick(),但ViewGroupLayout注册的onTouch()不会执行
- 只有点击空白区域时才会执行ViewGroupLayout的onTouch();
- 结论:Button的onClick()将事件消费掉了,因此事件不会再继续向下传递。
3.2 源码分析
- ViewGroup的dispatchTouchEvent()源码分析,该方法比较复杂,截取几个重要的逻辑片段进行介绍,来解析整个分发流程。
// 发生ACTION_DOWN事件或者已经发生过ACTION_DOWN,并且将mFirstTouchTarget赋值,才进入此区域,主要功能是拦截器
final boolean intercepted;
if (actionMasked == MotionEvent.ACTION_DOWN|| mFirstTouchTarget != null) {
//disallowIntercept:是否禁用事件拦截的功能(默认是false),即不禁用
//可以在子View通过调用requestDisallowInterceptTouchEvent方法对这个值进行修改,不让该View拦截事件
final boolean disallowIntercept = (mGroupFlags & FLAG_DISALLOW_INTERCEPT) != 0;
//默认情况下会进入该方法
if (!disallowIntercept) {
//调用拦截方法
intercepted = onInterceptTouchEvent(ev);
ev.setAction(action);
} else {
intercepted = false;
}
} else {
// 当没有触摸targets,且不是down事件时,开始持续拦截触摸。
intercepted = true;
}
- 这一段的内容主要是为判断是否拦截。如果当前事件的MotionEvent.ACTION_DOWN,则进入判断,调用ViewGroup onInterceptTouchEvent()方法的值,判断是否拦截。如果mFirstTouchTarget != null,即已经发生过MotionEvent.ACTION_DOWN,并且该事件已经有ViewGroup的子View进行处理了,那么也进入判断,调用ViewGroup onInterceptTouchEvent()方法的值,判断是否拦截。如果不是以上两种情况,即已经是MOVE或UP事件了,并且之前的事件没有对象进行处理,则设置成true,开始拦截接下来的所有事件。这也就解释了如果子View的onTouchEvent()方法返回false,那么接下来的一些列事件都不会交给他处理。如果VieGroup的onInterceptTouchEvent()第一次执行为true,则mFirstTouchTarget = null,则也会使得接下来不会调用onInterceptTouchEvent(),直接将拦截设置为true。
- 当ViewGroup不拦截事件的时候,事件会向下分发交由它的子View或ViewGroup进行处理。
/* 从最底层的父视图开始遍历,
** 找寻newTouchTarget,即上面的mFirstTouchTarget
** 如果已经存在找寻newTouchTarget,说明正在接收触摸事件,则跳出循环。
*/
for (int i = childrenCount - 1; i >= 0; i--) {
final int childIndex = customOrder
? getChildDrawingOrder(childrenCount, i) : i;
final View child = (preorderedList == null)
? children[childIndex] : preorderedList.get(childIndex); // 如果当前视图无法获取用户焦点,则跳过本次循环
if (childWithAccessibilityFocus != null) {
if (childWithAccessibilityFocus != child) {
continue;
}
childWithAccessibilityFocus = null;
i = childrenCount - 1;
}
//如果view不可见,或者触摸的坐标点不在view的范围内,则跳过本次循环
if (!canViewReceivePointerEvents(child)
|| !isTransformedTouchPointInView(x, y, child, null)) {
ev.setTargetAccessibilityFocus(false);
continue;
} newTouchTarget = getTouchTarget(child);
// 已经开始接收触摸事件,并退出整个循环。
if (newTouchTarget != null) {
newTouchTarget.pointerIdBits |= idBitsToAssign;
break;
} //重置取消或抬起标志位
//如果触摸位置在child的区域内,则把事件分发给子View或ViewGroup
if (dispatchTransformedTouchEvent(ev, false, child, idBitsToAssign)) {
// 获取TouchDown的时间点
mLastTouchDownTime = ev.getDownTime();
// 获取TouchDown的Index
if (preorderedList != null) {
for (int j = 0; j < childrenCount; j++) {
if (children[childIndex] == mChildren[j]) {
mLastTouchDownIndex = j;
break;
}
}
} else {
mLastTouchDownIndex = childIndex;
} //获取TouchDown的x,y坐标
mLastTouchDownX = ev.getX();
mLastTouchDownY = ev.getY();
//添加TouchTarget,则mFirstTouchTarget != null。
newTouchTarget = addTouchTarget(child, idBitsToAssign);
//表示以及分发给NewTouchTarget
alreadyDispatchedToNewTouchTarget = true;
break;
}
dispatchTransformedTouchEvent()
方法实际就是调用子元素的dispatchTouchEvent()
方法。- 其中
dispatchTransformedTouchEvent()
方法的重要逻辑如下:
if (child == null) {
handled = super.dispatchTouchEvent(event);
} else {
handled = child.dispatchTouchEvent(event);
}
- 由于其中传递的child不为空,所以就会调用子元素的dispatchTouchEvent()。如果子元素的dispatchTouchEvent()方法返回true,那么mFirstTouchTarget就会被赋值,同时跳出for循环。
//添加TouchTarget,则mFirstTouchTarget != null。
newTouchTarget = addTouchTarget(child, idBitsToAssign);
//表示以及分发给NewTouchTarget
alreadyDispatchedToNewTouchTarget = true;
- 其中在
addTouchTarget(child, idBitsToAssign);
内部完成mFirstTouchTarget被赋值。如果mFirstTouchTarget为空,将会让ViewGroup默认拦截所有操作。如果遍历所有子View或ViewGroup,都没有消费事件。ViewGroup会自己处理事件。
3.3 得出结论
- Android事件分发是先传递到ViewGroup,再由ViewGroup传递到View
- 在ViewGroup中通过onInterceptTouchEvent()对事件传递进行拦截
- 1.onInterceptTouchEvent方法返回true代表拦截事件,即不允许事件继续向子View传递;
- 2.返回false代表不拦截事件,即允许事件继续向子View传递;(默认返回false)
- 3.子View中如果将传递的事件消费掉,ViewGroup中将无法接收到任何事件。
04.View事件的分发机制
4.1 源码分析
- View中dispatchTouchEvent()的源码分析
public boolean dispatchTouchEvent(MotionEvent event) {
if (mOnTouchListener != null && (mViewFlags & ENABLED_MASK) == ENABLED &&
mOnTouchListener.onTouch(this, event)) {
return true;
}
return onTouchEvent(event);
}
- 从上面可以看出:
- 只有以下三个条件都为真,dispatchTouchEvent()才返回true;否则执行onTouchEvent(event)方法
第一个条件:mOnTouchListener != null;
第二个条件:(mViewFlags & ENABLED_MASK) == ENABLED;
第三个条件:mOnTouchListener.onTouch(this, event);
- 下面,我们来看看下这三个判断条件:
- 第一个条件:mOnTouchListener!= null
//mOnTouchListener是在View类下setOnTouchListener方法里赋值的
public void setOnTouchListener(OnTouchListener l) { //即只要我们给控件注册了Touch事件,mOnTouchListener就一定被赋值(不为空)
mOnTouchListener = l;
}
- 第二个条件:(mViewFlags & ENABLED_MASK) == ENABLED
- 该条件是判断当前点击的控件是否enable
- 由于很多View默认是enable的,因此该条件恒定为true
- 第三个条件:mOnTouchListener.onTouch(this, event)
- 回调控件注册Touch事件时的onTouch方法
//手动调用设置
button.setOnTouchListener(new OnTouchListener() {
@Override
public boolean onTouch(View v, MotionEvent event) {
return false;
}
});
- 如果在onTouch方法返回true,就会让上述三个条件全部成立,从而整个方法直接返回true。
- 如果在onTouch方法里返回false,就会去执行onTouchEvent(event)方法。
- 接下来,我们继续看:**onTouchEvent(event)**的源码分析
public boolean onTouchEvent(MotionEvent event) {
final int viewFlags = mViewFlags;
if ((viewFlags & ENABLED_MASK) == DISABLED) {
// A disabled view that is clickable still consumes the touch
// events, it just doesn't respond to them.
return (((viewFlags & CLICKABLE) == CLICKABLE ||
(viewFlags & LONG_CLICKABLE) == LONG_CLICKABLE));
}
if (mTouchDelegate != null) {
if (mTouchDelegate.onTouchEvent(event)) {
return true;
}
}
//如果该控件是可以点击的就会进入到下两行的switch判断中去; if (((viewFlags & CLICKABLE) == CLICKABLE ||
(viewFlags & LONG_CLICKABLE) == LONG_CLICKABLE)) {
//如果当前的事件是抬起手指,则会进入到MotionEvent.ACTION_UP这个case当中。 switch (event.getAction()) {
case MotionEvent.ACTION_UP:
boolean prepressed = (mPrivateFlags & PREPRESSED) != 0;
// 在经过种种判断之后,会执行到关注点1的performClick()方法。
//请往下看关注点1
if ((mPrivateFlags & PRESSED) != 0 || prepressed) {
// take focus if we don't have it already and we should in
// touch mode.
boolean focusTaken = false;
if (isFocusable() && isFocusableInTouchMode() && !isFocused()) {
focusTaken = requestFocus();
}
if (!mHasPerformedLongPress) {
// This is a tap, so remove the longpress check
removeLongPressCallback();
// Only perform take click actions if we were in the pressed state
if (!focusTaken) {
// Use a Runnable and post this rather than calling
// performClick directly. This lets other visual state
// of the view update before click actions start.
if (mPerformClick == null) {
mPerformClick = new PerformClick();
}
if (!post(mPerformClick)) {
//关注点1
//请往下看performClick()的源码分析
performClick();
}
}
}
if (mUnsetPressedState == null) {
mUnsetPressedState = new UnsetPressedState();
}
if (prepressed) {
mPrivateFlags |= PRESSED;
refreshDrawableState();
postDelayed(mUnsetPressedState,
ViewConfiguration.getPressedStateDuration());
} else if (!post(mUnsetPressedState)) {
// If the post failed, unpress right now
mUnsetPressedState.run();
}
removeTapCallback();
}
break;
case MotionEvent.ACTION_DOWN:
if (mPendingCheckForTap == null) {
mPendingCheckForTap = new CheckForTap();
}
mPrivateFlags |= PREPRESSED;
mHasPerformedLongPress = false;
postDelayed(mPendingCheckForTap, ViewConfiguration.getTapTimeout());
break;
case MotionEvent.ACTION_CANCEL:
mPrivateFlags &= ~PRESSED;
refreshDrawableState();
removeTapCallback();
break;
case MotionEvent.ACTION_MOVE:
final int x = (int) event.getX();
final int y = (int) event.getY();
// Be lenient about moving outside of buttons
int slop = mTouchSlop;
if ((x < 0 - slop) || (x >= getWidth() + slop) ||
(y < 0 - slop) || (y >= getHeight() + slop)) {
// Outside button
removeTapCallback();
if ((mPrivateFlags & PRESSED) != 0) {
// Remove any future long press/tap checks
removeLongPressCallback();
// Need to switch from pressed to not pressed
mPrivateFlags &= ~PRESSED;
refreshDrawableState();
}
}
break;
}
//如果该控件是可以点击的,就一定会返回true
return true;
}
//如果该控件是不可以点击的,就一定会返回false
return false;
}
- 关注点1:
- performClick()的源码分析
public boolean performClick() {
sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_CLICKED); if (mOnClickListener != null) {
playSoundEffect(SoundEffectConstants.CLICK);
mOnClickListener.onClick(this);
return true;
}
return false;
}
- 只要mOnClickListener不为null,就会去调用onClick方法;
- 那么,mOnClickListener又是在哪里赋值的呢?请继续看:
public void setOnClickListener(OnClickListener l) {
if (!isClickable()) {
setClickable(true);
}
mOnClickListener = l;
}
- 当我们通过调用setOnClickListener方法来给控件注册一个点击事件时,就会给mOnClickListener赋值(不为空),即会回调onClick()。
4.2 得出结论
- 1.onTouch()的执行高于onClick()
- 2.每当控件被点击时:
- 如果在回调onTouch()里返回false,就会让dispatchTouchEvent方法返回false,那么就会执行onTouchEvent();如果回调了setOnClickListener()来给控件注册点击事件的话,最后会在performClick()方法里回调onClick()。
- onTouch()返回false(该事件没被onTouch()消费掉) = 执行onTouchEvent() = 执行OnClick()
- 如果在回调onTouch()里返回true,就会让dispatchTouchEvent方法返回true,那么将不会执行onTouchEvent(),即onClick()也不会执行;
- onTouch()返回true(该事件被onTouch()消费掉) = dispatchTouchEvent()返回true(不会再继续向下传递) = 不会执行onTouchEvent() = 不会执行OnClick()
- 如果在回调onTouch()里返回false,就会让dispatchTouchEvent方法返回false,那么就会执行onTouchEvent();如果回调了setOnClickListener()来给控件注册点击事件的话,最后会在performClick()方法里回调onClick()。
4.3 验证结论
- 在回调onTouch()里返回true
TextView textView = findViewById(R.id.tv_13);
//设置OnTouchListener()
textView.setOnTouchListener(new View.OnTouchListener() { @Override
public boolean onTouch(View v, MotionEvent event) {
Log.d("小杨逗比","执行了onTouch(), 动作是:" + event.getAction());
return true;
}
});
//设置OnClickListener
textView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Log.d("小杨逗比","执行了onClick()");
}
});
- 打印日志如下所示
- 注意action为0是ACTION_DOWN,为2是ACTION_MOVE,为1是ACTION_UP。
2019-04-04 13:37:58.301 13616-13616/org.yczbj.ycrefreshview D/小杨逗比: 执行了onTouch(), 动作是:0
2019-04-04 13:37:58.315 13616-13616/org.yczbj.ycrefreshview D/小杨逗比: 执行了onTouch(), 动作是:2
2019-04-04 13:37:58.405 13616-13616/org.yczbj.ycrefreshview D/小杨逗比: 执行了onTouch(), 动作是:2
2019-04-04 13:37:58.408 13616-13616/org.yczbj.ycrefreshview D/小杨逗比: 执行了onTouch(), 动作是:1
- 在回调onTouch()里返回false
- 打印结果如下所示
2019-04-04 13:41:26.961 14006-14006/org.yczbj.ycrefreshview D/小杨逗比: 执行了onTouch(), 动作是:0
2019-04-04 13:41:26.978 14006-14006/org.yczbj.ycrefreshview D/小杨逗比: 执行了onTouch(), 动作是:2
2019-04-04 13:41:27.072 14006-14006/org.yczbj.ycrefreshview D/小杨逗比: 执行了onTouch(), 动作是:2
2019-04-04 13:41:27.074 14006-14006/org.yczbj.ycrefreshview D/小杨逗比: 执行了onTouch(), 动作是:1
2019-04-04 13:41:27.076 14006-14006/org.yczbj.ycrefreshview D/小杨逗比: 执行了onClick()
- 总结:onTouch()返回true就认为该事件被onTouch()消费掉,因而不会再继续向下传递,即不会执行OnClick()。
05.思考一下
5.1 onTouch()和onTouchEvent()的区别
- 这两个方法都是在View的dispatchTouchEvent中调用,但onTouch优先于onTouchEvent执行。
- 如果在onTouch方法中返回true将事件消费掉,onTouchEvent()将不会再执行。
- 特别注意:请看下面代码
//&&为短路与,即如果前面条件为false,将不再往下执行
//所以,onTouch能够得到执行需要两个前提条件:
//1. mOnTouchListener的值不能为空
//2. 当前点击的控件必须是enable的。
mOnTouchListener != null && (mViewFlags & ENABLED_MASK) == ENABLED &&
mOnTouchListener.onTouch(this, event)
- 因此如果你有一个控件是非enable的,那么给它注册onTouch事件将永远得不到执行。对于这一类控件,如果我们想要监听它的touch事件,就必须通过在该控件中重写onTouchEvent方法来实现。
5.2 Touch事件的后续事件(MOVE、UP)层级传递
- 如果给控件注册了Touch事件,每次点击都会触发一系列action事件(ACTION_DOWN,ACTION_MOVE,ACTION_UP等)
- 当dispatchTouchEvent在进行事件分发的时候,只有前一个事件(如ACTION_DOWN)返回true,才会收到后一个事件(ACTION_MOVE和ACTION_UP)
- 即如果在执行ACTION_DOWN时返回false,后面一系列的ACTION_MOVE和ACTION_UP事件都不会执行
- 从上面对事件分发机制分析知:
- dispatchTouchEvent()和 onTouchEvent()消费事件、终结事件传递(返回true)
- 而onInterceptTouchEvent 并不能消费事件,它相当于是一个分叉口起到分流导流的作用,对后续的ACTION_MOVE和ACTION_UP事件接收起到非常大的作用
- 请记住:接收了ACTION_DOWN事件的函数不一定能收到后续事件(ACTION_MOVE、ACTION_UP)
- 这里给出ACTION_MOVE和ACTION_UP事件的传递结论:
- 如果在某个对象(Activity、ViewGroup、View)的dispatchTouchEvent()消费事件(返回true),那么收到ACTION_DOWN的函数也能收到ACTION_MOVE和ACTION_UP
- 如果在某个对象(Activity、ViewGroup、View)的onTouchEvent()消费事件(返回true),那么ACTION_MOVE和ACTION_UP的事件从上往下传到这个View后就不再往下传递了,而直接传给自己的onTouchEvent()并结束本次事件传递过程。
其他介绍
01.关于博客汇总链接
02.关于我的博客
- github:https://github.com/yangchong211
- 知乎:https://www.zhihu.com/people/yczbj/activities
- 简书:http://www.jianshu.com/u/b7b2c6ed9284
- csdn:http://my.csdn.net/m0_37700275
- 喜马拉雅听书:http://www.ximalaya.com/zhubo/71989305/
- 开源中国:https://my.oschina.net/zbj1618/blog
- 泡在网上的日子:http://www.jcodecraeer.com/member/content_list.php?channelid=1
- 邮箱:yangchong211@163.com
- segmentfault头条:https://segmentfault.com/u/xiangjianyu/articles
- 掘金:https://juejin.im/user/5939433efe88c2006afa0c6e
View事件机制源码分析的更多相关文章
- ApplicationEvent事件机制源码分析
<spring扩展点之三:Spring 的监听事件 ApplicationListener 和 ApplicationEvent 用法,在spring启动后做些事情> <服务网关zu ...
- React 为什么要把事件挂载到 document 上 & 事件机制源码分析
前言 我们都知道 React 组件绑定事件的本质是代理到 document 上,然而面试被问到,为什么要这么设计,有什么好处吗? 我知道肯定不会是因为虚拟 DOM 的原因,因为 Vue 的事件就能挂载 ...
- Android事件分发机制源码分析
Android事件分发机制源码分析 Android事件分发机制源码分析 Part1事件来源以及传递顺序 Activity分发事件源码 PhoneWindow分发事件源码 小结 Part2ViewGro ...
- SpringBoot事件监听机制源码分析(上) SpringBoot源码(九)
SpringBoot中文注释项目Github地址: https://github.com/yuanmabiji/spring-boot-2.1.0.RELEASE 本篇接 SpringApplicat ...
- Springboot学习04-默认错误页面加载机制源码分析
Springboot学习04-默认错误页面加载机制源码分析 前沿 希望通过本文的学习,对错误页面的加载机制有这更神的理解 正文 1-Springboot错误页面展示 2-Springboot默认错误处 ...
- Zepto事件模块源码分析
Zepto事件模块源码分析 一.保存事件数据的handlers 我们知道js原生api中要移除事件,需要传入绑定时的回调函数.而Zepto则可以不传入回调函数,直接移除对应类型的所有事件.原因就在于Z ...
- Android查缺补漏(View篇)--事件分发机制源码分析
在上一篇博文中分析了事件分发的流程及规则,本篇会从源码的角度更进一步理解事件分发机制的原理,如果对事件分发规则还不太清楚的童鞋,建议先看一下上一篇博文 <Android查缺补漏(View篇)-- ...
- hadoop的RPC机制 -源码分析
这些天一直奔波于长沙和武汉之间,忙着腾讯的笔试.面试,以至于对hadoop RPC(Remote Procedure Call Protocol ,远程过程调用协议,它是一种通过网络从远程计算机程序上 ...
- 【Cocos2d-x 3.x】 事件处理机制源码分析
在游戏中,触摸是最基本的,必不可少的.Cocos2d-x 3.x中定义了一系列事件,同时也定义了负责监听这些事件的监听器,另外,cocos定义了事件分发类,用来将事件派发出去以便可以实现相应的事件. ...
- Android异步消息传递机制源码分析
1.Android异步消息传递机制有以下两个方式:(异步消息传递来解决线程通信问题) handler 和 AsyncTask 2.handler官方解释的用途: 1).定时任务:通过handler.p ...
随机推荐
- MYSQL 1 DAY
目录 MySQL 1.sql.DB.DBMS分别是什么,他们之间的关系? 2.什么是表? 3.学习MySQL主要还是学习通用的SQL语句,那么SQL语句包括增删改查,SQL语句怎么分类呢? 4.导入数 ...
- 看似简单的input框输入竟然异常卡顿,记一个日常性能问题的排查思路
壹 ❀ 引 我们公司产品主要提供企业项目管理服务,那么自然有配套的desk工单管理系统,用于搜集客户bug以及相关问题反馈.有一天我在测试功能时碰巧发现了一个bug,所以就想着提一个工单记录下方便日后 ...
- Python def() 后的-> 符号的作用
python – 定义函数 def 后面的 ->,:表示的含义-> 常常出现在python函数定义的函数名后面,为函数添加元数据,描述函数返回的类型. : 表示参数的类型建议符示例: de ...
- Ubuntu20.04/22.04 ESP32 命令行开发环境配置
ESP32 芯片系列 ESP32分三个系列 ESP32-S ESP32-S3: Xtensa 32位 LX7 双核 240 MHz, 384KB ROM, 512KB SRAM, QFN7x7, 56 ...
- 贝壳云P1刷机记录(5.10内核Armbian)
说明 贝壳云基于瑞芯微的RK3328芯片, 芯片介绍, Cortex-A53架构, 4核, 1G内存, 8G eMMC. 板载1个千兆网口, 4个USB3.0. 这个盒子比较赞的地方就是不到百元的价格 ...
- 在SpringBoot中实践AOP编程
具体实践 Spring AOP是Spring框架中一个支持实现面向切面编程的模块,由于Spring Boot已经把Spring框架组合得非常好用,所以在基于Spring Boot框架的项目中实现AOP ...
- c# rdkafka 设置偏移量(offset)
参考资料: librdkafka: 如何设置Kafka消费者订阅消息的起始偏移位置 领导要求kafka消费者端消费最新的数据. 不知道怎么设置偏移量,查了资料. 用惯了封装好的东西,都不知道怎么设置了 ...
- 【Azure Redis 缓存】Lettuce 连接到Azure Redis服务,出现15分钟Timeout问题
问题描述 在Java应用中,使用 Lettuce 作为客户端SDK与Azure Redis 服务连接,当遇见连接断开后,长达15分钟才会重连.导致应用在长达15分的时间,持续报错Timeout 问题解 ...
- 【Azure 事件中心】如何查看事件中心的消息中具体报文内容呢?
问题描述 如何查看事件中心的消息中具体报文内容呢? 问题解答 正常情况是通过 Event Hub 的消费端获取消息进行处理查看,但是没有客户端代码的情况下,也可以通过微软的默认客户端Service B ...
- Android 安装手机程序有问题/点击runAPP 程序安装不了手机
可以在 gradle.properties 里添加 android.injected.testOnly=false 点击同步 就可以运行了 如下: