Android的單線程

線程

應(yīng)用啟動時佳簸,系統(tǒng)會為應(yīng)用創(chuàng)建一個名為“主線程”的執(zhí)行線程滔韵。 此線程非常重要逻谦,因為它負(fù)責(zé)將事件分派給相應(yīng)的用戶界面小部件,其中包括繪圖事件奏属。 此外跨跨,它也是應(yīng)用與 Android UI 工具包組件(來自 android.widget 和 android.view 軟件包的組件)進行交互的線程。因此,主線程有時也稱為 UI 線程勇婴。

系統(tǒng)不會為每個組件實例創(chuàng)建單獨的線程忱嘹。運行于同一進程的所有組件均在 UI 線程中實例化,并且對每個組件的系統(tǒng)調(diào)用均由該線程進行分派耕渴。 因此颁股,響應(yīng)系統(tǒng)回調(diào)的方法(例如实柠,報告用戶操作的 onKeyDown() 或生命周期回調(diào)方法)始終在進程的 UI 線程中運行云矫。

例如抱冷,當(dāng)用戶觸摸屏幕上的按鈕時,應(yīng)用的 UI 線程會將觸摸事件分派給小部件添诉,而小部件反過來又設(shè)置其按下狀態(tài)屁桑,并將失效請求發(fā)布到事件隊列中。 UI 線程從隊列中取消該請求并通知小部件應(yīng)該重繪自身栏赴。

在應(yīng)用執(zhí)行繁重的任務(wù)以響應(yīng)用戶交互時蘑斧,除非正確實現(xiàn)應(yīng)用,否則這種單線程模式可能會導(dǎo)致性能低下须眷。 具體地講竖瘾,如果 UI 線程需要處理所有任務(wù),則執(zhí)行耗時很長的操作(例如花颗,網(wǎng)絡(luò)訪問或數(shù)據(jù)庫查詢)將會阻塞整個 UI捕传。 一旦線程被阻塞,將無法分派任何事件扩劝,包括繪圖事件庸论。 從用戶的角度來看,應(yīng)用顯示為掛起今野。 更糟糕的是葡公,如果 UI 線程被阻塞超過幾秒鐘時間(目前大約是 5 秒鐘)罐农,用戶就會看到一個讓人厭煩的“應(yīng)用無響應(yīng)”(ANR) 對話框条霜。如果引起用戶不滿,他們可能就會決定退出并卸載此應(yīng)用涵亏。

此外宰睡,Android UI 工具包并非線程安全工具包。因此气筋,您不得通過工作線程操縱 UI拆内,而只能通過 UI 線程操縱用戶界面。 因此宠默,Android 的單線程模式必須遵守兩條規(guī)則:

  1. 不要阻塞 UI 線程
  2. 不要在 UI 線程之外訪問 Android UI 工具包

工作線程

根據(jù)上述單線程模式麸恍,要保證應(yīng)用 UI 的響應(yīng)能力,關(guān)鍵是不能阻塞 UI 線程。 如果執(zhí)行的操作不能很快完成抹沪,則應(yīng)確保它們在單獨的線程(“后臺”或“工作”線程)中運行刻肄。
例如,以下代碼演示了一個點擊偵聽器從單獨的線程下載圖像并將其顯示在 ImageView中:

public void onClick(View v) {
  new Thread(new Runnable() {
    public void run() {
        Bitmap b = loadImageFromNetwork("http://example.com/image.png");
        mImageView.setImageBitmap(b);
    }
}).start();}

乍看起來融欧,這段代碼似乎運行良好敏弃,因為它創(chuàng)建了一個新線程來處理網(wǎng)絡(luò)操作。 但是噪馏,它違反了單線程模式的第二條規(guī)則:不要在 UI 線程之外訪問 Android UI 工具包 — 此示例從工作線程(而不是 UI 線程)修改了 ImageView麦到。 這可能導(dǎo)致出現(xiàn)不明確、不可預(yù)見的行為欠肾,但要跟蹤此行為困難而又費時瓶颠。

為解決此問題,Android 提供了幾種途徑來從其他線程訪問 UI 線程刺桃。 以下列出了幾種有用的方法:

  • Activity.runOnUiThread(Runnable)
  • View.post(Runnable)
  • View.postDelayed(Runnable, long)

