主頁 >  其他 > Android程式員事件分發機制學習筆記

Android程式員事件分發機制學習筆記

2020-09-17 17:57:07 其他

 

 

通過問題來學習一個東西是很好的方法,學習Android中View的事件體系,我也通過給自己提問題,在解決問題的同時也就知道了其中原理,

首先來幾個問題起步:

  • 什么是事件?什么是事件分發機制?

在我們通過螢屏與手機互動的時候,每一次點擊、長按、移動等都是一個個事件,按照面向物件的思想,這些一個個事件都被封裝成了MotionEvent,

分發機制就是某一個事件從螢屏傳遞給app視圖中的各個View,然后由其中的某個View來使用這一事件或者忽略這一事件,這整個程序的控制就是分發機制了,

要注意的是,事件分發機制中,事件是按一個事件序列的形式分發給View的,這一序列由 ACTION_DOWN 開始,經過一系列 ACTION_MOVE 等事件,最后以 ACTION_UP 事件結束,這一個序列中的所有事件,要么被忽略,要么就只能有一個事件能使用,要是同一個序列,比如從按下到移動這一系列的動作,不同的View都能接受的話,那整個界面就會非常混亂,而且邏輯很復雜,

接下來我提出這三個問題:

某一個事件從螢屏一直傳遞到View上這一程序的大致流程是怎樣的?前面說了事件分發的其實是事件序列,那么同一個序列里那么多事件,是怎樣的機制只交給一個View的?我們平時在應用開發時,在外部給View設定的的OnClick OnLongClick 的監聽,是在哪里被View處理的?

問題一:事件傳遞的流程是怎樣的?

Android中的View是樹狀結構,如下圖所示:

Android程式員事件分發機制學習筆記

 

每一個Activity內部都包含一個Window用來管理要顯示的視圖,而Window是一個抽象類,其具體實作是 PhoneWindow類,DecovrView作為PhoneWindow的一個內部類,實際管理著具體視圖的顯示,他是FrameLayout的子類,盛放著我們的標題欄和根視圖,我們自己寫的一些列View和ViewGroup都是由他來管理的,因此事件分發的時候,頂層的這些“大View”們實際上是不會對事件有任何操作的,他們只是把事件不斷的向下遞交,直到我們可以使用這些事件,

所以,事件自頂向下的傳遞程序應該是這樣的:

Activity(不處理)-> 根View -> 一層一層ViewGroup(如果有的話) -> 子View

如果傳遞到最后我們的子View們沒有處理這一事件怎么辦呢?這時候就會原路回傳,最終傳遞給Activity,只有當Activity也沒有處理這一事件時,這一事件才會被丟棄,

Activity(不處理則丟棄) <- 根View <- 一層一層ViewGroup(如果有的話) <- 子View

具體在傳遞事件的時候,是由以下三個方法來控制的:

  • dispatchTouchEvent : 分發事件onInterceptTouchEvent : 攔截事件onTouchEvent : 消費事件

這三個方法有一個共同點,就是他們具體是否執行了自己的功能(分發、攔截、消費)完全由自己的回傳值來確定,回傳true就表示自己完成了自己的功能(分發、攔截、消費),不同之處除了功能外,還有使用的場景,dispatchTouchEvent()和onTouchEvent()這兩個方法,無論是Activity ViewGroup 還是View,都會被用到,而onInterceptTouchEvent()方法因為只是為了攔截事件,那么Activity和View一個在最頂層,一個在最底層,也就沒必要使用了,因此在View 和 Activity中是沒有onInterceptTouchEvent()方法的,

我這里自定義幾個ViewGroup和View,分別重寫他們的這些方法,在重寫的時候打上log,在不添加任何監聽(即沒有View消費事件)的條件下看一下運行結果:

點擊外部ViewGroup:

Android程式員事件分發機制學習筆記

 

點擊子View:

Android程式員事件分發機制學習筆記

 

