Android线程详解
現在大多數的移動設備已經變得越來越快,但是它們其實也不算是非常快。如果你想讓你的APP既可以承受一些繁雜的工作而又不影響用戶體驗的話,那么必須把任務并行執行。在Android上,我們使用線程。
端一杯咖啡,然后仔細閱讀這篇文章。我會給大家介紹一下線程的概念,還有在Java中怎么使用線程,在線程中怎么使用Handler等。
如果需要使用異步處理或者并行任務的話,那么你一定會用到線程。
什么是線程?
線程或者線程執行本質上就是一串命令(也是程序代碼),然后我們把它發送給操作系統執行。
一般來說,我們的CPU在任何時候一個核只能處理一個線程。多核處理器(目前大多數Android設備已經都是多核)顧名思義,就是可以同時處理多線程(通俗地講就是可以同時處理多件事)。
多核處理與單核多任務處理的實質
上面我說的是一般情況,并不是所有的描述都是一定正確的。因為單核也可以用多任務模擬出多線程。
每個運行在線程中的任務都可以分解成多條指令,而且這些指令不用同時執行。所以,單核設備可以首先切換到線程1去執行指令1A,然后切換到線程2去執行指令2A,接著返回到線程1再去執行1B、1C、1D,然后繼續切換到線程2,執行2B、2C等等,以此類推。
這個線程之間的切換十分迅速,以至于在單核的設備中也會發生。幾乎所有的線程都在相同的時間內進行任務處理。其實,這都是因為速度太快造成的假象,就像電影《黑客帝國》里的特工Brown一樣,可以變幻出很多的頭和手。
接下來我們來看一些代碼。
Java核心里的線程
在Java中,如果要想做平行任務處理的話,會在Runnable里面執行你的代碼。可以繼承Thread類,或者實現Runnable接口:
// Version 1 publicclass IAmAThread extendsThread { ????publicIAmAThread() { ????????super("IAmAThread"); ????} ????@Override ????publicvoid run() { ????????// your code (sequence of instructions) ????} } // to execute this sequence of instructions in a separate thread. newIAmAThread().start(); // Version 2 publicclass IAmARunnable implementsRunnable { ????@Override ????publicvoid run() { ????????// your code (sequence of instructions) ????} } // to execute this sequence of instructions in a separate thread. IAmARunnable myRunnable = newIAmARunnable(); newThread(myRunnable).start();這兩個方法基本上是一樣的。第一個版本是創建一個Thread類,第二個版本是需要創建一個Runnable對象,然后也需要一個Thread類來調用它。
第二個版是通常建議使用的方法。這也是一個很大的主題了,超過了本文的范圍,以后會再做討論。
Android上的線程
無論何時啟動APP,所有的組件都會運行在一個單獨的線程中(默認的)——叫做主線程。這個線程主要用于處理UI的操作并為視圖組件和小部件分發事件等,因此主線程也被稱作UI線程。
如果你在UI線程中運行一個耗時操作,那么UI就會被鎖住,直到這個耗時操作結束。對于用戶體驗來說,這是非常糟糕的!這也就是為什么我們要理解Android上的線程機制了。理解這些機制就可以把一些復雜的工作移動到其它的線程中去執行。如果你在UI線程中運行一個耗時的任務,那么很有可能會發生ANR(應用無響應),這樣用戶就會很快地結束掉你的APP。
Android和Java一樣,它支持使用Java里面的Thread類來進行一步任務處理。所以可以輕松地像上面Java的例子一樣來使用Android上的線程,不過那好像還是有點困難。
為什么在Android上使用標準Java的線程會困難呢?
其實平行任務處理沒有想象中的那么簡單,你必須在多線程中保證并發,就像偉大的Tim Bray說的那樣:ordinary humans can’t do concurrency at scale (or really at all) …
特別對于Android來說,以下這些功能就略顯臃腫:
那么在Android上怎么進行任務并發處理呢?
你可能聽過一些Android上一些常見的名詞:
1、Handler
這就是我們今天要討論的詳細主題。
2、AsyncTask
使用AsyncTask是在Android上操作線程最簡單的方式,也是最容易出錯的方式。
3、IntentService
這種方式需要寫更多的代碼,但是這是把耗時任務移動到后臺的很好的方式,也是我最喜歡的方式。配上使用一個EventBus機制的框架如Otto,這樣的話實現IntentService就非常簡單了。
4、Loader
關于處理異步任務,還有很多事情需要做,比如從數據庫或者內容提供者那里處理一些數據。
5、Service
如果你曾經使用過Service的話,你應該知道這里會有一點誤區,其中一個常見的誤解就是服務是運行在后臺線程的。其實不是!看似運行在后臺是因為它們不與UI組件關聯,但是它們(默認)是運行在UI線程上的……所以默認運行在UI線程上,甚至在上面沒有UI部件。
如果想要把服務運行在后臺線程中,那么必須自定義一個線程,然后把操作代碼都運行在那個線程中(與上面提到的方法很類似)。事實上你應該使用IntentService實現,但是這不是本文討論的主題。
Android上的Handler
以下是從?Android developer documentation for Handlers:中摘選的一段話:
> A Handler allows you to send and process Message and Runnable objects associated with a thread’s MessageQueue. Each Handler instance is associated with a single thread and that thread’s message queue. When you create a new Handler, it is bound to the thread/message queue of the thread that is creating it — from that point on, it will deliver messages and runnables to that message queue and execute them as they come out of the message queue.
為了更好地了解這個概念,也許你需要去看看什么是Message Queues。
消息隊列
在線程里基本都有一個叫做“消息隊列”的東西,它負責線程間通信。這是一種設計模式,所有控制指令或者內容在線程間傳遞。
消息隊列如同它的名字那樣,對于線程來說,它就是一個指令隊列。這里我們還可以做一些更酷的事:
- 定時消息和線程在某個時間點才執行。
- 需要在另一個線程中去添加入隊動作,而不是在本線程中。
注意:這里說的“消息”和Runnable對象、指令隊列的概念是一樣的。
回到Android上的Handler……如果你仔細閱讀的話,可以看到文檔是這樣說的:
> A Handler allows you to send and process Message and Runnable objects associated with a thread’s MessageQueue.
所以Handler可以讓你給線程隊列發消息:
> Each Handler instance is associated with a single thread and that thread’s message queue.
一個Handler對象只能和一個線程關聯:
> When you create a new Handler, it is bound to the thread / message queue of the thread that is creating it
所以一個Handler到底和哪個線程關聯呢?就是創造它的線程。
> — from that point on, it will deliver messages and runnables to that message queue and execute them as they come out of the message queue.、
在我們了解這些知識后,請繼續看……
小貼士:?這里有幾點可能你還不知道。每個線程都和一個Handler類實例綁定,而且可以和別的線程一起運行,相互通信。
還有一個小建議(如果用過AsyncTask的話),AsyncTask內部也是使用Handler進行處理的,只是不是運行在UI線程而已,它會提供一個channel來和UI線程通信,使用postExecute方法即可實現。
這還挺酷的,那怎么創建Handler呢?
有兩種方式:
Handler里面有什么實用的API嗎?
請記住:
- Handler只是簡單往消息隊列中發送消息而已(或者使用post方式)
- 它們有更方便的方法可以幫助與UI線程通信。
如果你現在看看Handler的API,可以清楚看到這幾個方法:
代碼示例
這里的代碼都是很基礎的,不過你可以好好看看注釋。
示例1:使用Handler的“post”方法
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 | publicclass TestActivity extendsActivity { // ... // all standard stuff @Override publicvoid onCreate(Bundle savedInstanceState) { ????// ... ????// all standard stuff ????// we're creating a new handler here ????// and we're in the UI Thread (default) ????// so this Handler is associated with the UI thread ????Handler mHandler = newHandler(); ????// I want to start doing something really long ????// which means I should run the fella in another thread. ????// I do that by sending a message - in the form of another runnable object ????// But first, I'm going to create a Runnable object or a message for this ????Runnable mRunnableOnSeparateThread = newRunnable() { ????????@Override ????????publicvoid run () { ????????????// do some long operation ????????????longOperation(); ????????????// After mRunnableOnSeparateThread is done with it's job, ????????????// I need to tell the user that i'm done ????????????// which means I need to send a message back to the UI thread ????????????// who do we know that's associated with the UI thread? ????????????mHandler.post(newRunnable(){ ????????????????@Override ????????????????publicvoid run(){ ????????????????????// do some UI related thing ????????????????????// like update a progress bar or TextView ????????????????????// .... ????????????????} ????????????}); ????????} ????}; ????// Cool but I've not executed the mRunnableOnSeparateThread yet ????// I've only defined the message to be sent ????// When I execute it though, I want it to be in a different thread ????// that was the whole point. ????newThread(mRunnableOnSeparateThread).start(); } } |
如果根本就沒有Handler對象,回調post方法會比較難辦。
示例2:使用postDelayed方法
近期本站新介紹的特性中,我每次都要模擬EditText的自動完成功能,每次文字改變后都會觸發一個API的調用,從服務器中檢索數據。
我想減少APP調用API的次數,所以決定使用Handler的postDelayed方法來實現這個功能。
本例不針對平行處理,只是關于Handler給消息隊列發送消息還有安排消息在未來的某一點執行等。
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 | // the below code is inside a TextWatcher // which implements the onTextChanged method // I've simplified it to only highlight the parts we're // interested in privatelong lastChange = 0; @Override publicvoid onTextChanged(finalCharSequence chars, ??????????????????????????intstart, intbefore, intcount) { ????????// The handler is spawned from the UI thread ????????newHandler().postDelayed( ????????????// argument 1 for postDelated = message to be sent ????????????newRunnable() { ????????????????@Override ????????????????publicvoid run() { ????????????????????if(noChangeInText_InTheLastFewSeconds()) { ????????????????????????searchAndPopulateListView(chars.toString());?// logic ????????????????????} ????????????????} ????????????}, ????????????// argument 2 for postDelated = delay before execution ????????????300); ????????lastChange = System.currentTimeMillis(); } privateboolean noChangeInText_InTheLastFewSeconds() { ????returnSystem.currentTimeMillis() - lastChange >= 300 } |
最后我就把“postAtTime”這個方法作為聯系留給讀者們了,掌握Handler了嗎?如果是的話,那么可以盡情使用線程了。
1. Android進程
- 前臺進程
前臺進程是用戶當前正在使用的進程。只有一些前臺進程可以在任何時候都存在。他們是最后一個被結束的,當內存低到根本連他們都不能運行的時候。一般來說, 在這種情況下,設備會進行內存調度,中止一些前臺進程來保持對用戶交互的響應。 - 可見進程
可見進程不包含前臺的組件但是會在屏幕上顯示一個可見的進程是的重要程度很高,除非前臺進程需要獲取它的資源,不然不會被中止。 - 服務進程
運行著一個通過startService() 方法啟動的service,這個service不屬于上面提到的2種更高重要性的。service所在的進程雖然對用戶不是直接可見的,但是他們執行了用戶非常關注的任務(比如播放mp3,從網絡下載數據)。只要前臺進程和可見進程有足夠的內存,系統不會回收他們。 - 后臺進程
運行著一個對用戶不可見的activity(調用過 onStop() 方法).這些進程對用戶體驗沒有直接的影響,可以在服務進程、可見進程、前臺進 程需要內存的時候回收。通常,系統中會有很多不可見進程在運行,他們被保存在LRU (least recently used) 列表中,以便內存不足的時候被第一時間回收。如果一個activity正 確的執行了它的生命周期,關閉這個進程對于用戶體驗沒有太大的影響。 - 空進程
未運行任何程序組件。運行這些進程的唯一原因是作為一個緩存,縮短下次程序需要重新使用的啟動時間。系統經常中止這些進程,這樣可以調節程序緩存和系統緩存的平衡。
2. 單線程模型
2.1 子線程更新UI
2.2 Message Queue
1. Message Message消息,理解為線程間交流的信息,處理數據后臺線程需要更新UI,則發送Message內含一些數據給UI線程。 2. Handler Handler處理者,是Message的主要處理者,負責Message的發送,Message內容的執行處理。后臺線程就是通過傳進來的Handler對象引用來sendMessage(Message)。而使用Handler,需要implement 該類的?handleMessage(Message)方法,它是處理這些Message的操作內容,例如Update UI。通常需要子類化Handler來實現handleMessage方法。 3. Message Queue Message Queue消息隊列,用來存放通過Handler發布的消息,按照先進先出執行。 每個message queue都會有一個對應的Handler。Handler會向message queue通過兩種方法發送消息:sendMessage或post。這兩種消息都會插在message queue隊尾并按先進先出執行。但通過這兩種方法發送的消息執行的方式略有不同:通過sendMessage發送的是一個message對象,會被Handler的handleMessage()函數處理;而通過post方法發送的是一個runnable對象,則會自己執行。 4. Looper Looper是每條線程里的Message Queue的管家。Android沒有Global的Message Queue,而Android會自動替主線程(UI線程)建立Message Queue,但在子線程里并沒有建立Message Queue。所以調用Looper.getMainLooper()得到的主線程的Looper不為NULL,但調用Looper.myLooper()得到當前線程的Looper就有可能為NULL。2.3 AsyncTask
AsyncTask版:轉自:http://blog.jobbole.com/73267/ ?
? ? ? ? ? ?http://android.blog.51cto.com/268543/343823
與50位技術專家面對面20年技術見證,附贈技術全景圖總結
以上是生活随笔為你收集整理的Android线程详解的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Android 多线程之Handler
- 下一篇: Android 应用程序消息处理机制(L