例如步清,您可以通過使用 View.post(Runnable) 方法修復(fù)上述代碼:

public void onClick(View v) {
  new Thread(new Runnable() {
    public void run() {
        final Bitmap bitmap =
                loadImageFromNetwork("http://example.com/image.png");
        mImageView.post(new Runnable() {
            public void run() {
                mImageView.setImageBitmap(bitmap);
            }
        });
    }
}).start();}

現(xiàn)在,上述實現(xiàn)屬于線程安全型:在單獨的線程中完成網(wǎng)絡(luò)操作虏肾,而在 UI 線程中操縱 ImageView廓啊。

但是,隨著操作日趨復(fù)雜封豪,這類代碼也會變得復(fù)雜且難以維護谴轮。 要通過工作線程處理更復(fù)雜的交互,可以考慮在工作線程中使用 Handler 處理來自 UI 線程的消息吹埠。當(dāng)然第步,最好的解決方案或許是擴展 AsyncTask 類,此類簡化了與 UI 進行交互所需執(zhí)行的工作線程任務(wù)缘琅。

使用 AsyncTask

AsyncTask 允許對用戶界面執(zhí)行異步操作粘都。 它會先阻塞工作線程中的操作,然后在 UI 線程中發(fā)布結(jié)果刷袍,而無需您親自處理線程和/或處理程序翩隧。

要使用它,必須創(chuàng)建 AsyncTask 的子類并實現(xiàn) doInBackground() 回調(diào)方法呻纹,該方法將在后臺線程池中運行堆生。 要更新 UI,應(yīng)該實現(xiàn) onPostExecute() 以傳遞 doInBackground() 返回的結(jié)果并在 UI 線程中運行雷酪,以便您安全地更新 UI淑仆。 稍后,您可以通過從 UI 線程調(diào)用 execute() 來運行任務(wù)哥力。

例如蔗怠,您可以通過以下方式使用 AsyncTask 來實現(xiàn)上述示例:

public void onClick(View v) {
    new DownloadImageTask().execute("http://example.com/image.png");
}

private class DownloadImageTask extends AsyncTask<String, Void, Bitmap> {
    /** The system calls this to perform work in a worker thread and
      * delivers it the parameters given to AsyncTask.execute() */
    protected Bitmap doInBackground(String... urls) {
        return loadImageFromNetwork(urls[0]);
    }

    /** The system calls this to perform work in the UI thread and delivers
      * the result from doInBackground() */
    protected void onPostExecute(Bitmap result) {
        mImageView.setImageBitmap(result);
    }
}

現(xiàn)在 UI 是安全的,代碼也得到簡化,因為任務(wù)分解成了兩部分:一部分應(yīng)在工作線程內(nèi)完成寞射,另一部分應(yīng)在 UI 線程內(nèi)完成最住。

下面簡要概述了 AsyncTask 的工作方法,但要全面了解如何使用此類怠惶,您應(yīng)閱讀 AsyncTask 參考文檔:

可以使用泛型指定參數(shù)類型涨缚、進度值和任務(wù)最終值方法 doInBackground() 會在工作線程上自動執(zhí)行
onPreExecute()、onPostExecute() 和 onProgressUpdate() 均在 UI 線程中調(diào)用doInBackground() 返回的值將發(fā)送到 onPostExecute()您可以隨時在 doInBackground() 中調(diào)用publishProgress()策治,以在 UI 線程中執(zhí)行 onProgressUpdate()您可以隨時取消任何線程中的任務(wù)脓魏。

