Android 效能最佳化:多線程注

來源:互聯網
上載者:User

標籤:為什麼   添加   串列   隱式   ddc   回收   多線程   位置   選擇   

    • 前言
    • Android 開發中多線程的必要性
    • 理解 Android 多線程
      • MessageQueue
      • Looper
      • Handler
      • HandlerThread
    • Android 中為什麼只允許在主線程更新 UI
      • Android 系統為了避免過度複雜的安全執行緒問題特地規定只允許在主線程中更新 UI
      • 而開發人員為了避免上述問題需要注意的是
    • 總結
    • Thanks

前言

Android Performance Patterns Season 5 主要介紹了 Android 多線程環境下的效能問題。通過介紹 Android 提供的多種多線程工具類 (AsyncTask, HandlerThread, IntentService, ThreadPool),讓我們熟悉各個組件的適用情境,從而在特定情境下選擇效能最好的一個。

本文為觀看視頻 1 ~ 3 節,參考 胡凱的 Android 效能最佳化典範第 5 季 總結所得,感謝他們。

Android 開發中多線程的必要性

Android 開發中,許多操作都需要由 主線程(UI 線程)來執行,比如:

  • 系統事件(例如裝置狀態變動)
  • 輸入事件
  • 服務
  • 鬧鐘
  • UI 繪製

我們經常需要針對這些情況編寫代碼。

由於主線程只有一個,所有任務都是串列執行,如果我們在某個操作中包含大量的網路請求、I/O,將會影響後續使用者後續操作。

使用者感知最明顯的就是介面繪製、響應是否及時

我們知道 Android 系統的螢幕重新整理頻率為 60 fps, 也就是每隔 16 ms 重新整理一次。如果在某次繪製過程中,我們的操作不能在 16 ms 內完成,那它則不能趕上這次的繪製公交車,只能等下一輪,這種現象叫做 “掉幀”,使用者看到的就是介面繪製不連續、卡頓

為了避免耗時較久的操作導致 “掉幀”,我們會把這些操作從主線程執行換到子線程,這樣主線程的其他動作不會受到影響,使用者體驗也會流暢許多。

理解 Android 多線程

一個線程,主要有三個狀態:開始、執行任務、結束。

當線程存活期間,我們會讓它執行大量的任務,當任務完成或者主動取消時,線程功成身退。

很多情況下,我們會有很多線程同時存活、執行任務,這時需要添加一個 任務隊列,讓線程不停地從隊列中擷取任務,同時有其他線程向其中新增工作,典型的 生產者-消費者 模型:

如果我們來實現這個模型,需要寫三個角色:生產者線程、消費者線程、任務隊列,同時還要保證它們的協作有條不紊,這可能會難倒一大堆人。

為了讓開發人員更省心,Android 系統替我們實現了上述類,分別是:

  • MessageQueue
  • Looper
  • Handler
MessageQueue

MessageQueue 就是任務隊列,儲存著不同類型任務的載體 (Intent, Runnable, Message)。

Looper

Looper 就是我們所說的 “消費者”,它不停地從任務隊列中擷取任務並執行。

Handler

Handler 就是 “生產者”,它把任務從其他線程送到 MessageQueue 中。

Handler 可以指定任務在任務隊列中的位置,也可以按照一定的時間延遲送貨。

HandlerThread

HandlerThread 就是上述三個組件的組合。

每個應用啟動時,系統會建立一個該應用的進程以及主線程,這裡的主線程就是一個 HandlerThread。

這個主線程會處理主要事件,具體內容:

Android 中為什麼只允許在主線程更新 UI

Android 系統中,預設只能在 主線程(UI 線程)更新 UI,當你在 子線程進行 UI 修改時,可能不起作用甚至是奔潰:

為什麼要這樣設計呢?

我們知道,多線程並發訪問資源要遵循重要的原則就是 原子性、可見度、有序性。沒有同步機制的情況下,多個線程同時讀寫記憶體可能會導致意料之外的問題:

多線程同時操作 UI 也一樣,如果想要允許多個線程更新 UI,就要設計對應的同步機制,為了避免這種問題,Android 系統直接規定只允許在 UI 線程更新 UI。

除了安全執行緒外,還有個原因: UI 組件的生命週期並不確定

可能有這種情況:我們在某個執行網路請求的線程中持有一個 Button 的引用,然而在請求結果返回之前,這個 button 被 View Hierarchy 移除,這時對 button 的任何操作都不可用,並且也沒有意義了。

此外還有一點 線程引用導致的記憶體流失問題

我們知道每個 View 都持有當前 Context, Activity 的引用,如果子線程持有某個 View 的引用,繼而持有了對應 Activity 引用,那麼線上程返回之前,即使該 Activity 不可用,也無法回收,這就造成了 記憶體流失。

除了持有 View,線程隱式持有 Activity 也可能導致記憶體流失,只要子線程沒有結束,參考關聯性就一直存在。

比如在 Activity 中建立個內部 AsyncTask:

或者是常見的在 Activity 裡建立個 Handler:

正如 Android Studio 提示的那樣,內部線程工具類持有外部類引用,可能會導致 記憶體流失

Android 系統為了避免過度複雜的安全執行緒問題,特地規定只允許在主線程中更新 UI。而開發人員,為了避免上述問題,需要注意的是:

不要再任何子線程持有 UI 組件或者 Activity 的引用。

總結

本文大概介紹了 Android 中多線程的必要性以及一些基礎概念。

Android 系統為我們提供了以下幾種工具類:

  • AsyncTask
    • 主線程、子線程間任務的切換
  • HandlerThread
    • 為某個任務/回調單獨開一個線程
  • ThreadPool
    • 管理多個線程,並發執行任務
  • IntentService
    • 在子線程中擷取 Intent,用於執行由 UI 出發的後台 Service

接下來我們將跟隨官方視頻逐漸瞭解這幾個工具類的特點,從而能夠在合適的情境下選擇對的人,儘可能地最佳化應用的效能。

感謝關注。

Thanks

https://www.youtube.com/watch?v=qk5F6Bxqhr4&index=1&list=PLWz5rJ2EKKc9CBxr3BVjPTPoDPLdPIFCE&t=39s

http://hukai.me/android-performance-patterns-season-5/

Android 效能最佳化:多線程注

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.