可以看到,事件分發首先由ViewGroup的dispatchTouchEvent()方法開始,先呼叫自己的onInterceptTouchEvent()方法判斷是否攔截,回傳false表示自己沒有攔截,那么接下來直接把事件傳給子View,子View呼叫自己的dispatchTouchEvent()方法進行分發,因為View沒有onInterceptTouchEvent()方法,所以不存在攔截操作,因此直接將事件交給自己的onTouchEvent()方法消費,因為我的子View沒有使用這個事件,因此onTouchEvent()方法直接回傳了false表示自己沒有消費,那么這個事件此時就算是傳到底了,因為自己沒有消費,因此自己就沒有分發出去,那么子View的dispatchTouchEvent()方法回傳false,把這個事件交還給上一層的ViewGroup,ViewGroup發現這個事件沒有子View消費,那么就自己動手吧!將事件傳給自己的onTouchEvent()方法消費,可是ViewGroup也沒有消費,那么onTouchEvent()方法只能是再回傳false了,同理,ViewGroup自己沒有消費事件,因此他的dispatchTouchEvent()方法也回傳了false,這段文字說得可能有點亂,那么就貼一張圖來演示一下:(圖中紅色箭頭表示事件自頂向下分發的程序,黃色則表示自底向上回傳的程序)

Android程式員事件分發機制學習筆記

 

接下來,我在子View上添加OnClick監聽,再看一下點擊子View時的運行結果:

Android程式員事件分發機制學習筆記

 

乍一看,呀,怎么重復列印了兩遍log?其實并不是哪里寫錯了,前面我說了,事件分發分發的是一個事件序列,我添加了點擊事件,那么我就要消費點擊事件,而點擊事件其實是要分成兩個事件的,即ACTION_DOWN + ACTION_UP ,只有這樣才算是一次點擊事件,因此列印了“兩遍”log其實是先列印了ACTION_DOWN的分發流程,再列印了一遍ACTION_UP的分發流程,因此會看到最后一行列印了click事件,即,click事件是在ACTION_UP事件發生后才發生的,

然后看看各個方法的回傳值,果然由于我的子View明確表示要消費這個事件序列,因此從ACTION_DOWN開始的所有事件就都交給他消費了,所以子View的onTouchEvent的回傳值為true,表示自己需要消費這個事件,然后他的dispatchTouchEvent也回傳了true,表示這一事件被自己分發了,既然自己的子View消費了事件,ViewGroup就認為這一事件是被自己分發了,因此他的dispatchTouchEvent也就回傳了true,還是來一張圖更清楚一點:最后,我在上一步的基礎上,給ViewGroup的onInterceptTouchEvent()方法回傳值強行改為true,表示事件傳到這一層的時候就被攔截了,看一下log:

果然,雖然我要在子View消費事件,但是事件在傳到子View之前就被ViewGroup攔截了,那么事件就只會由ViewGroup來消費了,所以ViewGroup就把事件傳給了自己的onTouchEvent()來消費,再來一張圖:

綜上,事件分發的大致流程就是這樣,

問題二:如何保證統一序列的事件都交給一個View來處理

先上結論:在傳遞程序中,只要有一個View主動去消費了第一個事件(ACTION_DOWN),那么ViewGroup會將這個View保存起來,之后同一事件序列的其他事件都直接交給這個View來處理,具體怎么操作,需要看一下原始碼:

//這是ViewGroup dispatchTouchEvent()的原始碼:
@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
//省略前面一部分無關代碼
//handled是回傳的結果,表示是否被分發,默認當然是
boolean handled = false;
if (onFilterTouchEventForSecurity(ev)) {
final int action = ev.getAction();
final int actionMasked = action & MotionEvent.ACTION_MASK;
// 判斷一下是不是ACTION_DOWN,如果是的話,代表一個新的事件序列來臨了
if (actionMasked == MotionEvent.ACTION_DOWN) {
//要注意一下這兩個方法,在這里會做一下相當于是“清零”的操作
//在這里包含了諸如mFirstTouchTarget=null這樣的初始化操作
cancelAndClearTouchTargets(ev);
resetTouchState();
}
// intercepted是用來記錄是否被攔截的結果
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 {
// 沒有mFirstTouchTarget,同時事件為非ACTION_DOWN,那么就算要在這里攔截了
intercepted = true;
}
//忽略部分攔截相關的代碼
//這兩個物件記一下,后面會碰到
TouchTarget newTouchTarget = null;
boolean alreadyDispatchedToNewTouchTarget = false;
if (!canceled && !intercepted) {
// 這里就開始對事件型別區分了,如果是ACTION_DOWN,那么就算是一個新的事件序列開始
if (actionMasked == MotionEvent.ACTION_DOWN
|| (split && actionMasked == MotionEvent.ACTION_POINTER_DOWN)
|| actionMasked == MotionEvent.ACTION_HOVER_MOVE) {
// 準備一下,接下來開始遍歷自己的子View們
final int childrenCount = mChildrenCount;
if (newTouchTarget == null && childrenCount != 0) {
// 獲取到點擊的坐標,用來從子View中篩選出點擊到的VIEW
final float x = ev.getX(actionIndex);
final float y = ev.getY(actionIndex);
// 按從后向前的順序開始遍歷子View們
final ArrayList<View> preorderedList = buildTouchDispatchChildList();
final boolean customOrder = preorderedList == null
&& isChildrenDrawingOrderEnabled();
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們過濾掉
//一個一個continue就表示在發現View不合適的時候直接進入下一次回圈
if (childWithAccessibilityFocus != null) {
if (childWithAccessibilityFocus != child) {
continue;
}
childWithAccessibilityFocus = null;
i = childrenCount - 1;
}
if (!canViewReceivePointerEvents(child)
|| !isTransformedTouchPointInView(x, y, child, null)) {
ev.setTargetAccessibilityFocus(false);
continue;
}
//終于找到了合適的子View,注意這里將子View封裝為一個target
//要是回傳的結果不為空就跳出回圈
newTouchTarget = getTouchTarget(child);
if (newTouchTarget != null) {
// Child is already receiving touch within its bounds.
// Give it the new pointer in addition to the ones it is handling.
newTouchTarget.pointerIdBits |= idBitsToAssign;
break;
}
//就算回傳結果為空也沒關系,在這里繼續遞回的呼叫子View的dispatchTransformedTouchEvent()
resetCancelNextUpFlag(child);
if (dispatchTransformedTouchEvent(ev, false, child, idBitsToAssign)) {
// Child wants to receive touch within its bounds.
mLastTouchDownTime = ev.getDownTime();
if (preorderedList != null) {
// childIndex points into presorted list, find original index
for (int j = 0; j < childrenCount; j++) {
if (children[childIndex] == mChildren[j]) {
mLastTouchDownIndex = j;
break;
}
}
} else {
mLastTouchDownIndex = childIndex;
}
mLastTouchDownX = ev.getX();
mLastTouchDownY = ev.getY();
newTouchTarget = addTouchTarget(child, idBitsToAssign);
alreadyDispatchedToNewTouchTarget = true;
break;
}
}
if (preorderedList != null) preorderedList.clear();
}
//沒有找到要接受事件的View
if (newTouchTarget == null && mFirstTouchTarget != null) {
// Did not find a child to receive the event.
// Assign the pointer to the least recently added target.
newTouchTarget = mFirstTouchTarget;
while (newTouchTarget.next != null) {
newTouchTarget = newTouchTarget.next;
}
newTouchTarget.pointerIdBits |= idBitsToAssign;
}
}
}
//接下來就是對于非ACTION_DOWN事件的分發了,這里有兩種情況
if (mFirstTouchTarget == null) {
// 1.壓根就沒有找到要接受事件的view,或者被攔截了,呼叫了自身的dispatchTransformedTouchEvent()且穿了一個null的View進去,這樣有什么用呢?需要后面分析dispatchTransformedTouchEvent()
handled = dispatchTransformedTouchEvent(ev, canceled, null,
TouchTarget.ALL_POINTER_IDS);
} else {
//2.有View接受ACTION_DOWN事件,那么這個View也將接受其余的事件
TouchTarget predecessor = null;
TouchTarget target = mFirstTouchTarget;
while (target != null) {
final TouchTarget next = target.next;
if (alreadyDispatchedToNewTouchTarget && target == newTouchTarget) {
//alreadyDispatchedToNewTouchTarget這個變數在前面View接受ACTION_DOWN事件時設為了true
//同時這個mFirstTouchTarget也就是那個View封裝好的target
//那么這個回傳值handled就為true
handled = true;
} else {
//對于非ACTION_DOWN事件,依然是遞回呼叫dispatchTransformedTouchEvent
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;
}
}
predecessor = target;
target = next;
}
}
// 處理ACTION_UP和ACTION_CANCEL
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;
}