注意:使用工作線程時可能會遇到另一個問題,即:運行時配置變更(例如通惫,用戶更改了屏幕方向)導(dǎo)致 Activity 意外重啟茂翔,這可能會銷毀工作線程。 要了解如何在這種重啟情況下堅持執(zhí)行任務(wù)履腋,以及如何在 Activity 被銷毀時正確地取消任務(wù)珊燎,請參閱書架示例應(yīng)用的源代碼。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末遵湖,一起剝皮案震驚了整個濱河市悔政,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌谋国,老刑警劉巖,帶你破解...
    沈念sama閱讀 217,084評論 6 503
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件芦瘾,死亡現(xiàn)場離奇詭異集畅,居然都是意外死亡近弟,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,623評論 3 392
  • 文/潘曉璐 我一進店門挺智,熙熙樓的掌柜王于貴愁眉苦臉地迎上來祷愉,“玉大人逃贝,你說我怎么就攤上這事°灏猓” “怎么了?”我有些...
    開封第一講書人閱讀 163,450評論 0 353
  • 文/不壞的土叔 我叫張陵句占,是天一觀的道長沪摄。 經(jīng)常有香客問我,道長,這世上最難降的妖魔是什么祈餐? 我笑而不...
    開封第一講書人閱讀 58,322評論 1 293
  • 正文 為了忘掉前任哄陶,我火速辦了婚禮帆阳,結(jié)果婚禮上屋吨,老公的妹妹穿的比我還像新娘。我一直安慰自己至扰,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 67,370評論 6 390
  • 文/花漫 我一把揭開白布敢课。 她就那樣靜靜地躺著,像睡著了一般濒募。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上萨咳,一...
    開封第一講書人閱讀 51,274評論 1 300
  • 那天疫稿,我揣著相機與錄音,去河邊找鬼遗座。 笑死,一個胖子當(dāng)著我的面吹牛途蒋,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播号坡,決...
    沈念sama閱讀 40,126評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼腌紧!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起壁肋,我...
    開封第一講書人閱讀 38,980評論 0 275
  • 序言:老撾萬榮一對情侶失蹤号胚,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后猫胁,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,414評論 1 313
  • 正文 獨居荒郊野嶺守林人離奇死亡弃秆,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,599評論 3 334
  • 正文 我和宋清朗相戀三年髓帽,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片氢卡。...
    茶點故事閱讀 39,773評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖峡捡,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情们拙,我是刑警寧澤,帶...
    沈念sama閱讀 35,470評論 5 344
  • 正文 年R本政府宣布砚婆,位于F島的核電站突勇,受9級特大地震影響装盯,放射性物質(zhì)發(fā)生泄漏甲馋。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,080評論 3 327
  • 文/蒙蒙 一定躏、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧垮抗,春花似錦、人聲如沸碧聪。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,713評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽豁护。三九已至哼凯,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間断部,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,852評論 1 269
  • 我被黑心中介騙來泰國打工她渴, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人趁耗。 一個月前我還...
    沈念sama閱讀 47,865評論 2 370
  • 正文 我出身青樓疆虚,卻偏偏與公主長得像苛败,于是被迫代替她去往敵國和親径簿。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 44,689評論 2 354

推薦閱讀更多精彩內(nèi)容

  • Android的線程和線程池 從用途上來說缠捌,線程分為主線程和子線程译蒂,主線程主要處理和界面相關(guān)的事情曼月,而子線程則往往...
    努力生活的西魚閱讀 286評論 0 0
  • 當(dāng)某個應(yīng)用組件啟動且該應(yīng)用沒有運行其他任何組件時柔昼,Android 系統(tǒng)會使用單個執(zhí)行線程為應(yīng)用啟動新的 Linux...
    小蕓論閱讀 1,691評論 0 12
  • 導(dǎo)語 在Android系統(tǒng),線程主要分為主線程和子線程绩衷,主線程處理和界面相關(guān)的事情,而子線程一般用于執(zhí)行耗時操作咳燕。...
    一個有故事的程序員閱讀 590評論 0 3
  • 進程和線程 來源官方文檔 當(dāng)某個應(yīng)用組件啟動且該應(yīng)用沒有運行其他任何組件時乒躺,Android 系統(tǒng)會使用單個執(zhí)行線程...
    霧里看花最是迷人閱讀 809評論 0 2
  • 接著需要工作,打包一天需要的山藥水嘉冒,帶著紅糖咆繁,去上班顶籽! 早:兩碗蜂蜜水玩般,一杯紅糖水礼饱,一杯清水 午:服氣49口坏为,拍打...
    悠哉輕創(chuàng)聯(lián)盟閱讀 311評論 0 0