什么是線程?
線程或者線程執(zhí)行本質(zhì)上就是一串命令(也是程序代碼),然后我們把它發(fā)送給操作系統(tǒng)執(zhí)行。
Multithreaded_process
一般來說,我們的CPU在任何時(shí)候一個核只能處理一個線程。多核處理器(目前大多數(shù)Android設(shè)備已經(jīng)都是多核)顧名思義,就是可以同時(shí)處理多線程(通俗地講就是可以同時(shí)處理多件事)。
多核處理與單核多任務(wù)處理的實(shí)質(zhì)
上面我說的是一般情況,并不是所有的描述都是一定正確的。因?yàn)閱魏艘部梢杂枚嗳蝿?wù)模擬出多線程。
每個運(yùn)行在線程中的任務(wù)都可以分解成多條指令,而且這些指令不用同時(shí)執(zhí)行。所以,單核設(shè)備可以首先切換到線程1去執(zhí)行指令1A,然后切換到線程2去執(zhí)行指令2A,接著返回到線程1再去執(zhí)行1B、1C、1D,然后繼續(xù)切換到線程2,執(zhí)行2B、2C等等,以此類推。
這個線程之間的切換十分迅速,以至于在單核的設(shè)備中也會發(fā)生。幾乎所有的線程都在相同的時(shí)間內(nèi)進(jìn)行任務(wù)處理。其實(shí),這都是因?yàn)樗俣忍煸斐傻募傧螅拖耠娪啊逗诳偷蹏防锏奶毓rown一樣,可以變幻出很多的頭和手。
接下來我們來看一些代碼。
Java核心里的線程
在Java中,如果要想做平行任務(wù)處理的話,會在Runnable里面執(zhí)行你的代碼。可以繼承Thread類,或者實(shí)現(xiàn)Runnable接口:
// Version 1public class IAmAThread extends Thread {  public IAmAThread() {    super("IAmAThread");  }   @Override  public void run() {     // your code (sequence of instructions)  }}// to execute this sequence of instructions in a separate thread.new IAmAThread().start(); // Version 2public class IAmARunnable implements Runnable {  @Override  public void run() {     // your code (sequence of instructions)  }}// to execute this sequence of instructions in a separate thread.IAmARunnable myRunnable = new IAmARunnable();new Thread(myRunnable).start(); 這兩個方法基本上是一樣的。第一個版本是創(chuàng)建一個Thread類,第二個版本是需要創(chuàng)建一個Runnable對象,然后也需要一個Thread類來調(diào)用它。
第二個版是通常建議使用的方法。這也是一個很大的主題了,超過了本文的范圍,以后會再做討論。
Android上的線程
無論何時(shí)啟動APP,所有的組件都會運(yùn)行在一個單獨(dú)的線程中(默認(rèn)的)――叫做主線程。這個線程主要用于處理UI的操作并為視圖組件和小部件分發(fā)事件等,因此主線程也被稱作UI線程。
如果你在UI線程中運(yùn)行一個耗時(shí)操作,那么UI就會被鎖住,直到這個耗時(shí)操作結(jié)束。對于用戶體驗(yàn)來說,這是非常糟糕的!這也就是為什么我們要理解Android上的線程機(jī)制了。理解這些機(jī)制就可以把一些復(fù)雜的工作移動到其它的線程中去執(zhí)行。如果你在UI線程中運(yùn)行一個耗時(shí)的任務(wù),那么很有可能會發(fā)生ANR(應(yīng)用無響應(yīng)),這樣用戶就會很快地結(jié)束掉你的APP。
Android和Java一樣,它支持使用Java里面的Thread類來進(jìn)行一步任務(wù)處理。所以可以輕松地像上面Java的例子一樣來使用Android上的線程,不過那好像還是有點(diǎn)困難。
為什么在Android上使用標(biāo)準(zhǔn)Java的線程會困難呢?
其實(shí)平行任務(wù)處理沒有想象中的那么簡單,你必須在多線程中保證并發(fā),就像偉大的Tim Bray說的那樣:ordinary humans can't do concurrency at scale (or really at all) …
特別對于Android來說,以下這些功能就略顯臃腫:
異步對于UI線程來說是一個主要的PITA(如果你需要在后臺線程中向主線程更新界面,那么你就會用到)。 如果屏幕方向或者屏幕配置改變的話,就會出現(xiàn)一些更加奇怪的現(xiàn)象。因?yàn)楦淖兤聊环较颍瑫餉ctivity重建(所以后臺線程就需要去改變被銷毀的Activity的狀態(tài)了,而如果后臺線程不是在UI線程之上的話,那情況會更加復(fù)雜,原因如條件1)。 對于線程池來說,沒有默認(rèn)的處理方式。 取消線程操作需要自定義代碼實(shí)現(xiàn)。
那么在Android上怎么進(jìn)行任務(wù)并發(fā)處理呢?
你可能聽過一些Android上一些常見的名詞:
1、Handler
這就是我們今天要討論的詳細(xì)主題。
2、AsyncTask
使用AsyncTask是在Android上操作線程最簡單的方式,也是最容易出錯的方式。
3、IntentService
這種方式需要寫更多的代碼,但是這是把耗時(shí)任務(wù)移動到后臺的很好的方式,也是我最喜歡的方式。配上使用一個EventBus機(jī)制的框架如Otto,這樣的話實(shí)現(xiàn)IntentService就非常簡單了。
4、Loader
關(guān)于處理異步任務(wù),還有很多事情需要做,比如從數(shù)據(jù)庫或者內(nèi)容提供者那里處理一些數(shù)據(jù)。
5、Service
如果你曾經(jīng)使用過Service的話,你應(yīng)該知道這里會有一點(diǎn)誤區(qū),其中一個常見的誤解就是服務(wù)是運(yùn)行在后臺線程的。其實(shí)不是!看似運(yùn)行在后臺是因?yàn)樗鼈儾慌cUI組件關(guān)聯(lián),但是它們(默認(rèn))是運(yùn)行在UI線程上的……所以默認(rèn)運(yùn)行在UI線程上,甚至在上面沒有UI部件。
如果想要把服務(wù)運(yùn)行在后臺線程中,那么必須自定義一個線程,然后把操作代碼都運(yùn)行在那個線程中(與上面提到的方法很類似)。事實(shí)上你應(yīng)該使用IntentService實(shí)現(xiàn),但是這不是本文討論的主題。
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。
消息隊(duì)列
在線程里基本都有一個叫做“消息隊(duì)列”的東西,它負(fù)責(zé)線程間通信。這是一種設(shè)計(jì)模式,所有控制指令或者內(nèi)容在線程間傳遞。
消息隊(duì)列如同它的名字那樣,對于線程來說,它就是一個指令隊(duì)列。這里我們還可以做一些更酷的事:
定時(shí)消息和線程在某個時(shí)間點(diǎn)才執(zhí)行。 需要在另一個線程中去添加入隊(duì)動作,而不是在本線程中。
注意:這里說的“消息”和Runnable對象、指令隊(duì)列的概念是一樣的。
回到Android上的Handler……如果你仔細(xì)閱讀的話,可以看到文檔是這樣說的:
> A Handler allows you to send and process Message and Runnable objects associated with a thread's MessageQueue.
所以Handler可以讓你給線程隊(duì)列發(fā)消息:
> Each Handler instance is associated with a single thread and that thread's message queue.
一個Handler對象只能和一個線程關(guān)聯(lián):
> When you create a new Handler, it is bound to the thread / message queue of the thread that is creating it
所以一個Handler到底和哪個線程關(guān)聯(lián)呢?就是創(chuàng)造它的線程。
> ― 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.、
在我們了解這些知識后,請繼續(xù)看……
小貼士:這里有幾點(diǎn)可能你還不知道。每個線程都和一個Handler類實(shí)例綁定,而且可以和別的線程一起運(yùn)行,相互通信。
還有一個小建議(如果用過AsyncTask的話),AsyncTask內(nèi)部也是使用Handler進(jìn)行處理的,只是不是運(yùn)行在UI線程而已,它會提供一個channel來和UI線程通信,使用postExecute方法即可實(shí)現(xiàn)。
這還挺酷的,那怎么創(chuàng)建Handler呢?
有兩種方式:
使用默認(rèn)的構(gòu)造方法:new Handler()。 使用帶參的構(gòu)造方法,參數(shù)是一個Runnable對象或者回調(diào)對象。
Handler里面有什么實(shí)用的API嗎?
請記住:
Handler只是簡單往消息隊(duì)列中發(fā)送消息而已(或者使用post方式) 它們有更方便的方法可以幫助與UI線程通信。
如果你現(xiàn)在看看Handler的API,可以清楚看到這幾個方法:
post postDelayed postAtTime
代碼示例
這里的代碼都是很基礎(chǔ)的,不過你可以好好看看注釋。
示例1:使用Handler的“post”方法
public class TestActivity extends Activity { // ...// all standard stuff @Overridepublic void 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 = new Handler();    // 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 = new Runnable() {    @Override    public void 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(new Runnable(){        @Override        public void 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.   new Thread(mRunnableOnSeparateThread).start();} }如果根本就沒有Handler對象,回調(diào)post方法會比較難辦。
示例2:使用postDelayed方法
近期本站新介紹的特性中,我每次都要模擬EditText的自動完成功能,每次文字改變后都會觸發(fā)一個API的調(diào)用,從服務(wù)器中檢索數(shù)據(jù)。
我想減少APP調(diào)用API的次數(shù),所以決定使用Handler的postDelayed方法來實(shí)現(xiàn)這個功能。
本例不針對平行處理,只是關(guān)于Handler給消息隊(duì)列發(fā)送消息還有安排消息在未來的某一點(diǎn)執(zhí)行等。
// 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 private long lastChange = 0; @Overridepublic void onTextChanged(final CharSequence chars,             int start, int before, int count) {      // The handler is spawned from the UI thread    new Handler().postDelayed(        // argument 1 for postDelated = message to be sent      new Runnable() {        @Override        public void run() {           if (noChangeInText_InTheLastFewSeconds()) {            searchAndPopulateListView(chars.toString()); // logic          }        }      },        // argument 2 for postDelated = delay before execution      300);     lastChange = System.currentTimeMillis();}  private boolean noChangeInText_InTheLastFewSeconds() {  return System.currentTimeMillis() - lastChange >= 300}最后我就把“postAtTime”這個方法作為聯(lián)系留給讀者們了,掌握Handler了嗎?如果是的話,那么可以盡情使用線程了。
1. Android進(jìn)程
在了解Android線程之前得先了解一下Android的進(jìn)程。當(dāng)一個程序第一次啟動的時(shí)候,Android會啟動一個LINUX進(jìn)程和一個主線程。默認(rèn)的情況下,所有該程序的組件都將在該進(jìn)程和線程中運(yùn)行。同時(shí),Android會為每個應(yīng)用程序分配一個單獨(dú)的LINUX用戶。Android會盡量保留一個正在運(yùn)行進(jìn)程,只在內(nèi)存資源出現(xiàn)不足時(shí),Android會嘗試停止一些進(jìn)程從而釋放足夠的資源給其他新的進(jìn)程使用, 也能保證用戶正在訪問的當(dāng)前進(jìn)程有足夠的資源去及時(shí)地響應(yīng)用戶的事件。Android會根據(jù)進(jìn)程中運(yùn)行的組件類別以及組件的狀態(tài)來判斷該進(jìn)程的重要性,Android會首先停止那些不重要的進(jìn)程。按照重要性從高到低一共有五個級別:
前臺進(jìn)程
前臺進(jìn)程是用戶當(dāng)前正在使用的進(jìn)程。只有一些前臺進(jìn)程可以在任何時(shí)候都存在。他們是最后一個被結(jié)束的,當(dāng)內(nèi)存低到根本連他們都不能運(yùn)行的時(shí)候。一般來說, 在這種情況下,設(shè)備會進(jìn)行內(nèi)存調(diào)度,中止一些前臺進(jìn)程來保持對用戶交互的響應(yīng)。 可見進(jìn)程
可見進(jìn)程不包含前臺的組件但是會在屏幕上顯示一個可見的進(jìn)程是的重要程度很高,除非前臺進(jìn)程需要獲取它的資源,不然不會被中止。 服務(wù)進(jìn)程
運(yùn)行著一個通過startService() 方法啟動的service,這個service不屬于上面提到的2種更高重要性的。service所在的進(jìn)程雖然對用戶不是直接可見的,但是他們執(zhí)行了用戶非常關(guān)注的任務(wù)(比如播放mp3,從網(wǎng)絡(luò)下載數(shù)據(jù))。只要前臺進(jìn)程和可見進(jìn)程有足夠的內(nèi)存,系統(tǒng)不會回收他們。 后臺進(jìn)程
運(yùn)行著一個對用戶不可見的activity(調(diào)用過 onStop() 方法).這些進(jìn)程對用戶體驗(yàn)沒有直接的影響,可以在服務(wù)進(jìn)程、可見進(jìn)程、前臺進(jìn) 程需要內(nèi)存的時(shí)候回收。通常,系統(tǒng)中會有很多不可見進(jìn)程在運(yùn)行,他們被保存在LRU (least recently used) 列表中,以便內(nèi)存不足的時(shí)候被第一時(shí)間回收。如果一個activity正 確的執(zhí)行了它的生命周期,關(guān)閉這個進(jìn)程對于用戶體驗(yàn)沒有太大的影響。 空進(jìn)程
未運(yùn)行任何程序組件。運(yùn)行這些進(jìn)程的唯一原因是作為一個緩存,縮短下次程序需要重新使用的啟動時(shí)間。系統(tǒng)經(jīng)常中止這些進(jìn)程,這樣可以調(diào)節(jié)程序緩存和系統(tǒng)緩存的平衡。
Android 對進(jìn)程的重要性評級的時(shí)候,選取它最高的級別。另外,當(dāng)被另外的一個進(jìn)程依賴的時(shí)候,某個進(jìn)程的級別可能會增高。一個為其他進(jìn)程服務(wù)的進(jìn)程永遠(yuǎn)不會比被服務(wù)的進(jìn)程重要級低。因?yàn)榉?wù)進(jìn)程比后臺activity進(jìn)程重要級高,因此一個要進(jìn)行耗時(shí)工作的activity最好啟動一個service來做這個工作,而不是開啟一個子進(jìn)程
主站蜘蛛池模板:
雷州市|
科技|
平顺县|
密云县|
大石桥市|
赫章县|
偃师市|
灌云县|
衡东县|
保康县|
湘潭县|
柘荣县|
宁远县|
永平县|
白朗县|
长子县|
瑞安市|
桐柏县|
抚远县|
诏安县|
微博|
遵化市|
江达县|
衡阳县|
伊宁县|
申扎县|
沅陵县|
荔波县|
张掖市|
临洮县|
许昌县|
阳谷县|
辰溪县|
广州市|
武邑县|
邳州市|
石渠县|
铜陵市|
方山县|
敦化市|
苍南县|