Android系統編程入門系列之服務Service齊頭並進多線程任務

上篇文章中初步瞭解了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.ExecutorsnewCachedThreadPool()等系列靜態方法,能夠直接建立java.util.concurrent.Executor接口定義的線程池類實例化對象。
以後在須要執行任務的位置調用線程池對象的execute(Runnable command)方法便可執行一次任務。
線程池類的優勢在於當該組件的生命週期方法銷燬後,該線程池及其中的線程都會被強制銷燬,不須要手動管理。異步

併發庫

關於Android系統的多線程開發,目前已有多個成熟的併發庫能夠直接使用,包括基於Java的RxJava、基於Kotlin的協程等,然而他們的底層原理都是與上述相似的。至於如何使用現有的多線程開發庫,將在後續文章中詳細介紹。oop

任務間通訊(多線程通訊)

因爲不一樣任務是運行在不一樣線程中的,因此任務間通訊實際上也是線程間的通訊。這主要經過android.os.Handler類來實現的。說到通訊的話就是一方發送內容和另外一方接收內容的過程,Android系統將要通訊的內容封裝爲android.os.Message類,其中有 int arg1int 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對象中的處理操做是在哪一個線程。參數 callbackandroid.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對象發送便可。

相關文章
相關標籤/搜索