接下來看看dispatchTransformedTouchEvent()的原始碼:

//前面在分析dispatchTouchEvent()的時候發現有多處呼叫了這個dispatchTransformedTouchEvent(),而且有的地方傳來的第三個引數是null
private boolean dispatchTransformedTouchEvent(MotionEvent event, boolean cancel,
View child, int desiredPointerIdBits) {
final boolean handled;
//處理ACTION_CANCEL
final int oldAction = event.getAction();
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 (newPointerIdBits == oldPointerIdBits) {
if (child == null || child.hasIdentityMatrix()) {
if (child == null) {
//如果傳來的引數child為空時,呼叫自身dispatchTouchEvent()
handled = super.dispatchTouchEvent(event);
} else {
//不為空,那么就呼叫他的dispatchTouchEvent()
handled = child.dispatchTouchEvent(event);
}
return handled;
}
} else {
//...
}
if (child == null) {
handled = super.dispatchTouchEvent(transformedEvent);
} else {
final float offsetX = mScrollX - child.mLeft;
final float offsetY = mScrollY - child.mTop;
transformedEvent.offsetLocation(offsetX, offsetY);
if (! child.hasIdentityMatrix()) {
transformedEvent.transform(child.getInverseMatrix());
}
handled = child.dispatchTouchEvent(transformedEvent);
}
// Done.
transformedEvent.recycle();
return handled;
}

上面是對dispatchTouchEvent()和dispatchTransformedTouchEvent()的分析,看起來有點亂,這里梳理一下:

  • 首先明確一點,事件分發是從ViewGroup的dispatchTouchEvent()開始的ViewGroup在遇到一個新的事件序列,即事件ACTION_DOWN時,開始遍歷自己的所有子View,找到需要接收到事件的View無論是否找到,都會呼叫dispatchTransformedTouchEvent()方法,區別在于如果找到了,那么在這個方法中傳入的是那個View,否則就是nulldispatchTransformedTouchEvent()方法中第三個引數child為空時,會呼叫父類的dispatchTouchEvent()方法,否則會呼叫那個child的dispatchTouchEvent()方法,總而言之,都會去呼叫View類的dispatchTouchEvent()方法,dispatchTransformedTouchEvent()方法是進行具體的事件分發,除了OnClick()等事件外,onTouchEvent()方法就是在這里呼叫的只要找到了要接受事件的View,就會將他封裝為一個target,保存起來,后續的其他事件都由他來接受

問題三:OnClick OnLongClick等對外的監聽是在哪里處理的?

首先想一想一個很簡單的邏輯,OnClick事件是先ACTION_DOWN之后再ACTION_UP,所以必定要在onTouchEvent()處理,同理,OnLongClick是在保持ACTION_DOWN一段時間后發生,因此也要在onTouchEvent()中處理,看看原始碼,發現果然是在這里:

//以下原始碼均為忽略了不想關部分,只保留了重點
public boolean onTouchEvent(MotionEvent event) {
//...
if (((viewFlags & CLICKABLE) == CLICKABLE ||
(viewFlags & LONG_CLICKABLE) == LONG_CLICKABLE) ||
(viewFlags & CONTEXT_CLICKABLE) == CONTEXT_CLICKABLE) {
switch (action) {
case MotionEvent.ACTION_UP:
if (!mHasPerformedLongPress && !mIgnoreNextUpEvent) {
// 處理click
if (!focusTaken) {
if (mPerformClick == null) {
mPerformClick = new PerformClick();
}
if (!post(mPerformClick)) {
performClick();
}
}
}
}
break;
case MotionEvent.ACTION_DOWN:
// a short period in case this is a scroll.
if (isInScrollingContainer) {
//...
} else {
// 處理longclick
setPressed(true, x, y);
checkForLongClick(0, x, y);
}
break;
case MotionEvent.ACTION_CANCEL:
setPressed(false);
//...
mIgnoreNextUpEvent = false;
break;
case MotionEvent.ACTION_MOVE:
//...
break;
}
return true;
}
return false;
}

