Android View系列(二):事件分發機制源碼解析

概述

在介紹點擊事件規則之前,我們需要知道我們分析的是MotionEvent,即點擊事件,所謂的事件分發就是對MotionEvent事件的分發過程,即當一個MotionEvent生成以后,系統需要把這個事件傳遞給具體的View,而這個傳遞過程就是分發過程,MotionEvent我們上節已經介紹過

事件分發主要涉及以下幾個方法:

  • dispatchTouchEvent:用來進行事件的分發,如果事件可以傳遞到當前View那么此方法一定會被調用,返回結果受當前View的onTouchEvent和子View的dispatchTouchEvent方法影響,表示是否消耗當前事件
  • onInterceptTouchEvent:在上個方法內部調用,用來判斷是否攔截事件,如果當前View攔截了事件,那么在同一時間序列內,此方法不會再次被調用,返回結果表示是否攔截事件
  • onTouchEvent:在dispatchTouchEvent方法中調用,用于事件的處理,返回值表示是否消耗事件,如果不消耗當前View無法再次接受到事件

這三個方法到底有什么關系?

我們先簡述一下他們之間的關系,之后再進行源碼的詳細分析

當一個事件傳遞給一個根ViewGroup之后,這時他的dispatchTouchEvent就會被調用,進行事件的分發,如果該ViewGroup的onInterceptTouchEvent返回true,表示他要攔截此事件,接著這個事件就會交給ViewGroup處理,即他的onTouchEvent就會被調用,如果他的onInterceptTouchEvent返回fasle就表示不攔截此事件,這時就會把此事件傳遞給他的子View,接著子View的dispatchTouchEvent就會被調用,如此反復直到事件最終被處理

源碼分析

當一個事件產生后,他的傳遞遵循如下順序Activity→Window→View,即事件總是縣傳遞給Activity,然后Activity傳遞給Window,最后Window傳遞給頂級View,頂級View接收到事件后,就會按照事件分發機制分發事件

Activity對事件的分發

當一個點擊操作發生時,事件最先傳遞給當前的Activity,由Activity的dispatchTouchEvent進行分發,我們看下Activity的dispatchTouchEvent的源碼

  public boolean dispatchTouchEvent(MotionEvent ev) {if (ev.getAction() == MotionEvent.ACTION_DOWN) {onUserInteraction();}if (getWindow().superDispatchTouchEvent(ev)) {return true;}return onTouchEvent(ev);}
復制代碼

上面代碼表示,Activity會把事件交給Window處理,如果Window的分發返回true,表示事件就此結束,返回false,表示沒有人處理,那么Activity的onTouchEvent就會被調用

Window對事件的分發

那么Window是怎么分發事件的呢?我們看下Window的源碼,我們發現Window其實是一個抽象類,superDispatchTouchEvent也是一個抽象方法

public abstract boolean superDispatchTouchEvent(MotionEvent event);
復制代碼

那么Window的實現類是什么?其實是PhoneWindow,那我們看一下PhoneWindow是怎么處理事件的

 @Overridepublic boolean superDispatchTouchEvent(MotionEvent event) {return mDecor.superDispatchTouchEvent(event);}
復制代碼

PhoneWindow直接把事件交給了DecorView,DecorView其實就是最頂層的View我們setContentView的View就是DecorView的一個子View,DecorView繼承自FrameLayout,這個時候事件已經分發到了ViewGroup上

ViewGroup事件的分發

