Android-Handler學習總結

??面試官?:你好!我看你簡歷里提到熟悉 Android 的 Handler 機制,能簡單說一下它的作用嗎?

?候選人?:
????????Handler 是 Android 中用來做線程間通信的工具。比如Android 應用的 UI 線程(也叫主線程)是非常繁忙的,它負責處理用戶的交互、繪制界面等等。如果我們直接在其他子線程(比如網絡請求線程、文件讀寫線程)里更新 UI,程序就會崩潰,因為 Android 不允許非 UI 線程直接操作 UI 組件。這時候 Handler 就派上用場了。簡單來說,它可以做到:

  1. 將子線程中需要更新 UI 的操作,發送到主線程的消息隊列中去排隊。
  2. 主線程通過 Looper 不斷地從這個消息隊列中取出消息,然后交給 Handler 自己來處理。
  3. Handler 在收到消息后,就可以安全地在主線程中更新 UI 了。

????????舉個實際例子吧:主線程啟動時,系統會自動創建一個 Looper 和消息隊列,所以主線程的 Handler 可以直接用。但如果是子線程,得手動調用?Looper.prepare()?和?Looper.loop(),否則會報錯——就像你去餐廳吃飯,主線程是服務員已經站在桌邊等你點菜,子線程得自己喊服務員過來。


?面試官?:嗯,那主線程為什么可以直接用 Handler?子線程用的時候要注意什么?

?候選人?:

????????關于主線程為什么可以直接用 Handler:

????????這是因為 Android 應用在啟動的時候,系統就已經為?主線程在啟動時,系統已經幫我們初始化了 Looper(比如在 ActivityThread.main() 里調用了 Looper.prepareMainLooper()loop()),并且調用了 Looper.loop() 方法。這個 Looper 會自動為主線程維護一個消息隊列 (MessageQueue)。所以,當我們在主線程中創建 Handler 實例時,它默認就會關聯到主線程的 Looper 和 MessageQueue,不需要我們再額外做什么特殊處理。我們直接 new Handler() 就可以了,它自然就能和主線程的Looper配合工作。

????????關于子線程用 Handler 的時候要注意什么:

????????子線程默認情況下是沒有 Looper 的,因此也就沒有消息隊列。如果想在子線程中使用 Handler 來處理消息(比如子線程之間通信,或者讓子線程自己處理一些定時任務),就需要我們手動為這個子線程創建和啟動 Looper。具體來說,有以下幾個關鍵點需要注意:

  1. 創建 Looper: 在子線程的 run() 方法中,首先需要調用 Looper.prepare()。這個方法會為當前線程創建一個 Looper 對象,并將其保存在一個 ThreadLocal 變量中,同時也會創建一個 MessageQueue。
  2. 創建 Handler: 在調用了 Looper.prepare() 之后,我們就可以在這個子線程中創建 Handler 實例了。這個 Handler 會自動關聯到剛剛創建的 Looper。
  3. 啟動消息循環: 創建完 Handler 之后,非常重要的一步是調用 Looper.loop()。這個方法會開啟一個無限循環,不斷地從 MessageQueue 中取出消息,并分發給對應的 Handler 處理。如果忘記調用 Looper.loop(),那么發送到這個子線程 Handler 的消息將永遠得不到處理。
  4. 退出 Looper(如果需要): Looper.loop() 是一個死循環,會阻塞線程。如果子線程的任務執行完畢后不再需要處理消息,或者希望線程能夠正常結束,就需要調用 Looper 的 quit()quitSafely() 方法來停止消息循環,從而讓線程能夠退出。否則,這個子線程會一直處于等待消息的狀態,無法被回收,可能會導致資源浪費。
    • quit():會立即清空消息隊列中所有消息(包括未處理和延遲消息),然后退出 Looper。
    • quitSafely():則會處理完消息隊列中已有的消息后,再安全退出 Looper,不會處理新的消息。通常推薦使用 quitSafely(),因為它更加安全。

????????總結一下就是,主線程天生就有 Looper,可以直接用 Handler。子線程想用 Handler,就必須自己動手 Looper.prepare()、創建 Handler、然后 Looper.loop(),并且在不需要的時候記得 Looper.quit()quitSafely() 來釋放資源。