根據前面的分析,在View的dispatchTouchEvent()方法中,會對

public boolean dispatchTouchEvent(MotionEvent event) {
//...
boolean result = false;
if (mInputEventConsistencyVerifier != null) {
mInputEventConsistencyVerifier.onTouchEvent(event, 0);
}
//...
if (onFilterTouchEventForSecurity(event)) {
if ((mViewFlags & ENABLED_MASK) == ENABLED && handleScrollBarDragging(event)) {
result = true;
}
//只要獲取到的ListenerInfo不為空,就說明我們設定了監聽,那么就會認為我們想讓這個View處理所有事件
ListenerInfo li = mListenerInfo;
if (li != null && li.mOnTouchListener != null
&& (mViewFlags & ENABLED_MASK) == ENABLED
&& li.mOnTouchListener.onTouch(this, event)) {//所以會在這里執行onTouch()
result = true;
}
//而如果沒有處理,那么再呼叫onTouchEvent(),直到onTouchEvent()也回傳false才會認為該View不消費事件
if (!result && onTouchEvent(event)) {
result = true;
}
}
return result;
}

可以看到,在View的dispatchTouchEvent()方法中,會通過查看是否由設定監聽器等方法來判斷是否要消費事件,onTouchEvent()方法永遠會呼叫,click和longclick都在這里面,而無論內部如何處理,只要回傳了true,就會認為消費了這一事件,

分析就到這了,作為一個小菜雞,分析程序難免有些錯誤和疏漏,歡迎在評論區討論~

轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/67497.html

標籤:其他

上一篇:請問ucos iii 有do178c認證嗎?

下一篇:Cocos2d-lua物理引擎

標籤雲
其他(157675) Python(38076) JavaScript(25376) Java(17977) C(15215) 區塊鏈(8255) C#(7972) AI(7469) 爪哇(7425) MySQL(7132) html(6777) 基礎類(6313) sql(6102) 熊猫(6058) PHP(5869) 数组(5741) R(5409) Linux(5327) 反应(5209) 腳本語言(PerlPython)(5129) 非技術區(4971) Android(4554) 数据框(4311) css(4259) 节点.js(4032) C語言(3288) json(3245) 列表(3129) 扑(3119) C++語言(3117) 安卓(2998) 打字稿(2995) VBA(2789) Java相關(2746) 疑難問題(2699) 细绳(2522) 單片機工控(2479) iOS(2429) ASP.NET(2402) MongoDB(2323) 麻木的(2285) 正则表达式(2254) 字典(2211) 循环(2198) 迅速(2185) 擅长(2169) 镖(2155) 功能(1967) .NET技术(1958) Web開發(1951) python-3.x(1918) HtmlCss(1915) 弹簧靴(1913) C++(1909) xml(1889) PostgreSQL(1872) .NETCore(1853) 谷歌表格(1846) Unity3D(1843) for循环(1842)