現在我們看一下ViewGroup的dispatchTouchEvent方法的源碼

    @Overridepublic boolean dispatchTouchEvent(MotionEvent ev) {...
//--------TAG=1-------------------這里是一開始---------------------------------------------------//如果是Action_down 就對其先前所有的狀態進行重置if (actionMasked == MotionEvent.ACTION_DOWN) {cancelAndClearTouchTargets(ev);resetTouchState();}
//--------TAG=2-----------------這里開始進行攔截驗證-----------------------------------------------//如果是ACTION_DOWN,或者mFirstTouchTarget != null,就進行攔截驗證final boolean intercepted;if (actionMasked == MotionEvent.ACTION_DOWN|| mFirstTouchTarget != null) {final boolean disallowIntercept = (mGroupFlags & FLAG_DISALLOW_INTERCEPT) != 0;if (!disallowIntercept) {intercepted = onInterceptTouchEvent(ev);ev.setAction(action); // restore action in case it was changed} else {intercepted = false;}} else {// There are no touch targets and this action is not an initial down// so this view group continues to intercept touches.intercepted = true;}
//------------------------------------------------------------------------------------------------------------------------------....//----------TAG=3----------------這里看是遍歷子view---------------------------------------------------------------//如果不攔截,并且不是cancel事件,就進行遍歷子view分發事件if (!canceled && !intercepted) {...//當ACTION_DOWN和ACTION_POINTER_DOWN和ACTION_HOVER_MOVE時候才會遍歷子viewif (actionMasked == MotionEvent.ACTION_DOWN|| (split && actionMasked == MotionEvent.ACTION_POINTER_DOWN)|| actionMasked == MotionEvent.ACTION_HOVER_MOVE) {//找到可以接受觸摸事件孩子,從前向后遍歷查找final View[] children = mChildren;for (int i = childrenCount - 1; i >= 0; i--) {final int childIndex = getAndVerifyPreorderedIndex(childrenCount, i, customOrder);final View child = getAndVerifyPreorderedView(preorderedList, children, childIndex);...//判斷觸摸點是否在此View的范圍中,是否在移動if (!canViewReceivePointerEvents(child)|| !isTransformedTouchPointInView(x, y, child, null)) {ev.setTargetAccessibilityFocus(false);continue;}...//分發事件,如果事件被子view消費,就跳出循環,不再繼續分發給其他viewif (dispatchTransformedTouchEvent(ev, false, child, idBitsToAssign)) {...//addTouchTarget內部賦值mFirstTouchTarget=當前viewnewTouchTarget = addTouchTarget(child, idBitsToAssign);alreadyDispatchedToNewTouchTarget = true;break;}}
//-----------TAG=4-----------------這里已經遍歷完了子view--------------------------------------------// //遍歷完所有的子View后,還沒有處理事件,就自己處理if (mFirstTouchTarget == null) {// No touch targets so treat this as an ordinary view.handled = dispatchTransformedTouchEvent(ev, canceled, null,TouchTarget.ALL_POINTER_IDS);} else {//Action_Down之外的事件直接分發給目標viewTouchTarget predecessor = null;TouchTarget target = mFirstTouchTarget;while (target != null) {final TouchTarget next = target.next;//如果上方遍歷已經傳遞過改事件,則跳過本次事件if (alreadyDispatchedToNewTouchTarget && target == newTouchTarget) {handled = true;} else {final boolean cancelChild = resetCancelNextUpFlag(target.child)|| intercepted;if (dispatchTransformedTouchEvent(ev, cancelChild,target.child, target.pointerIdBits)) {handled = true;}...}}
//------------------------------------------------------------------------------------------------------------------------------// Update list of touch targets for pointer up or cancel, if needed.if (canceled|| actionMasked == MotionEvent.ACTION_UP|| actionMasked == MotionEvent.ACTION_HOVER_MOVE) {resetTouchState();} else if (split && actionMasked == MotionEvent.ACTION_POINTER_UP) {final int actionIndex = ev.getActionIndex();final int idBitsToRemove = 1 << ev.getPointerId(actionIndex);removePointersFromTouchTargets(idBitsToRemove);}}if (!handled && mInputEventConsistencyVerifier != null) {mInputEventConsistencyVerifier.onUnhandledEvent(ev, 1);}return handled;}
復制代碼

首先我們分析一下攔截事件的源碼

   			//如果是ACTION_DOWN,或者mFirstTouchTarget != null,就進行攔截驗證final boolean intercepted;if (actionMasked == MotionEvent.ACTION_DOW || mFirstTouchTarget != null) {final boolean disallowIntercept = (mGroupFlags & FLAG_DISALLOW_INTERCEPT) != 0;if (!disallowIntercept) {intercepted = onInterceptTouchEvent(ev);ev.setAction(action); // restore action in case it was changed} else {intercepted = false;}} else {// There are no touch targets and this action is not an initial down// so this view group continues to intercept touches.intercepted = true;}
復制代碼

這段代碼我們可以看到,有倆種情況會判斷是否要攔截當前事件,事件類型是Action_Down,或者mFirstTouchTarget != null,ACTION_DOWN我們可以理解,mFirstTouchTarget != null代表什么呢?

我們從后面的代碼可以看出,事件由ViewGroup的子元素處理成功時,mFirstTouchTarget被賦值并指向該子元素,也就是說當ViewGroup不攔截事件交由子元素處理時mFirstTouchTarget != null

一旦ViewGroup攔截事件mFirstTouchTarget != null就不成立,而當ACTION_MOVE ,ACTION_UP到來時,由于(actionMasked == MotionEvent.ACTION_DOWN|| mFirstTouchTarget != null)這個判斷為false,ViewGroup的onInterceptTouchEvent不在會被調用,并且同一序列的其他事件,會默認交給ViewGroup處理

這里還有一種特殊情況,FLAG_DISALLOW_INTERCEPT標志位,這個標志位是通過requestDisallowInterceptTouchEvent來設置的,一般用于子View中,一旦FLAG_DISALLOW_INTERCEPT標志為被設置后,ViewGroup將無法攔截,除了ACTION_DOWN之外的其他事件,為什么要除了ACTION_DOWN呢,因為每當ACTION_DOWN帶來都會重置FLAG_DISALLOW_INTERCEPT這個標記位,ACTION_DOWN事件總會調用自己的onInterceptTouchEvent詢問是否攔截

強調一點requestDisallowInterceptTouchEvent,這個方法并不是萬能的,執行他的前提是子View必須獲取事件,假如父View的Down事件的onInterceptTouchEvent就返回true,攔截事件,那么子View做任何操作也不可能獲取到事件

從上面分析我們可以得出結論

  • 當ViewGroup決定攔截事件的時候,那么后續的點擊事件將默認交給他,不再調用onInterceptTouchEvent
  • FLAG_DISALLOW_INTERCEPT作用是讓ViewGroup不再攔截事件,前提是ViewGroup不攔截Action_Down事件
  • onInterceptTouchEvent不是每次都會調用的,如果我們要提前處理點擊事件需要在dispatchTouchEvent
  • 當我們遇到滑動沖突的時候,可以考慮FLAG_DISALLOW_INTERCEPT來處理

我們看一下ViewGroup不攔截的事件的情況

先看一下源碼,這個是刪減后的源碼,看起來比較清楚

            //如果不攔截,并且不是cancel事件,就進行遍歷子view分發事件if (!canceled && !intercepted) {...//當ACTION_DOWN和ACTION_POINTER_DOWN和ACTION_HOVER_MOVE時候才會遍歷子viewif (actionMasked == MotionEvent.ACTION_DOWN|| (split && actionMasked == MotionEvent.ACTION_POINTER_DOWN)|| actionMasked == MotionEvent.ACTION_HOVER_MOVE) {//找到可以接受觸摸事件孩子,從前向后遍歷查找final View[] children = mChildren;for (int i = childrenCount - 1; i >= 0; i--) {final int childIndex = getAndVerifyPreorderedIndex(childrenCount, i, customOrder);final View child = getAndVerifyPreorderedView(preorderedList, children, childIndex);...//判斷觸摸點是否在此View的范圍中,是否在移動if (!canViewReceivePointerEvents(child)|| !isTransformedTouchPointInView(x, y, child, null)) {ev.setTargetAccessibilityFocus(false);continue;}...//分發事件,如果事件被子view消費,就跳出循環,不再繼續分發給其他viewif (dispatchTransformedTouchEvent(ev, false, child, idBitsToAssign)) {...//addTouchTarget內部賦值mFirstTouchTarget=當前viewnewTouchTarget = addTouchTarget(child, idBitsToAssign);alreadyDispatchedToNewTouchTarget = true;break;}}
復制代碼

首先遍歷ViewGroup的所有子元素,然后判斷判斷子元素是否能接收到點擊事件,是否能接收到點擊事件主要由倆點來衡量

  • 點擊的坐標是否落在了子元素的區域內
  • 子元素是否在播放動畫

如果子元素滿足這倆個條件,那么事件將傳遞給他處理,分發事件其實dispatchTransformedTouchEvent是這個方法做的,我們看一下dispatchTransformedTouchEvent源碼

 private boolean dispatchTransformedTouchEvent(MotionEvent event, boolean cancel,View child, int desiredPointerIdBits) {final boolean handled;//先記住這一段判斷cancel的源碼,很重要下面分析if (cancel || oldAction == MotionEvent.ACTION_CANCEL) {event.setAction(MotionEvent.ACTION_CANCEL);if (child == null) {handled = super.dispatchTouchEvent(event);} else {handled = child.dispatchTouchEvent(event);}event.setAction(oldAction);return handled;}....if (child == null) {handled = super.dispatchTouchEvent(event);} else {...handled = child.dispatchTouchEvent(event);}.....return handled;}
復制代碼

這里面主要代碼如果 if (cancel || oldAction == MotionEvent.ACTION_CANCEL) 為false,這個判斷的意思是,如果不是ACTION_CANCEL,外部傳入的cancel也為fasle,就進行下面的判斷,而下面的判斷主要是根據傳入的child是否為null來判斷的,如果child不為null,那么就調用child的dispatchTouchEvent方法,這個事件就交給子元素去處理,這就完成一輪的事件分發

如果child的dispatchTouchEvent返回為true,先不考慮事件怎么在子元素中分發,那么mFirstTouchTarget就被賦值,跳出for循環

    //分發事件,如果事件被子view消費,就跳出循環,不再繼續分發給其他viewif (dispatchTransformedTouchEvent(ev, false, child, idBitsToAssign)) {...//addTouchTarget內部賦值mFirstTouchTarget=當前viewnewTouchTarget = addTouchTarget(child, idBitsToAssign);alreadyDispatchedToNewTouchTarget = true;break;}
復制代碼

上面的代碼完成了,給mFirstTouchTarget賦值,并且跳出for循環,終止對子元素的遍歷,如果子元素的dispatchTouchEvent返回fasle,那么就會繼續遍歷子元素,把事件傳遞給下一個合適的子元素(如果還有合適的子元素的話)

mFirstTouchTarget賦值是在addTouchTarget方法內部完成的,mFirstTouchTarget是一個單鏈表結構

  private TouchTarget addTouchTarget(@NonNull View child, int pointerIdBits) {final TouchTarget target = TouchTarget.obtain(child, pointerIdBits);//注意這里這里很重要,target.next =null,然后 mFirstTouchTarget = target;也就是說這時候的 mFirstTouchTarget.next=nulltarget.next = mFirstTouchTarget;mFirstTouchTarget = target;return target;}
復制代碼

如果遍歷所有的子元素事件都沒有合適的處理,這里包含倆種情況,一種就是ViewGroup沒有子元素,第二種就是子元素的dispatchTouchEvent返回了fasle,這倆種情況下ViewGroup會自己處理事件

       //遍歷完所有的子View后,還沒有處理事件,就自己處理if (mFirstTouchTarget == null) {handled = dispatchTransformedTouchEvent(ev, canceled, null,TouchTarget.ALL_POINTER_IDS);}
復制代碼

注意這里child參數傳入的是null,根據之前的分析就會調用 super.dispatchTouchEvent(event);由于ViewGroup也是繼承自View,這里就會轉到View的dispatchTouchEvent,即點擊事件交給View處理

注意敲黑板了啊

我看了很多博客,都沒有對這種情況進行分析,這個問題一度卡了我很久

現在考慮一種情況,如果父View的onInterceptTouchEvent的Down事件返回false不攔截,move up事件返回true攔截,這個效果就是子View只能收到Down事件而收不到Up和Move事件

那么我們現在分析一下這種情況,按照我們上方的分析,父View的Down事件不攔截,那么mFirstTouchTarget就會被賦值,第二次Move和Up事件要攔截,但是由于mFirstTouchTarget被賦值了,所以是走不到下面這步的

    // //遍歷完所有的子View后,還沒有處理事件,就自己處理if (mFirstTouchTarget == null) {// No touch targets so treat this as an ordinary view.handled = dispatchTransformedTouchEvent(ev, canceled, null,TouchTarget.ALL_POINTER_IDS);}
復制代碼

那么父View是怎么攔截Move和Up事件的呢? 當地一個Move事件傳遞給父View后,此時mFirstTouchTarget不為null,所以走攔截這一步代碼

		//如果是ACTION_DOWN,或者mFirstTouchTarget != null,就進行攔截驗證final boolean intercepted;if (actionMasked == MotionEvent.ACTION_DOW || mFirstTouchTarget != null) {final boolean disallowIntercept = (mGroupFlags & FLAG_DISALLOW_INTERCEPT) != 0;if (!disallowIntercept) {intercepted = onInterceptTouchEvent(ev);ev.setAction(action); // restore action in case it was changed} else {intercepted = false;}} else {// There are no touch targets and this action is not an initial down// so this view group continues to intercept touches.intercepted = true;}
復制代碼

攔截返回true后,不走遍歷子Vew代碼,直接到最后的判斷代碼

    // //遍歷完所有的子View后,還沒有處理事件,就自己處理if (mFirstTouchTarget == null) {// No touch targets so treat this as an ordinary view.handled = dispatchTransformedTouchEvent(ev, canceled, null,TouchTarget.ALL_POINTER_IDS);} else {//Action_Down之外的事件直接分發給目標viewTouchTarget predecessor = null;TouchTarget target = mFirstTouchTarget;while (target != null) {final TouchTarget next = target.next;//如果上方遍歷已經傳遞過改事件,則跳過本次事件if (alreadyDispatchedToNewTouchTarget && target == newTouchTarget) {handled = true;} else {final boolean cancelChild = resetCancelNextUpFlag(target.child)|| intercepted;if (dispatchTransformedTouchEvent(ev, cancelChild,target.child, target.pointerIdBits)) {handled = true;}if (cancelChild) {if (predecessor == null) {mFirstTouchTarget = next;} else {predecessor.next = next;}target.recycle();target = next;continue;}}}
復制代碼

由于mFirstTouchTarget在Down的時候已經賦值不為null,會走下邊代碼

       final boolean cancelChild = resetCancelNextUpFlag(target.child)|| intercepted;
復制代碼

由于攔截事件,cancelChild為true,也就是說下面這個分發dispatchTransformedTouchEvent的方法傳入的是true

     if (dispatchTransformedTouchEvent(ev, cancelChild,target.child, target.pointerIdBits)) {handled = true;}
復制代碼

在這個分發方法里,有判斷Cancel事件的代碼

 private boolean dispatchTransformedTouchEvent(MotionEvent event, boolean cancel,View child, int desiredPointerIdBits) {final boolean handled;//先記住這一段判斷cancel的源碼,很重要下面分析if (cancel || oldAction == MotionEvent.ACTION_CANCEL) {event.setAction(MotionEvent.ACTION_CANCEL);if (child == null) {handled = super.dispatchTouchEvent(event);} else {handled = child.dispatchTouchEvent(event);}event.setAction(oldAction);return handled;}...return handled;}
復制代碼

由于傳入的cancel為true, 會重新定義事件為Cancel事件event.setAction(MotionEvent.ACTION_CANCEL);child不為null所以會調用child.dispatchTouchEvent(event);也就是說第一個Move事件,父View不會攔截,但會給子View發送一個Cancel事件

接下來會繼續走代碼

  TouchTarget target = mFirstTouchTarget;final TouchTarget next = target.next;
...if (cancelChild) {...mFirstTouchTarget = next;...}
復制代碼

上面已經分析過cancelChild為true,進入方法給mFirstTouchTarget重新賦值mFirstTouchTarget.next,那么mFirstTouchTarget.next等于什么?看下面一段代碼

  private TouchTarget addTouchTarget(@NonNull View child, int pointerIdBits) {final TouchTarget target = TouchTarget.obtain(child, pointerIdBits);//注意這里這里很重要,target.next =null,然后 mFirstTouchTarget = target;也就是說這時候的 mFirstTouchTarget.next=nulltarget.next = mFirstTouchTarget;mFirstTouchTarget = target;return target;}
復制代碼

其實mFirstTouchTarget.next=null,那整合起來就是把mFirstTouchTarget重新賦值為null,從這里開始,第二個Move事件就會直接傳遞給父View完成了攔截

    if (mFirstTouchTarget == null) {handled = dispatchTransformedTouchEvent(ev, canceled, null,TouchTarget.ALL_POINTER_IDS);} 
復制代碼

總結

當父View不攔截Down事件,但要攔截Move和Up事件時,第一個Move事件會重新賦值為Cancel事件發送給子View,然后mFirstTouchTarget賦值為null,第二次開始的Move事件就會交給父View

View的事件分發源碼

View對事件的處理比較簡單,注意這里的View不包括ViewGroup,先看他的dispatchTouchEvent

 public boolean dispatchTouchEvent(MotionEvent event) {...boolean result = false;ListenerInfo li = mListenerInfo;if (li != null && li.mOnTouchListener != null&& (mViewFlags & ENABLED_MASK) == ENABLED&& li.mOnTouchListener.onTouch(this, event)) {result = true;}if (!result && onTouchEvent(event)) {result = true;}}...return result;}
復制代碼

View的時間傳遞比較簡單,因為View(不包括ViewGroup),是一個單獨的元素,無法向下傳遞事件,所以沒有onInterceptTouchEvent方法,從上面源碼可以看出

  • 首先會判斷與沒有mOnTouchListener,如果有并且其中的onTouch方法返回true那么onTouchEvent放方法不會調用,可以看出mOnTouchListener的優先級高于onTouchEvent

下面看一下onTouchEvent方法的源碼

首先看一下,當View處于不可用狀態下,事件的處理過程

   final boolean clickable = ((viewFlags & CLICKABLE) == CLICKABLE|| (viewFlags & LONG_CLICKABLE) == LONG_CLICKABLE)|| (viewFlags & CONTEXT_CLICKABLE) == CONTEXT_CLICKABLE;if ((viewFlags & ENABLED_MASK) == DISABLED) {if (action == MotionEvent.ACTION_UP && (mPrivateFlags & PFLAG_PRESSED) != 0) {setPressed(false);}mPrivateFlags3 &= ~PFLAG3_FINGER_DOWN;// A disabled view that is clickable still consumes the touch// events, it just doesn't respond to them.return clickable;}
復制代碼

可以看出不可用的狀態下,View消耗點擊事件

再看一下對具體事件的處理

   final boolean clickable = ((viewFlags & CLICKABLE) == CLICKABLE|| (viewFlags & LONG_CLICKABLE) == LONG_CLICKABLE)|| (viewFlags & CONTEXT_CLICKABLE) == CONTEXT_CLICKABLE;if (clickable || (viewFlags & TOOLTIP) == TOOLTIP) {switch (action) {case MotionEvent.ACTION_UP:...if (mPerformClick == null) {mPerformClick = new PerformClick();}if (!post(mPerformClick)) {performClickInternal();}}}....case MotionEvent.ACTION_DOWN:...case MotionEvent.ACTION_CANCEL:...case MotionEvent.ACTION_MOVE:...break;}復制代碼

從上面代碼為可以看出

  • 只要View的CLICKABLE和LONG_CLICKABLE一個為true,不管他是不是DISABLED狀態都消耗事件,只不過DISABLED不走下面的down,up事件
  • 當Action_Up觸發時,會調用PerformClick方法,如果View設置了onClickListener,那么PerformClick將調用他的onClick方法
  • View的LONG_CLICKABLE默認是false,但是CLICKABLE是否為fasle,跟具體View有關,可點擊的CLICKABLE為true,不可點擊的CLICKABLE為false
  • setClickable和setLongClickable可以改變CLICKABLE,和LONG_CLICKABLE的值
  • setClickLinsterer和setLongClickLinsterer會自動設置CLICKABLE和LONG_CLICKABLE為true

到這里事件分發就處理完了

參考:Android開發藝術探索

allenfeng.com/2017/02/22/…

轉載于:https://juejin.im/post/5d4d212d5188253afe4c8ada

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/386238.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/386238.shtml
英文地址,請注明出處:http://en.pswp.cn/news/386238.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

從零開始系統化學Android,值得收藏!

前言 每年的3、4月份是各大企業為明年拓展業務大量吸納人才的關鍵時期&#xff0c;招聘需求集中、空缺崗位多&#xff0c;用人單位也習慣在初秋進行大規模招聘。 金九銀十&#xff0c;招聘旺季&#xff0c;也是一個求職旺季。 不打無準備的仗&#xff0c;在這種關鍵時期&…

[轉]清華夢的粉碎——寫給清華大學的退學申請

[轉]清華夢的粉碎——寫給清華大學的退學申請 讀了全文&#xff0c;感同身受&#xff0c;全文轉載。 By 王垠&#xff08;2005.09.22&#xff09; 作者王垠&#xff0c;非常有思想的一個人&#xff0c;川大計算機系97級本科&#xff0c;2001年畢業后直博保送清華大學計算機系&a…

網易嚴選Java開發三面面經:java技術編程培訓班

前言 Spring Security是一個功能強大且高度可定制的身份驗證和訪問控制框架。提供了完善的認證機制和方法級的授權功能。是一款非常優秀的權限管理框架。它的核心是一組過濾器鏈&#xff0c;不同的功能經由不同的過濾器。這篇文章就是想通過一個小案例將Spring Security整合到…

第九篇 并發(進程和線程)

Python里執行并發有進程和線程兩個&#xff0c;分布使用 threading 和multiprocessing 兩個庫&#xff0c;一般用的是這兩個庫里的Thread和Process from threading import Thread # 線程 : 同時運行from multiprocessing import Process # 進程 進程和線程的使用場景&#x…

網易嚴選Java開發三面面經:java讀文件內容

一、前言 Redis 提供了5種數據類型&#xff1a;String&#xff08;字符串&#xff09;、Hash&#xff08;哈希&#xff09;、List&#xff08;列表&#xff09;、Set&#xff08;集合&#xff09;、Zset&#xff08;有序集合&#xff09;&#xff0c;理解每種數據類型的特點對于…

網易嚴選Java開發三面面經:mysql索引面試題

寫在前面 最近&#xff0c;很多小伙伴出去面試都被問到了Spring問題&#xff0c;關于Spring&#xff0c;細節點很多&#xff0c;面試官也非常喜歡問一些很細節的技術點。所以&#xff0c;在 Spring 專題中&#xff0c;我們盡量把Spring的每個技術細節說清楚&#xff0c;將透徹…

網易嚴選Java開發三面面經:南京黑馬java培訓怎么樣

個人背景 如標題所示&#xff0c;我的個人背景非常簡單&#xff0c;Java開發經驗1年半&#xff0c;學歷普通&#xff0c;2本本科畢業&#xff0c;畢業后出來就一直在Crud&#xff0c;在公司每天重復的工作對我的技術提升并沒有什么幫助&#xff0c;但小鎮出來的我也深知自我努…

網易架構師深入講解Java開發!BAT等大廠必問技術面試題

前言 MyBatis是目前非常流行的ORM框架&#xff0c;它的功能很強大&#xff0c;然而其實現卻比較簡單、優雅。本文主要講述MyBatis的架構設計思路&#xff0c;并且討論MyBatis的幾個核心部件&#xff0c;然后結合一個select查詢實例&#xff0c;深入代碼&#xff0c;來探究MyBa…

IOS 消息轉發

最近在看消息轉發的資料&#xff0c;發現大部分都是理論知識&#xff0c;很少有完整的代碼。現在以代碼的形式形象的解釋一下&#xff1a; 用Xcode創建一個工程 1.正常方法調用 創建一個類Person 代碼如下 Person.h代碼如下&#xff1a; #import <Foundation/Foundation.h&g…

網易資深Java架構師:java數組對象轉為list集合

前言 現在刷抖音經常可以看到一些老外街坊&#xff0c;問他們最想把什么帶回自己的國家&#xff0c;我聽過很多的回答都是&#xff1a;淘寶&#xff0c;支付寶&#xff0c;美食&#xff0c;微信&#xff0c;外賣&#xff0c;高鐵等等。 確實如此&#xff0c;隨著國家的快速發…

夯實基礎——P2084 進制轉換

題目鏈接&#xff1a;https://www.luogu.org/problem/P2084 P2084 進制轉換 題目背景 無 題目描述 今天小明學會了進制轉換&#xff0c;比如&#xff08;10101&#xff09;2 &#xff0c;那么它的十進制表示的式子就是 : 1*2^40*2^31*2^20*2^11*2^0&#xff0c; 那么請你編程實…

網易資深Java架構師:java方法的定義和使用

前言 今年因為這個疫情&#xff0c;感覺這是從工作以來過的最久的一個年了&#xff0c;在家呆的時間不是一般的久&#xff0c;算一算有好幾個月呢&#xff01;我大概是3月底快4月了才出門&#xff0c;投了超多的簡歷&#xff0c;天天面試面試面試面試面試面試面試…慶幸的是還…

PHP----學生管理系統

閑來無事花費兩天時間寫了份簡易版的學生管理系統 源碼地址:https://www.cnblogs.com/post/ReadAuth?blogId509327&PostId11333758&url%2Fbyczyz%2Fprotected%2Fp%2F11333758.html 轉載于:https://www.cnblogs.com/byczyz/p/11333760.html

網易資深Java架構師:jdkjrejvm的區別和聯系

前言 作為同時具備高性能、高可靠和高可擴展性的典型鍵值數據庫&#xff0c;Redis不僅功能強大&#xff0c;而且穩定&#xff0c;理所當然地成為了大型互聯網公司的首選。 眾多大廠在招聘的時候&#xff0c;不僅會要求面試者能簡單地使用Redis&#xff0c;還要能深入地理解底…

深度學習之開端備注

Adagrad //適合稀疏樣本 RMSprop//借鑒Adagrad的思想&#xff0c;改進使得不會出現學習率越來越低的問題 由此可見Adadelta既不需要輸入學習率等參數&#xff0c;而且表現得非常好&#xff01;&#xff01;但是我試了幾次&#xff0c;這個優化器效果極差&#xff01;&#xff0…

網易資深Java架構師:疫情對java行業的影響分析

前言 在實際開發&#xff0c;Redis使用會頻繁&#xff0c;那么在使用過程中我們該如何正確抉擇數據類型呢&#xff1f;哪些場景下適用哪些數據類型。而且在面試中也很常會被面試官問到Redis數據結構方面的問題&#xff1a; Redis為什么快呢&#xff1f;為什么查詢操作會變慢了…

ListView與.FindControl()方法的簡單練習 #2 -- ItemUpdting事件中抓取「修改后」的值

原文出處 http://www.dotblogs.com.tw/mis2000lab/archive/2013/06/24/listview_itemupdating_findcontrol_20130624.aspx ListView與.FindControl()方法的簡單練習 #2 -- ItemUpdting事件中抓取「修改后」的值 本文跟上一篇文章有關連&#xff0c;請依照順序來練習&#xff1…

美團java研發崗二面:java靜態方法存儲在哪個區

思維導圖 前言 在很多時候&#xff0c;我們都可以在各種框架應用中看到ZooKeeper的身影&#xff0c;比如Kafka中間件&#xff0c;Dubbo框架&#xff0c;Hadoop等等。為什么到處都看到ZooKeeper&#xff1f; 一、 前些年&#xff0c;互聯網行業里對架構師這個崗位的標準還不是…

[學習之道] 修福不修慧,大象披瓔珞; 修慧不修福,羅漢托空缽 (學習寫程序,只靠補習上課嗎?)...

這是我的備份&#xff0c;原文請看 http://www.dotblogs.com.tw/mis2000lab/archive/2014/09/17/learning-and_do-it_20140917.aspx [學習之道] 修福不修慧&#xff0c;大象披瓔珞&#xff1b; 修慧不修福&#xff0c;羅漢托空缽 (學習寫程序&#xff0c;只靠補習上課嗎&#…

阿里P8親自教你!mysql列轉行

前言 今日博主聽聞&#xff0c;現在很多培訓出來的應屆生薪資都趕上了摸爬滾打兩三年的朋友&#xff0c;講道理&#xff0c;這說不過去啊 作為同行來說&#xff0c;這個行業發展很快&#xff0c;技術更新很快&#xff0c;淘汰也很快&#xff0c;千萬不要再找借口了&#xff0…