IntentService小酌

weixin_34290000發表於2016-11-25

前言:生活受到嚴重的打擊,又要投身到學習中,在程式碼中墮落。

2498006-c271a79dd33b4c42.png

IntentService是一個我們不常使用的類,可以說涉及到Service的我們都很少使用。最近有幸接觸這一個類,使用過程中發現這個類出乎意料的簡單,簡直是不學白不學,學習的過程中還能夠對Handler的知識進一步深入瞭解一下。

介紹

這是一個按要求處理非同步的請求(通過intent)繼承自Service的抽象子類。客戶端通過startService傳送請求傳遞Intent,開啟一個服務並且用一個工作執行緒輪流解決每一個intent,當結束工作後自動關閉服務。(依然擁有Service的特性,所以當我們在Service還沒關閉的時候呼叫Service,那麼不會建立新的Service,也就不會執行onCreate,但要是startService的時候,前一個請求已經執行完畢,那麼將會重新建立Service,執行onCreate)

實現這個IntentService類需要實現一個繼承自IntentService的子類,並且實現裡面的onHandleIntent(Intent)抽象方法,IntentService通過startService接收Intent後,在建立的時候建立一個工作執行緒,通過Handler傳送訊息傳遞Intent,,並且在執行完畢該Intent後試圖關閉。

所有的請求都會在一個單一的工作執行緒處理,它們不會阻塞主執行緒的loop,但一次只會處理一個請求。如果你想下載10個檔案,呼叫10次startService,那麼就會依次下載10個檔案。

構造方法

構造方法需要傳遞一個name引數,主要用於命名工作執行緒,對除錯比較重要。

2498006-e0dc7a29e22200fa.png

對於onBind,IntentService的預設實現是返回null,也可以理解為預設不與任何元件繫結,除非我們需要將它和某個元件繫結,不然不需要重寫這個方法,但是出於對IntentService的設計考慮,我認為我們不應該重寫這個方法,不然使用IntentService就沒有意義了。

Handler訊息機制

我們呼叫onStart還是onStartComand,都會執行onStart的操作

2498006-09d0938a826903b2.png
使用handler傳送Intent

既然使用到了Handler,那麼我們就要看一下IntentService是怎麼實現這個訊息傳遞的,以及怎麼處理訊息的:

2498006-511cb0ae5d1d4991.png
常量類,無法繼承

構造方法傳入了一個Looper,而handlerMessage中則是獲取訊息中的Intent,然後呼叫onHandleIntent方法,執行完之後就會根據startId來關閉service,需要注意的是stopSelf(int startId )這個方法,這個方法會嘗試關閉Service,只是嘗試而已,每次通過startService( )啟動的服務,系統都會生成一個startId,並且在onStartCommand中可以獲得這個startId。但是,只有當stopSelf裡面的startId等於最後一次呼叫startService( )所生成的startId,才會真正停止服務,否則服務是不會停止的。so easy。那麼IntentService是怎麼開啟一個工作執行緒來執行任務呢?前面已經提及到,我們來看一下IntentService中的onCreate方法:

2498006-d949c2ca851be55f.png

很明顯HandlerThread建立了一個執行緒,而這個執行緒是單一的,通過這個執行緒獲取Looper,並且使用這個Looper來輪訓訊息佇列MessageQueue。要知道,每個執行緒中有一個Looper,每一個Looper中有一個訊息佇列,而Looper會一直從訊息佇列中輪訓訊息,然後執行handler的handleMessage方法,就有了上面的步驟了,那麼可想而知,handleIntent是在哪個執行緒執行呢,自然是Looper所在的執行緒,也就是HandlerThread建立的這個執行緒了。

2498006-352aecd813fdb97d.png
外部傳入Looper所呼叫的構造方法

Handler(Looper looper)會直接呼叫this(looper,null,false),也就是上面的構造方法。而HandlerThread是一個繼承Thread類的子類,並且這個執行緒會有一個Looper。呼叫start()方法就會呼叫Looper的prepare和loop方法。到此,你就可以自己寫一個IntentService了,而這個IntentService可以做一些Intent級的後臺工作。

應用

比如,很多時候我們會在Application中做一些第三方類庫的初始化,但是當我們的類庫越來越多,或者第三方類庫的初始化越來越複雜的時候就會出現一個問題,開機時間過長,俗稱(冷啟動時間過長)。要知道,我們初始化我們的應用,做的可不僅僅是這些初始化第三方,如果都放在主執行緒中進行的話,那麼勢必會出現問題。而為了優化這個問題,我們可以嘗試使用IntentService來進行一些第三方類庫的初始化操作。好比如:

2498006-4e02db5c2b1efb5e.png
AppInitializeService呼叫start方法即可進行初始化

在檢視程式碼的過程中,我發現了一個比較有趣的東西,就是activity中的runOnUiThread這個方法

2498006-1fb78fb54559e89d.png

程式碼很簡單,如果不在主執行緒,那麼就使用mHandler傳送訊息,如果在主執行緒,那麼就直接執行。簡而言之,實現非同步的所有方法(非同步的概念是不用阻塞當前的執行緒來等待處理結果,允許後續操作,並且等其他執行緒處理完成,通過回撥通知此執行緒),僅且只有一種,那就是handler,而其餘的所有方法,都是對handler的一種封裝而已,有人可能會對IntentService的非同步不是很瞭解,要知道,IntentService繼承自Service,Service是一個在主執行緒執行的元件,只是這個元件執行在後臺而已,看不到的東西俗稱叫後臺。所以在這個Service內,我們可以通過handleIntent來實現一些非同步操作,當然了,在handleIntent中需要想辦法把訊息傳送到主執行緒的handler,才會達到非同步的需求。

總結

唉,當我們真正想了解一個類的時候,常常會發現研究程式碼的時候會涉及到越來越多的API,越來越多的機制和程式碼,即使簡單如AsyncTask,雖然程式碼量不多,但是展開了研究也是夠嗆,所以我們在研究程式碼的時候,點到即止,不然只會越來越混亂,比如說,我們通過檢視IntentService的程式碼從而得知這個API的特性:在Service中開啟一個工作執行緒執行Intent,並且在任務執行完畢後嘗試停止Service。

2498006-55c1cd724d270b3b.png

相關文章