熱門瀏覽
  • 網閘典型架構簡述

    網閘架構一般分為兩種:三主機的三系統架構網閘和雙主機的2+1架構網閘。 三主機架構分別為內端機、外端機和仲裁機。三機無論從軟體和硬體上均各自獨立。首先從硬體上來看,三機都用各自獨立的主板、記憶體及存盤設備。從軟體上來看,三機有各自獨立的作業系統。這樣能達到完全的三機獨立。對于“2+1”系統,“2”分為 ......

    uj5u.com 2020-09-10 02:00:44 more
  • 如何從xshell上傳檔案到centos linux虛擬機里

    如何從xshell上傳檔案到centos linux虛擬機里及:虛擬機CentOs下執行 yum -y install lrzsz命令,出現錯誤:鏡像無法找到軟體包 前言 一、安裝lrzsz步驟 二、上傳檔案 三、遇到的問題及解決方案 總結 前言 提示:其實很簡單,往虛擬機上安裝一個上傳檔案的工具 ......

    uj5u.com 2020-09-10 02:00:47 more
  • 一、SQLMAP入門

    一、SQLMAP入門 1、判斷是否存在注入 sqlmap.py -u 網址/id=1 id=1不可缺少。當注入點后面的引數大于兩個時。需要加雙引號, sqlmap.py -u "網址/id=1&uid=1" 2、判斷文本中的請求是否存在注入 從文本中加載http請求,SQLMAP可以從一個文本檔案中 ......

    uj5u.com 2020-09-10 02:00:50 more
  • Metasploit 簡單使用教程

    metasploit 簡單使用教程 浩先生, 2020-08-28 16:18:25 分類專欄: kail 網路安全 linux 文章標簽: linux資訊安全 編輯 著作權 metasploit 使用教程 前言 一、Metasploit是什么? 二、準備作業 三、具體步驟 前言 Msfconsole ......

    uj5u.com 2020-09-10 02:00:53 more
  • 游戲逆向之驅動層與用戶層通訊

    驅動層代碼: #pragma once #include <ntifs.h> #define add_code CTL_CODE(FILE_DEVICE_UNKNOWN,0x800,METHOD_BUFFERED,FILE_ANY_ACCESS) /* 更多游戲逆向視頻www.yxfzedu.com ......

    uj5u.com 2020-09-10 02:00:56 more
  • 北斗電力時鐘(北斗授時服務器)讓網路資料更精準

    北斗電力時鐘(北斗授時服務器)讓網路資料更精準 北斗電力時鐘(北斗授時服務器)讓網路資料更精準 京準電子科技官微——ahjzsz 近幾年,資訊技術的得了快速發展,互聯網在逐漸普及,其在人們生活和生產中都得到了廣泛應用,并且取得了不錯的應用效果。計算機網路資訊在電力系統中的應用,一方面使電力系統的運行 ......

    uj5u.com 2020-09-10 02:01:03 more
  • 【CTF】CTFHub 技能樹 彩蛋 writeup

    ?碎碎念 CTFHub:https://www.ctfhub.com/ 筆者入門CTF時時剛開始刷的是bugku的舊平臺,后來才有了CTFHub。 感覺不論是網頁UI設計,還是題目質量,賽事跟蹤,工具軟體都做得很不錯。 而且因為獨到的金幣制度的確讓人有一種想去刷題賺金幣的感覺。 個人還是非常喜歡這個 ......

    uj5u.com 2020-09-10 02:04:05 more
  • 02windows基礎操作

    我學到了一下幾點 Windows系統目錄結構與滲透的作用 常見Windows的服務詳解 Windows埠詳解 常用的Windows注冊表詳解 hacker DOS命令詳解(net user / type /md /rd/ dir /cd /net use copy、批處理 等) 利用dos命令制作 ......

    uj5u.com 2020-09-10 02:04:18 more
  • 03.Linux基礎操作

    我學到了以下幾點 01Linux系統介紹02系統安裝,密碼啊破解03Linux常用命令04LAMP 01LINUX windows: win03 8 12 16 19 配置不繁瑣 Linux:redhat,centos(紅帽社區版),Ubuntu server,suse unix:金融機構,證券,銀 ......

    uj5u.com 2020-09-10 02:04:30 more
  • 05HTML

    01HTML介紹 02頭部標簽講解03基礎標簽講解04表單標簽講解 HTML前段語言 js1.了解代碼2.根據代碼 懂得挖掘漏洞 (POST注入/XSS漏洞上傳)3.黑帽seo 白帽seo 客戶網站被黑帽植入劫持代碼如何處理4.熟悉html表單 <html><head><title>TDK標題,描述 ......

    uj5u.com 2020-09-10 02:04:36 more