????????我平時在項目中如果遇到需要在子線程處理消息的情況,通常會優先考慮使用 HandlerThreadHandlerThread 是 Android 提供的一個封裝好的類,它繼承自 Thread,并且內部已經幫我們處理了 Looper.prepare()Looper.loop() 的邏輯,使用起來會更方便一些,也減少了出錯的可能。


?面試官?:如果子線程不調用 Looper.loop() 會怎么樣?

?候選人?:
????????線程會直接結束,Handler 收不到任何消息。loop() 方法內部是個死循環,但不用擔心卡死,因為沒消息時會通過 Linux 的 ?epoll 機制? 休眠,有消息時再喚醒。比如主線程的 Looper 雖然一直循環,但沒消息時 CPU 占用幾乎是 0。

????????那子線程的 Handler 就收不到消息了。比如我寫了個子線程的 Handler,但忘記調?loop(),結果發送的消息石沉大海,日志里還會拋異常。不過不用擔心死循環卡死線程,因為 Looper 內部用了 Linux 的 ?epoll 機制,沒消息時會休眠,有消息才喚醒——就像你晚上睡覺,手機靜音了,但有人打電話進來會立刻震醒你。


?面試官?:提到消息隊列,Handler 的 postDelayed() 能保證準時執行嗎?

?候選人?:

????????不一定準!比如我設置了 5 秒后彈 Toast,但如果手機休眠了 3 秒,實際可能要 8 秒后才執行。因為?postDelayed?用的是系統非休眠時間(SystemClock.uptimeMillis()),休眠時間不算在內。另外,如果主線程前面有耗時操作,比如解析大文件,后面的消息都得排隊等著——就像堵車時,你就算預約了時間,也可能遲到。


?面試官?:那如果子線程發消息到主線程,什么時候切換到主線程執行?

?候選人?:
????????子線程發消息時,消息會被加到主線程的 MessageQueue。此時子線程的任務就結束了,主線程的 Looper 會在下次循環取到這個消息,并在主線程執行 handleMessage()。整個過程 ?沒有顯式的線程切換,只是消息被不同線程的 Looper 處理了。


?面試官?:Handler 導致內存泄漏遇到過嗎?怎么解決?

?候選人?:
????????遇到過!比如在 Activity 里直接寫 Handler handler = new Handler() { ... },這個 Handler 會隱式持有 Activity 的引用。如果 Activity 銷毀時 Handler 還有未處理的消息,就會導致 Activity 無法被回收。
解決辦法有兩種:

  1. ?靜態內部類 + 弱引用?:
    static class SafeHandler extends Handler {private WeakReference<Activity> mActivity;SafeHandler(Activity activity) {mActivity = new WeakReference<>(activity);}@Overridepublic void handleMessage(Message msg) {Activity activity = mActivity.get();if (activity != null) { /* 處理消息 */ }}
    }
  2. ?onDestroy() 移除所有消息?:
    @Override
    protected void onDestroy() {super.onDestroy();handler.removeCallbacksAndMessages(null);
    }

?面試官?:如果讓你設計一個定時任務,每隔 1 秒更新 UI,用 Handler 怎么實現?

?候選人?:
可以用 postDelayed() 遞歸調用。比如:

