在上篇文章中初步瞭解了Android系統的四大組件之一的服務Service
,在服務內能夠執行無用戶交互的耗時操做任務,可是包括以前關於界面系列文章在內,生命週期方法都是在主線程內被系統回調的。若是直接在生命週期方法中執行耗時操做,一樣可能會在主線程5s內無響應而觸發系統對應用程序的ANR異常。爲了解決這個問題,就須要使用多線程開發來執行耗時任務,在任務執行結束後將結果返回到主線程中響應。html
什麼是線程呢?每一個應用程序在初始化時,默認運行在以其包名命名的進程中,同一進程中的內存是能夠共享使用的。而每一個進程在建立時,都會隨之建立一個java.lang.Thread實例的線程,用以執行Android系統對當前應用程序的生命週期方法回調,也就是一般意義上的UI界面繪製等任務,這也就是所謂的主線程。而每一個進程在主線程中,能夠繼續建立多個線程,理論上只要硬件內存支持,線程是能夠無限建立的。這些新建立的線程,被稱爲子線程。子線程中能夠執行耗時任務,以此使得主線程中的界面任務保持與用戶的及時響應。
值得注意的是,子線程中是不容許執行更新界面相關操做,必須切換回主線程繪製界面。java
新任務類須要實現java.lang.Runnable接口,在實現的run()
方法中處理須要操做的任務。android
在開發中啓動新任務的方式主要有三種,其一是直接建立最基礎的線程類,單獨管理並執行耗時任務;其二是建立一個由一堆線程組成的線程池,將耗時任務放進去執行,剩下的由線程池管理;其三則是使用成熟的併發庫,根據不一樣的併發庫建立及啓動任務的方式也將不只限於Runnable
類型的實例。另外在Android R即API30之前,還可使用android.os.AsyncTask建立異步任務,可是該方式在API30以後已廢棄,故不推薦使用。下面將簡單介紹以上三種主流方式。多線程
一般使用Thread(Runnable target)
構造方法建立子線程,參數 target 做爲要執行的任務對象。
以後在須要執行任務的位置調用子線程對象的start()
方法啓動運行該線程便可。
因爲建立的線程是依託於某個界面Activity
或服務Service
的一個組件,因此當該組件的生命週期方法銷燬後,其中建立的子線程也就銷燬了。因此子線程必需要在被銷燬以前調用interrupt()
方法中斷運行並釋放其佔用的資源,以防止發生內存泄漏等問題。併發
經過線程池管理類java.util.concurrent.Executors的newCachedThreadPool()
等系列靜態方法,能夠直接建立java.util.concurrent.Executor接口定義的線程池類實例化對象。
以後在須要執行任務的位置調用線程池對象的execute(Runnable command)
方法便可執行一次任務。
線程池類的優勢在於當該組件的生命週期方法銷燬後,該線程池及其中的線程都會被強制銷燬,不須要手動管理。異步
關於Android系統的多線程開發,目前已有多個成熟的併發庫能夠直接使用,包括基於Java的RxJava、基於Kotlin的協程等,然而他們的底層原理都是與上述相似的。至於如何使用現有的多線程開發庫,將在後續文章中詳細介紹。oop
因爲不一樣任務是運行在不一樣線程中的,因此任務間通訊實際上也是線程間的通訊。這主要經過android.os.Handler類來實現的。說到通訊的話就是一方發送內容和另外一方接收內容的過程,Android系統將要通訊的內容封裝爲android.os.Message類,其中有 int arg1
和int arg2
兩個屬性儲存簡單的數值內容、Object obj
屬性存儲任意類型的對象、int what
屬性能夠標記區分不一樣的Message
類型。post
在須要處理通訊內容的線程中,建立Handler
實例化對象。
可使用Handler(Looper looper)
構造方法或者createAsync(Looper looper)
靜態方法,建立處理任意內容的實例化對象。其中的參數 looper 標記當前Handler
對象中的處理操做是在哪一個線程,若是是主線程可使用Looper.getMainLooper()
靜態方法獲取android.os.Looper對象,若是是子線程,能夠在子線程的run()
方法中使用Looper.myLooper()
靜態方法獲取當前線程的Looper
對象。google
或者使用Handler(Looper looper, Handler.Callback callback)
構造方法或者createAsync(Looper looper, Handler.Callback callback)
靜態方法,建立須要接收Message
消息處理的實例化對象。參數 looper 一樣是標記當前Handler
對象中的處理操做是在哪一個線程。參數 callback 是android.os.Handler.Callback接口實現的實例化對象,其中實現的handleMessage(Message msg)
方法能夠接收並處理通訊的結果。這裏的參數 msg 就是收到的Message
消息內容。線程
若是是經過Looper.myLooper()
靜態方法獲取的Looper
對象,也就是在子線程中處理通訊結果的話,在建立Handler
對象先後還要特別調用兩個方法。
在上面初始化Handler
對象以前,必須在子線程中先調用Looper.prepare()
靜態方法以初始化Looper
對象,以此保證在調用Looper.myLooper()
方法時獲取到的對象非空。
以及在初始化Handler
對象以後,必須在當前子線程中及時調用Looper.loop()
靜態方法以準備Message
消息隊列供當前子線程使用。
在須要發送通訊內容的線程中,須要首先接收到上文建立的Handler
實例化對象。
在切換線程處理的位置,調用Handler
對象的post(Runnable r)
系列方法,參數 r 就是要在Handler
對象所在線程中處理的Runnable
任務對象。
或者在須要發送消息的位置,調用Handler
對象的obtainMessage()
系列方法,能夠獲取到空閒可使用的Message
消息對象,將要發送的消息體內容賦值給Message
對象的不一樣屬性。最後再調用Handler
對象的sendMessage(Message msg)
系列方法,將消息體Message
對象發送便可。