最新发布
  • 2023年最新微信小程式抓包教程

    01 開門見山 隔一個月發一篇文章,不過分。 首先回顧一下《微信系結手機號資料庫被脫庫事件》,我也是第一時間得知了這個訊息,然后跟蹤了整件事情的經過。下面是這起事件的相關截圖以及近日流出的一萬條資料樣本: 個人認為這件事也沒什么,還不如關注一下之前45億快遞資料查詢渠道疑似在近日復活的訊息。 訊息是 ......

    uj5u.com 2023-04-20 08:48:24 more
  • web3 產品介紹:metamask 錢包 使用最多的瀏覽器插件錢包

    Metamask錢包是一種基于區塊鏈技術的數字貨幣錢包,它允許用戶在安全、便捷的環境下管理自己的加密資產。Metamask錢包是以太坊生態系統中最流行的錢包之一,它具有易于使用、安全性高和功能強大等優點。 本文將詳細介紹Metamask錢包的功能和使用方法。 一、 Metamask錢包的功能 數字資 ......

    uj5u.com 2023-04-20 08:47:46 more
  • vulnhub_Earth

    前言 靶機地址->>>vulnhub_Earth 攻擊機ip:192.168.20.121 靶機ip:192.168.20.122 參考文章 https://www.cnblogs.com/Jing-X/archive/2022/04/03/16097695.html https://www.cnb ......

    uj5u.com 2023-04-20 07:46:20 more
  • 從4k到42k,軟體測驗工程師的漲薪史,給我看哭了

    清明節一過,盲猜大家已經無心上班,在數著日子準備過五一,但一想到銀行卡里的余額……瞬間心情就不美麗了。最近,2023年高校畢業生就業調查顯示,本科畢業月平均起薪為5825元。調查一出,便有很多同學表示自己又被平均了。看著這一資料,不免讓人想到前不久中國青年報的一項調查:近六成大學生認為畢業10年內會 ......

    uj5u.com 2023-04-20 07:44:00 more
  • 最新版本 Stable Diffusion 開源 AI 繪畫工具之中文自動提詞篇

    🎈 標簽生成器 由于輸入正向提示詞 prompt 和反向提示詞 negative prompt 都是使用英文,所以對學習母語的我們非常不友好 使用網址:https://tinygeeker.github.io/p/ai-prompt-generator 這個網址是為了讓大家在使用 AI 繪畫的時候 ......

    uj5u.com 2023-04-20 07:43:36 more
  • 漫談前端自動化測驗演進之路及測驗工具分析

    隨著前端技術的不斷發展和應用程式的日益復雜,前端自動化測驗也在不斷演進。隨著 Web 應用程式變得越來越復雜,自動化測驗的需求也越來越高。如今,自動化測驗已經成為 Web 應用程式開發程序中不可或缺的一部分,它們可以幫助開發人員更快地發現和修復錯誤,提高應用程式的性能和可靠性。 ......

    uj5u.com 2023-04-20 07:43:16 more
  • CANN開發實踐:4個DVPP記憶體問題的典型案例解讀

    摘要:由于DVPP媒體資料處理功能對存放輸入、輸出資料的記憶體有更高的要求(例如,記憶體首地址128位元組對齊),因此需呼叫專用的記憶體申請介面,那么本期就分享幾個關于DVPP記憶體問題的典型案例,并給出原因分析及解決方法。 本文分享自華為云社區《FAQ_DVPP記憶體問題案例》,作者:昇騰CANN。 DVPP ......

    uj5u.com 2023-04-20 07:43:03 more
  • msf學習

    msf學習 以kali自帶的msf為例 一、msf核心模塊與功能 msf模塊都放在/usr/share/metasploit-framework/modules目錄下 1、auxiliary 輔助模塊,輔助滲透(埠掃描、登錄密碼爆破、漏洞驗證等) 2、encoders 編碼器模塊,主要包含各種編碼 ......

    uj5u.com 2023-04-20 07:42:59 more
  • Halcon軟體安裝與界面簡介

    1. 下載Halcon17版本到到本地 2. 雙擊安裝包后 3. 步驟如下 1.2 Halcon軟體安裝 界面分為四大塊 1. Halcon的五個助手 1) 影像采集助手:與相機連接,設定相機引數,采集影像 2) 標定助手:九點標定或是其它的標定,生成標定檔案及內參外參,可以將像素單位轉換為長度單位 ......

    uj5u.com 2023-04-20 07:42:17 more
  • 在MacOS下使用Unity3D開發游戲

    第一次發博客,先發一下我的游戲開發環境吧。 去年2月份買了一臺MacBookPro2021 M1pro(以下簡稱mbp),這一年來一直在用mbp開發游戲。我大致分享一下我的開發工具以及使用體驗。 1、Unity 官網鏈接: https://unity.cn/releases 我一般使用的Apple ......

    uj5u.com 2023-04-20 07:40:19 more