private void scheduleUpdate() {handler.postDelayed(new Runnable() {@Overridepublic void run() {updateUI(); // 更新 UIscheduleUpdate(); // 再次調用自己,形成循環}}, 1000); // 延遲 1 秒
}

但要注意在頁面銷毀時移除回調,否則 Runnable 會一直持有 Activity 導致泄漏。

但一定要注意在頁面銷毀時移除回調,不然就算頁面關了,Runnable 還在后臺跑——就像你出門忘了關燈,電費白白浪費。


?面試官?:最后一個問題,知道什么是 ?同步屏障(Sync Barrier)?? 嗎?

?候選人?:
????????同步屏障是 MessageQueue 里的一種特殊消息(target 為 null),用來阻塞同步消息,優先處理異步消息。比如系統在繪制 UI 時,會插入同步屏障,保證 Choreographer 的渲染消息優先執行。
代碼里可以通過 MessageQueue.postSyncBarrier() 插入屏障,處理完后再調用 removeSyncBarrier() 移除。


?ThreadLocal 在 Handler 機制中的作用?

?1. ThreadLocal 的角色:線程專屬的“儲物柜”??
  • ?核心作用?:
    ThreadLocal 是每個線程的“私人儲物柜”,用來保存線程獨有的數據。在 Handler 機制中,每個線程的 ?Looper? 就是通過 ThreadLocal 存儲的,確保線程隔離。
    舉個例子:主線程和子線程各自有一個“儲物柜”,主線程的柜子里放著主線程的 Looper,子線程的柜子放自己的 Looper,互不干擾。

  • ?源碼驗證?:

    public final class Looper {// ThreadLocal 存儲每個線程的 Looperstatic final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<>();// 初始化當前線程的 Looperpublic static void prepare() {if (sThreadLocal.get() != null) {throw new RuntimeException("Only one Looper per thread!");}sThreadLocal.set(new Looper(false)); // 存入當前線程的儲物柜}// 獲取當前線程的 Looperpublic static @Nullable Looper myLooper() {return sThreadLocal.get(); // 從儲物柜取出}
    }
  • ?面試回答?:
    “ThreadLocal 就像每個線程的專屬儲物柜。比如主線程啟動時,系統自動在它的柜子里放了一個 Looper,所以主線程的 Handler 可以直接用。但子線程的柜子一開始是空的,必須手動調用 Looper.prepare() 放一個 Looper 進去,否則創建 Handler 時會報錯。”


?2. 為什么每個線程只能有一個 Looper???
  • ?設計約束?:
    Android 規定一個線程只能有一個 Looper,避免多個消息循環競爭資源。ThreadLocal 的 set() 方法會檢查是否已有 Looper,重復創建直接拋異常。

  • ?源碼佐證?:

    private static void prepare(boolean quitAllowed) {if (sThreadLocal.get() != null) { // 如果柜子里已經有 Looperthrow new RuntimeException("Only one Looper may be created per thread");}sThreadLocal.set(new Looper(quitAllowed)); // 第一次放進去
    }
  • ?面試回答?:
    “這就好比一個線程只能有一個‘消息管家’(Looper)。ThreadLocal 的 prepare() 方法會檢查柜子是否已經有管家,如果有就直接報錯。這種設計防止了多個管家搶活干,導致消息處理混亂。”


?Handler 與 Choreographer 的關系?

?1. Choreographer 如何利用 Handler???
  • ?核心機制?:
    Choreographer 負責協調 UI 繪制和 VSYNC 信號,它內部通過 Handler 發送異步消息,并插入同步屏障,確保繪制任務優先執行。

  • ?源碼解析?:

    // Choreographer 內部使用 Handler 發送異步消息
    private final class FrameHandler extends Handler {public FrameHandler(Looper looper) {super(looper);}@Overridepublic void handleMessage(Message msg) {switch (msg.what) {case MSG_DO_FRAME:doFrame(System.nanoTime(), 0); // 處理繪制任務break;// 其他消息處理...}}
    }// 發送異步消息(帶同步屏障)
    private void postFrameCallback() {// 插入同步屏障,阻塞普通消息mHandler.postMessageAtTime(Message.obtain(mHandler, MSG_DO_FRAME), delay);msg.setAsynchronous(true); // 標記為異步消息
    }
  • ?面試回答?:
    “Choreographer 就像一個交通指揮員,負責在 VSYNC 信號到來時觸發 UI 繪制。它內部通過 Handler 發送一個異步消息(類似‘緊急任務’),并插入同步屏障,讓普通消息靠邊站。這樣繪制任務就能插隊優先執行,避免掉幀。”


?2. 同步屏障與異步消息的作用?
  • ?同步屏障?:
    一種特殊消息(target=null),阻塞后續同步消息,只允許異步消息執行。
    ?應用場景?:UI 繪制、動畫等需要高優先級的任務。

  • ?源碼驗證?:

    // MessageQueue 處理同步屏障
    Message msg = mMessages;
    if (msg != null && msg.target == null) { // 遇到同步屏障do {prevMsg = msg;msg = msg.next;} while (msg != null && !msg.isAsynchronous()); // 尋找下一個異步消息
    }
  • ?面試回答?:
    “同步屏障就像地鐵里的‘緊急通道’,普通消息(同步消息)被攔住,只有異步消息(比如 UI 繪制)能通過。這樣系統能優先處理關鍵任務,比如保證 60fps 的流暢度。”


?3. 為什么 UI 刷新不直接用普通 Handler???
  • ?性能優化?:
    直接使用普通 Handler 可能導致繪制任務被其他消息阻塞。通過同步屏障和異步消息,Choreographer 確保繪制任務在 VSYNC 信號到來時立即執行。

  • ?實際案例?:
    當用戶滑動列表時,Choreographer 在下一個 VSYNC 周期觸發繪制,避免中途被其他消息(如網絡回調)打斷,從而減少卡頓。

  • ?面試回答?:
    “如果直接用一個普通 Handler 處理 UI 刷新,可能有其他消息(比如數據加載)堵在前面,導致繪制延遲。而 Choreographer 通過同步屏障和異步消息,讓繪制任務‘插隊’,確保在 16ms 內完成,避免掉幀。”


?總結回答(自然口語化)??

“ThreadLocal 就像線程的私人儲物柜,保證每個線程的 Looper 獨立。比如主線程的柜子自動放了 Looper,子線程需要手動準備。而 Choreographer 是 UI 流暢的關鍵,它用 Handler 發送異步消息,并通過同步屏障讓繪制任務優先執行,就像給緊急任務開綠燈。這種機制確保動畫和滑動不會卡頓,是 Android 流暢 UI 的基石。”??


?面試官?:我看你項目里用了 Handler,能說說為什么 Handler 會導致內存泄漏嗎?具體是怎么發生的?

?候選人?:
當然可以。Handler 的內存泄漏主要發生在 ?非靜態內部類 + 延遲消息? 的場景。比如在 Activity 里直接寫:

public class MainActivity extends AppCompatActivity {private Handler mHandler = new Handler() {@Overridepublic void handleMessage(Message msg) {// 更新 UI}};@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);mHandler.postDelayed(() -> {// 延遲 10 秒執行任務}, 10_000);}
}

?問題核心?:
當用戶旋轉屏幕導致 Activity 銷毀時,如果延遲消息尚未執行,這條消息會通過 Message → Handler → Activity 的引用鏈,阻止 Activity 被回收。
就像你借了別人的充電寶(Activity),但對方(Handler)還沒用完(消息未處理),充電寶就一直沒法歸還(內存泄漏)。


?面試官?:那具體是怎么形成引用鏈的?能不能從源碼層面解釋?

?候選人?:
沒問題,我們從源碼看泄漏鏈路:

  1. ?Message 持有 Handler?:

    // Message 類
    public class Message implements Parcelable {Handler target;  // 發送消息的 Handler
    }

    當調用 handler.sendMessage(msg) 時,msg.target 被賦值為當前 Handler。

  2. ?非靜態 Handler 持有 Activity?:
    非靜態內部類 Handler 隱式持有外部 Activity 的引用(類似 MainActivity.this)。

  3. ?MessageQueue 持有 Message?:

    // MessageQueue 內部維護消息鏈表
    Message mMessages;  // 鏈表頭節點

?引用鏈?:
MessageQueue → Message → Handler → Activity
即使 Activity 銷毀,只要消息還在隊列中,這條鏈就會阻止 GC 回收 Activity。


?面試官?:你們項目里是怎么解決這個問題的?有實際案例嗎?

?候選人?:
我們項目里用 ?靜態內部類 + 弱引用 + 生命周期管理? 三管齊下。舉個實際場景:

?場景?:在直播間發送彈幕,需要 Handler 定時刷新 UI,同時處理禮物動畫回調。

?解決方案?:

  1. ?靜態 Handler + 弱引用?:

    private static class SafeHandler extends Handler {private final WeakReference<LiveRoomActivity> activityRef;public SafeHandler(LiveRoomActivity activity) {activityRef = new WeakReference<>(activity);}@Overridepublic void handleMessage(Message msg) {LiveRoomActivity activity = activityRef.get();if (activity == null || activity.isDestroyed()) return;switch (msg.what) {case MSG_UPDATE_DANMU:activity.updateDanmuList();break;case MSG_SHOW_GIFT:activity.playGiftAnimation();break;}}
    }
  2. ?生命周期管理?:

    @Override
    protected void onDestroy() {super.onDestroy();// 移除所有消息,徹底斷掉引用鏈mHandler.removeCallbacksAndMessages(null);
    }
  3. ?消息復用優化?:

    // 復用消息對象,避免頻繁創建
    Message msg = Message.obtain();
    msg.what = MSG_UPDATE_DANMU;
    mHandler.sendMessageDelayed(msg, 1000);

?效果?:直播間頻繁進出測試中,內存泄漏率降為 0,ANR 減少 30%。


?面試官?:如果不用弱引用,直接在 onDestroy 移除消息能解決問題嗎?

?候選人?:
可以,但不完全可靠。比如以下場景:

  1. ?異步回調延遲?:
    網絡請求在 onDestroy 之后才返回,回調中調用 Handler 發送消息,此時 Handler 可能已經銷毀,導致空指針。

  2. ?多線程競爭?:
    如果子線程在 onDestroy 執行過程中發送消息,可能漏刪消息。

?最佳實踐?:
?雙重保險——弱引用防止意外持有,onDestroy 移除消息確保徹底清理。就像出門時既鎖門(移除消息)又帶鑰匙(弱引用),雙重保障。


?面試官?:有沒有遇到過 Handler 導致的內存泄漏很難排查?怎么解決的?

?候選人?:
確實遇到過。有一次線上報 OOM,但 LeakCanary 沒抓到明顯泄漏。后來用 ?Android Profiler + 代碼審查? 才定位到問題。

?排查過程?:

  1. ?Profiler 抓堆轉儲?:
    發現 Activity 實例數量異常,存活時間遠超生命周期。

  2. ?分析引用鏈?:
    發現某個 Message 持有自定義 Handler 子類,而 Handler 持有 Activity。

  3. ?代碼審查?:
    發現同事寫了一個 ?匿名 Handler 子類,在自定義 View 中發送延遲消息,但未及時移除。

?修復方案?:

  • 將匿名 Handler 改為靜態內部類 + 弱引用;
  • 在 View 的 onDetachedFromWindow 中移除消息。

?教訓?:
匿名內部類 Handler 是隱藏殺手,必須強制代碼規范審查。


?面試官?:如果用 Kotlin 協程或 LiveData 替代 Handler,能完全避免泄漏嗎?

?候選人?:
大部分情況可以,但需要正確使用。比如:

?協程方案?:

// 在 ViewModel 中啟動協程
viewModelScope.launch {val data = withContext(Dispatchers.IO) { fetchData() } // 子線程執行_uiState.value = data // 主線程更新
}

?LiveData 方案?:

public class MyViewModel extends ViewModel {private MutableLiveData<String> data = new MutableLiveData<>();void loadData() {Executors.io().execute(() -> {String result = fetchData();data.postValue(result); // 自動切主線程});}
}

?優勢?:

  • 自動綁定生命周期,Activity 銷毀時自動取消訂閱;
  • 無需手動管理消息隊列,代碼更簡潔。

?注意點?:
如果協程或 LiveData 持有 Context 引用(如誤用 requireContext()),仍可能泄漏。所以關鍵還是遵循 ?生命周期感知? 原則。

?大廠高頻追問答案?:

  • ?問:為什么匿名 Runnable 也會導致泄漏???
    ??:匿名 Runnable 是匿名內部類,隱式持有外部類(如 Activity)引用。如果通過 postDelayed 發送,消息會持有 Runnable → Activity 的引用鏈。

  • ?問:主線程的 Looper 為什么不會泄漏???
    ??:主線程的 Looper 生命周期和進程一致,不需要回收。而子線程的 Looper 必須手動 quit(),否則線程無法結束,導致 Handler 持續持有引用。

  • ?問:如何檢測 MessageQueue 中的殘留消息???
    ??:通過反射獲取 MessageQueue.mMessages 鏈表,遍歷檢查是否有未處理的 Message 指向目標 Handler。?


?面試官?:聽說你在項目里用過 Handler,能聊聊它的工作原理嗎?比如 post()postDelayed() 有什么區別?

?候選人?:
當然可以!其實 post()postDelayed() 骨子里是同一個方法,就像雙胞胎兄弟。比如 post() 底層調用的是 sendMessageDelayed(..., 0),而 postDelayed() 只是多傳了個延遲時間參數。

舉個例子吧:

// 這兩個調用本質上是一樣的
handler.post(() -> updateUI());  
handler.postDelayed(() -> updateUI(), 0); // 效果和 post() 一樣

但細節上有點差別——post() 會直接把消息塞到隊列頭部,而 postDelayed(0) 是按時間排序插入。如果隊列里已經有消息在排隊,postDelayed(0) 的消息可能得等前面的處理完才能執行。


?面試官?:聽起來像是延遲時間的問題。那如果我在 Activity 里用 postDelayed() 發了個 10 分鐘的延遲任務,退出 Activity 后會有問題嗎?

?候選人?:
這問題我們踩過坑!如果 Handler 是非靜態內部類,消息會一直抓著 Activity 不放,就像有人借了你的充電寶(Activity)不還,結果你手機(內存)直接沒電(OOM)。

?具體原因?:
消息隊列(MessageQueue)里那個延遲 10 分鐘的任務還沒執行,而消息 → Handler → Activity 這條鏈子會一直存在。就算用戶退出了 Activity,這條鏈子也會讓 Activity 卡在內存里沒法回收。


?面試官?:那你們是怎么解決的?總不能不用 Handler 了吧?

?候選人?:
我們用了 ?三重防御?:

  1. ?靜態內部類?:把 Handler 變成“工具人”,不跟 Activity 綁定;
  2. ?弱引用?:加個橡皮筋(WeakReference),Activity 被回收時自動松手;
  3. ?生命周期管理?:在 onDestroy() 里清空消息隊列,就像退房前關水電。

比如這樣改代碼:

private static class SafeHandler extends Handler {private WeakReference<Activity> weakActivity; // 橡皮筋綁著 Activity@Overridepublic void handleMessage(Message msg) {Activity activity = weakActivity.get();if (activity == null) return; // 發現 Activity 沒了就收工// 安全干活...}
}// Activity 銷毀時徹底清理
@Override
protected void onDestroy() {super.onDestroy();handler.removeCallbacksAndMessages(null); // 把消息隊列全清空
}

?面試官?:如果不用弱引用,只在 onDestroy 移除消息夠嗎?

?候選人?:
不夠穩!我們有個血的教訓:同事在 onDestroy 里漏刪了一條消息,結果用戶快速進出頁面 10 次后直接閃退。后來用 ?LeakCanary? 一查,發現 8 個 Activity 尸體躺在內存里!

?排查過程?:

  1. LeakCanary 顯示引用鏈是 Message → Handler → Activity
  2. 發現是某個網絡回調在 Activity 銷毀后調了 Handler;
  3. 最后在基類 Activity 的 onDestroy 加了個全局清空消息的邏輯,一勞永逸。

?面試官?:如果讓你設計一個圖片下載庫,用 HandlerThread 還是線程池?

?候選人?:
果斷選線程池!比如這樣設計:

// 開個 4 線程的池子,并發下載
ExecutorService pool = Executors.newFixedThreadPool(4); pool.execute(() -> {Bitmap bitmap = downloadImage(url); // 子線程下載runOnUiThread(() -> imageView.setImageBitmap(bitmap)); // 切主線程更新
});

?理由?:

  • 線程池能并發處理多張圖片,速度比單線程的 HandlerThread 快多了;
  • 可以控制最大線程數(比如 4 個),避免手機 CPU 被吃滿;
  • 復用線程資源,不像頻繁創建 Thread 那樣浪費內存。

Android面試總結之Handler 機制深入探討原理、應用與優化_android handler原理 面試-CSDN博客https://blog.csdn.net/2301_80329517/article/details/146558080

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

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

相關文章

【iOS】分類、擴展、關聯對象

分類、擴展、關聯對象 前言分類擴展擴展和分類的區別關聯對象key的幾種用法流程 總結 前言 最近的學習中筆者發現自己對于分類、擴展相關知識并不是很熟悉&#xff0c;剛好看源碼類的加載過程中發現有類擴展與關聯對象詳解。本篇我們來探索一下這部分相關知識&#xff0c;首先…

30.第二階段x64游戲實戰-認識網絡數據包發送流程

免責聲明&#xff1a;內容僅供學習參考&#xff0c;請合法利用知識&#xff0c;禁止進行違法犯罪活動&#xff01; 內容參考于&#xff1a;圖靈Python學院 上一個內容&#xff1a;29.第二階段x64游戲實戰-技能冷卻 發送數據包的方式&#xff08;函數&#xff09;操作系統提供…

【每日一題】【前綴和優化】【前/后綴最值】牛客練習賽139 B/C題 大衛的密碼 (Hard Version) C++

牛客練習賽139 B題 大衛的密碼 (Easy Version) 牛客練習賽139 C題 大衛的密碼 (Hard Version) 大衛的密碼 題目背景 牛客練習賽139 題目描述 給定一個 n m n\times m nm的網格圖&#xff0c;我們使用 ( i , j ) (i,j) (i,j)表示網格中從上往下數第 i i i行和從左往右數第…

文件夾圖像批處理教程

前言 因為經常對圖像要做數據清洗&#xff0c;又很費時間去重新寫一個&#xff0c;我一直在想能不能寫一個通用的腳本或者制作一個可視化的界面對文件夾圖像做批量的修改圖像大小、重命名、劃分數據訓練和驗證集等等。這里我先介紹一下我因為寫過的一些腳本&#xff0c;然后我…

【Unity實戰筆記】第二十四 · 使用 SMB+Animator 實現基礎戰斗系統

轉載請注明出處&#xff1a;&#x1f517;https://blog.csdn.net/weixin_44013533/article/details/146409453 作者&#xff1a;CSDN|Ringleader| 1 結構 1.1 狀態機 1.2 SMB 2 代碼實現 2.1 核心控制 Player_Base_SMB 繼承 StateMachineBehaviour &#xff0c;控制變量初始…

Python虛擬環境再PyCharm中自由切換使用方法

Python開發中的環境隔離是必不可少的步驟,通過使用虛擬環境可以有效地管理不同項目間的依賴,避免包沖突和環境污染。虛擬環境是Python官方提供的一種獨立運行環境,每個項目可以擁有自己單獨的環境,不同項目之間的環境互不影響。在日常開發中,結合PyCharm這樣強大的IDE進行…

大模型智能體入門掃盲——基于camel的概述

前言 本篇博客想帶讀者進行一個智能體入門掃盲&#xff0c;了解基礎知識&#xff0c;為什么用camel呢&#xff0c;因為小洛發現它們文檔對這種智能體的基本組件介紹得很全面深入。 基礎概念 agent 一個典型的agent智能體包含三個核心部分&#xff1a; 感知模塊&#xff1…

目標檢測 RT-DETR(2023)詳細解讀

文章目錄 主干網絡&#xff1a;Encoder&#xff1a;不確定性最小Query選擇Decoder網絡&#xff1a; 將DETR擴展到實時場景&#xff0c;提高了模型的檢測速度。網絡架構分為三部分組成&#xff1a;主干網絡、混合編碼器、帶有輔助預測頭的變換器編碼器。具體來說&#xff0c;先利…

DeepSeek 賦能數字農業:從智慧種植到產業升級的全鏈條革新

目錄 一、數字農業的現狀與挑戰二、DeepSeek 技術解析2.1 DeepSeek 的技術原理與優勢2.2 DeepSeek 在人工智能領域的地位與影響力 三、DeepSeek 在數字農業中的應用場景3.1 精準種植決策3.2 病蟲害監測與防治3.3 智能灌溉與施肥管理3.4 農產品質量追溯與品牌建設 四、DeepSeek …

<uniapp><vuex><狀態管理>在uniapp中,如何使用vuex實現數據共享與傳遞?

前言 本專欄是基于uniapp實現手機端各種小功能的程序&#xff0c;并且基于各種通訊協議如http、websocekt等&#xff0c;實現手機端作為客戶端&#xff08;或者是手持機、PDA等&#xff09;&#xff0c;與服務端進行數據通訊的實例開發。 發文平臺 CSDN 環境配置 系統&…

高速串行差分信號仿真分析及技術發展挑戰續

7.3 3.125Gbps 差分串行信號設計實例仿真分析 7.3.1 設計用例說明 介紹完 Cadence 系統本身所具有的高速差分信號的仿真分析功能之后&#xff0c;我們以一個實例來說明 3.125Gbps 以下的高速差分系統的仿真分析方法。 在網上下載的設計文件“Booksi_Demo_Allegro160_Finishe…

【Golang】部分語法格式和規則

1、時間字符串和時間戳的相互轉換 func main() {t1 : int64(1546926630) // 外部傳入的時間戳&#xff08;秒為單位&#xff09;&#xff0c;必須為int64類型t2 : "2019-01-08 13:50:30" // 外部傳入的時間字符串//時間轉換的模板&#xff0c;golang里面只能是 &quo…

第十六章:數據治理之數據架構:數據模型和數據流轉關系

本章我們說一下數據架構&#xff0c;說到數據架構&#xff0c;就很自然的想到企業架構、業務架構、軟件架構&#xff0c;因為個人并沒有對這些內容進行深入了解&#xff0c;所以這里不做比對是否有相似或者共通的地方&#xff0c;僅僅來說一下我理解的數據架構。 1、什么是架構…

Day126 | 靈神 | 二叉樹 | 層數最深的葉子結點的和

Day126 | 靈神 | 二叉樹 | 層數最深的葉子結點的和 1302.層數最深的葉子結點的和 1302. 層數最深葉子節點的和 - 力扣&#xff08;LeetCode&#xff09; 思路&#xff1a; 這道題用層序遍歷的思路比較好想&#xff0c;就把每層的都算一下&#xff0c;然后返回最后一層的和就…

PCIE 4.0 vs PCIE 5.0固態硬盤——區別、科普與選購場景全解析

隨著數字內容和高性能計算需求的爆發&#xff0c;固態硬盤&#xff08;SSD&#xff09;已成為PC、游戲主機和工作站不可或缺的核心硬件。面對市面上層出不窮的新一代SSD產品&#xff0c;大家最常見的一個疑惑&#xff1a;**PCIe 4.0和PCIe 5.0固態硬盤&#xff0c;到底有啥區別…

vue pinia 獨立維護,倉庫統一導出

它允許您跨組件/頁面共享狀態 持久化 安裝依賴pnpm i pinia-plugin-persistedstate 將插件添加到 pinia 實例上 pinia獨立維護 統一導出 import { createPinia } from pinia import piniaPluginPersistedstate from pinia-plugin-persistedstateconst pinia creat…

Dify源碼學習

文章目錄 1 大模型基本原理1.1 model_context_tokens、max_tokens和prompt_tokens1.1.1 三者之間的關系1.1.2 總結對比 2 Dify源代碼2.0 前后端代碼跑起來【0】準備開發環境【1】下載代碼【2】運行后端&#xff08;1&#xff09;Start the docker-compose stack&#xff08;2&a…

連接表、視圖和存儲過程

1. 視圖 1.1. 視圖的概念 視圖&#xff08;View&#xff09;&#xff1a;虛擬表&#xff0c;本身不存儲數據&#xff0c;而是封裝了一個 SQL 查詢的結果集。 用途&#xff1a; 只顯示部分數據&#xff0c;提高數據訪問的安全性。簡化復雜查詢&#xff0c;提高復用性和可維護…

微信小程序中,解決lottie動畫在真機不顯示的問題

api部分 export function getRainInfo() {return onlineRequest({url: /ball/recruit/getRainInfo,method: get}); }data存儲json數據 data&#xff1a;{rainJson:{} }onLoad方法獲取json數據 onLoad(options) {let that thisgetRainInfo().then((res)>{that.setData({r…

從加密到信任|密碼重塑車路云一體化安全生態

目錄 一、密碼技術的核心支撐 二、典型應用案例 三、未來發展方向 總結 車路云系統涉及海量實時數據交互&#xff0c;包括車輛位置、傳感器信息、用戶身份等敏感數據。其安全風險呈現三大特征&#xff1a; 開放環境威脅&#xff1a;V2X&#xff08;車與萬物互聯&#xff0…