天天看點

android 線程那點事

在作業系統中,線程是作業系統排程的最小單元,同時線程又是一種受限的系統資源,即線程不可能無限制的産生,并且線程的建立和銷毀都會有相應的開銷,當系統中存在大量的線程時,系統會通過時間片輪轉的方式排程每個線程,在這麼多線程中有一個被稱為主線程,主線程是指程序所擁有的線程,在java中預設情況下一個程序隻有一個線程,這個線程就是主線程。主線程主要處理界面互動相關的邏輯,因為使用者随時會和界面發生互動,是以主線程在任何時候都必須有比較高的響應速度,否則就會産生一種界面卡頓的感覺。為了保持較高的響應速度,這就要求主線程中不能執行耗時的任務,這個時候子線程就派上用場了。子線程也叫工作線程,除了主線程以外的線程都是子線程。

android中的線程

android沿用了java的線程模型,其中的線程也分為主線程和子線程,其中主線程又叫ui線程。在android系統中,在預設情況下,一個應用程式内的各個元件(如activity、broadcastreceiver、service)都會在同一個程序(process)裡執行,且由此程序的主線程負責執行。如果有特别指定(通過android:process),也可以讓特定元件在不同的程序中運作。無論元件在哪一個程序中運作,預設情況下,他們都由此程序的主線程負責執行。主線程既要處理activity元件的ui事件,又要處理service背景服務工作,通常會忙不過來。為了解決此問題,主線程可以建立多個子線程來處理背景服務工作,而本身專心處理ui畫面的事件。子線程的任務則是執行耗時任務,比如網絡請求,i/o操作等。從android4.0開始系統要求網絡通路必須在子線程中進行,否則網絡通路将會失敗并抛出networkonmainthreadexception這個異常,這樣做是為了避免主線程由于被耗時操作阻塞進而出現anr現象。

為什麼會出現anr

android希望ui線程能根據使用者的要求做出快速響應,如果ui線程花太多時間處理背景的工作,當ui事件發生時,讓使用者等待時間超過5秒而未處理,android系統就會給使用者顯示anr提示資訊。主線程除了處理ui事件之外,還要處理broadcast消息。是以在broadcastreceiver的onreceive()函數中,不宜占用太長的時間,否則導緻主線程無法處理其它的broadcast消息或ui事件。如果占用時間超過10秒,android系統就會給使用者顯示anr提示資訊。解決辦法自然還是解放ui主線程,将耗時操作交給子線程,避免阻塞。

android中也有main()方法

剛接觸android的開發者可能會因為找不到java程式的執行入口main()方法而覺得疑惑,其實android中當然是也有main()方法的(如下),它被包裝在源碼中的activitythread類裡。activitythread為應用程式的主線程類,所有的apk程式都有且僅有一個activitythread類,程式的入口為該類中的static main()方法,activitythread所在的線程即為ui線程或主線程。activity從main()方法開始執行,調用preparemain()為ui線程建立一個消息隊列(messagequeue)。然後建立一個activitythread對象,在activitythread的初始化代碼中會建立一個h(handler)對象和一個applicationthread(binder)對象。其中binder負責接收遠端ams的ipc調用,接收到調用後,則通過hander把消息發送到消息隊列,ui主線程會異步地從消息隊列中取出消息并執行相應操作,比如start,pause,stop等。接着ui主線程調用looper.loop()方法進入消息循環體,進入後就會不斷地從消息隊列中讀取并處理消息。

android中的子線程

android中開啟一個子線程無非還是這兩種方法

1:繼承thread類

2:實作runnable接口

android apk程式中都有哪些線程?

通過debug,我們可以捕獲目前應用程式中的線程(如下圖),其中藍色選中部分即為目前應用程式的主線程,目前程式中還運作了三個binder,每個binder對象都對應一個線程,這些binder線程主要負責接收linux binder驅動發送的ipc調用。除此以外還有java中的守護線程和垃圾回收線程堆裁剪守護程序等在運作。

android 線程那點事

paste_image.png

程式中自定義thread和ui線程的差別是什麼?

自定義thread和ui線程的差別在于,ui線程是從activitythread運作的,在該類中的main()方法中,已經使用looper.preparemainlooper()為該線程添加了looper對象,即已經為該線程建立了消息隊列(messagequeue),是以,程式員才可以在activity中定義hander對象(因為聲明hander對象時,所在的線程必須已經建立了messagequeue)。而普通的自定義thread是一個裸線程,是以,不能直接在thread中定義hander對象,從使用場景的角度講,即不能直接給thread對象發消息,但卻可以給ui線程發消息。

子線程為什麼不能更新ui

因為ui通路是沒有加鎖的,在多個線程中通路ui是不安全的,如果有多個子線程都去更新ui,會導緻界面不斷改變而混亂不堪。是以最好的解決辦法就是隻有一個線程有更新ui的權限,是以這個時候就隻能有一個線程振臂高呼:放開那女孩,讓我來!那麼最合适的人選隻能是主線程。

子線程也可以更新ui

surfaceview是 android 裡唯一一個可以在子線程更新的控件。surfaceview可以在主線程之外的線程中向螢幕繪圖。這樣可以避免畫圖任務繁重的時候造成主線程阻塞,進而提高了程式的反應速度。當需要快速,主動地更新view的ui,或者目前渲染代碼阻塞gui線程的時間過長的時候,surfaceview就是解決上述問題的最佳選擇。

子線程可以更新除surfaceview以外的ui

子線程更新ui?沒錯,不信下面的代碼跑一遍試試,并不會報錯,而且正确顯示。

這是為什麼呢?一個應用程式中有一個主線程和若幹個子線程,而線程的檢查工作是由viewroot完成的。viewroot是什麼呢?可以簡單的了解為window和view之前的橋梁或者紐帶。而viewroot的建立是在onresume()之後才完成的,也就是說在onresume()之前,系統本身是無法區分目前線程到底是主線程還是子線程,而上面的代碼中ui的更新操作在oncreate()中完成,先于onresume(),是以上述的子線程才有機會越俎代庖。

子線程如何與主線程通信

1、activity.runonuithread(runnable)

2、 view.post(runnable)

3、view.postdelayed(runnable,long)

4、handler(子線程調用handler的

handle.sendmessage(msg);

5、asynctask

主線程調用:

總結

最後來個總結,android中的線程延續了java的設計模型,預設一個應用程式隻有一個主線程,主線程的開啟是在activity的main()方法。主線程實際上是一個死循環,不斷的循環處理系統以及其他子線程發來的消息。主線程的綁定是在decorview初始化的時候,也就是生命周期的onresume()之後。主線程主要處理ui操作,和broadcast相關消息,主線程如果長時間無法響應,将出現anr,為了避免anr,耗時操作一般都開啟子線程處理。子線程處理完再發消息通知主線程來改變ui。

下一篇: Squid

繼續閱讀