多執行緒和回呼入門

「以 Kotlin 開發 Android 應用程式」課程假設你已熟悉多執行緒的概念和術語。這個頁面屬於概略介紹和複習性質。

行動裝置都有處理器,而且今日大多數的裝置甚至搭載了多個硬體處理器,每個處理器會同時執行處理程序。這就是「多重處理程序」功能。

為了更有效率地使用處理器,作業系統可讓應用程式在處理程序內建立多個執行緒。這就是所謂的「多執行緒」功能。

圖片

這就像是同時閱讀多本書籍、每讀一章就換另一本,最終可以讀完所有書籍,但是同一時間點只能閱讀一本書。

要管理所有執行緒,其實需要基礎架構。

圖片

「排程器」會將優先順序等因素納入考量,以及確保所有執行緒都開始執行並完成。所有的書都不該永遠在書架上積灰塵,但是如果書籍篇幅很長或者可以稍後再讀,可能要一段時間之後才會送到你的手中。

「調度工具」會設定執行緒,也就是負責傳送你需要閱讀的書籍,並指定相關作業的結構定義。你可以將結構定義視為獨立的特殊閱讀室。某些結構定義最適合使用者介面作業,有些則專門用於處理輸入/輸出作業。

唯一需要另外注意的是,使用者導向的應用程式通常會具備在前景執行的「主要執行緒」,並且會調度可在背景執行的其他執行緒。

在 Android 中,主要執行緒是處理使用者介面所有更新作業的單一執行緒。這個主要執行緒又稱為「UI 執行緒」,同時也是呼叫所有點擊處理常式和其他 UI 及生命週期回呼的執行緒。UI 執行緒是預設執行緒。除非應用程式明確切換執行緒,或是使用在其他執行緒上執行的類別,否則應用程式只會在主執行緒上進行各項工作。

這可能會造成潛在的挑戰。UI 執行緒必須順暢執行,才能確保提供優質的使用者體驗。如果想讓應用程式持續向使用者顯示,而沒有任何可見的暫停點,主要執行緒每 16 毫秒或更短的間隔 (或大約每秒 60 影格數) 就必須更新畫面。就這個速度來看,人類認為畫面變更的過程完全行雲流水。影格很多,但時間很短。因此,如何避免在 Android 上封鎖 UI 執行緒,就顯得相當重要。在這個脈絡中,「封鎖」表示 UI 執行緒在等待其他事項 (例如等待資料庫完成更新) 期間,完全未執行任何動作。

圖片

許多常見工作耗時超過 16 毫秒,例如從網際網路擷取資料、讀取大型檔案,或是將資料寫入資料庫。因此,如果呼叫程式碼來執行主執行緒中的工作,可能會導致應用程式暫停、延遲,甚至是凍結。但是,如果封鎖主執行緒的時間過長,應用程式甚至可能會當機,並顯示「應用程式無回應」(ANR) 對話方塊。

回呼

你可以透過幾種不同的方式處理主執行緒上的工作。

在不封鎖主執行緒的情況下,執行長時間工作的一種模式就是回呼。透過回呼,你可以開始在背景執行緒上執行長時間的工作。 工作完成後,系統會呼叫做為引數的回呼,然後將結果通知主要執行緒上的程式碼。

回呼還算是不錯的模式,但有一些缺點。大量使用回呼的程式碼會變得不易讀取且難以理解。因為程式碼雖然看起來是依序執行,但回呼程式碼日後會在某些非同步的時間點執行。此外,回呼不允許使用某些語言功能,例如例外狀況。

協同程式

在 Kotlin 中,協同程式是能夠流暢且有效率地處理長時間執行工作的解決方案。運用 Kotlin 協同程式,你可將以回呼為基礎的程式碼轉換為循序程式碼。依序編寫的程式碼通常較容易閱讀,甚至能使用例外狀況等的語言功能。說到底,協同程式和回呼所做的工作其實完全相同:等到長時間執行的工作產生結果後,再繼續執行作業。