国产探花免费观看_亚洲丰满少妇自慰呻吟_97日韩有码在线_资源在线日韩欧美_一区二区精品毛片,辰东完美世界有声小说,欢乐颂第一季,yy玄幻小说排行榜完本

首頁 > 系統(tǒng) > Android > 正文

探尋Android的線程問題

2020-01-02 07:00:17
字體:
供稿:網(wǎng)友

什么是線程?

線程或者線程執(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)程

主站蜘蛛池模板: 雷州市| 科技| 平顺县| 密云县| 大石桥市| 赫章县| 偃师市| 灌云县| 衡东县| 保康县| 湘潭县| 柘荣县| 宁远县| 永平县| 白朗县| 长子县| 瑞安市| 桐柏县| 抚远县| 诏安县| 微博| 遵化市| 江达县| 衡阳县| 伊宁县| 申扎县| 沅陵县| 荔波县| 张掖市| 临洮县| 许昌县| 阳谷县| 辰溪县| 广州市| 武邑县| 邳州市| 石渠县| 铜陵市| 方山县| 敦化市| 苍南县|