日本不卡不码高清免费观看,久久国产精品久久w女人spa,黄色aa久久,三上悠亚国产精品一区二区三区

您的位置:首頁技術文章
文章詳情頁

Android 多線程編程的總結

瀏覽:23日期:2022-09-27 13:40:56
前言

這幾天在研究Android的多線程方面的知識,閱讀了許多大牛的文章,發現Android的多線程方式挺多的,關于各種方式的優缺點也都各有看法,所以這部分的知識還是很容易令人覺得混亂的,所以自己梳理了相關知識,用自己的角度去簡單總結這些知識,鞏固自己知識的同時也希望幫助到其他人。首先,從兩個問題入手:我們為什么需要多線程機制?什么時候需要到多線程?答:1、因為Android官方明確聲明在多線程編程時有兩大原則:第一、不要阻塞UI線程(即主線程,下文兩個稱呼可互換)、第二、不要在UI線程之外訪問UI組件。這個話題是老生常談了,想必很多人都明白個中緣由。2、我對多線程的使用情況歸結為主要有兩種情況:第一、將任務從主線程拋到工作線程,第二種情況是將任務從工作線程拋到主線程。這兩種情況其實跟上面兩個原則是對應的。當我們有耗時的任務,如果在UI線程中執行,那就會阻塞UI線程了,必須要拋到工作線程中去執行;而當我們要更新UI組件時,就一定得在UI線程里執行了,所以就得把在工作線程中執行的任務結果返回到UI線程中去更新組件了。

一、將任務從工作線程拋到主線程

我們先從一段代碼開始

protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); button = (Button) findViewById(R.id.button); text = (TextView) findViewById(R.id.text);//耗時任務完成時在該TextView上顯示文本 mRunnable = new Runnable() { @Override public void run() { try { Thread.sleep(5000);//模擬耗時任務 } catch (InterruptedException e) { e.printStackTrace(); } text.setText('Task Done!!');//在非UI線程之外去訪問UI組件 } }; button.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { Thread thread = new Thread(mRunnable); thread.start(); } }); }

布局上只定義了一個Button和TextView,Button按下時會開啟一個新線程執行耗時任務,任務完成后更新TextView的文本。有點基礎的都能明白這段代碼是有問題的,因為它在非UI線程之外去訪問UI組件了。

那這個時候就得想辦法讓text.setText('Task Done!!');這句代碼拋到UI線程中去執行了。對此,我們大概有四種方法,下面分別演示。有如下5種方式

1、Handler.sendXXXMessage()等方法

首先是在上面的Activity中定義一個Handler

Handler mHandler = new Handler(){ @Override public void handleMessage(Message msg){ if(msg.what == 0x123){ text.setText('Task Done!!'); } } };

然后將工作線程的代碼改為下面的樣子

mRunnable = new Runnable() { @Override public void run() { try { Thread.sleep(5000);//模擬耗時任務 } catch (InterruptedException e) { e.printStackTrace(); } mHandler.sendEmptyMessage(0x123);//關于發消息的方法有很多,比如sendMessage(Message msg),sendMessageDelayed(Message msg, long delayMills)等等,可按具體需求選擇,這里不作擴展 } };

這樣程序運行起來后就不會報錯了。

關于Handler的底層機制網上有非常多文章作了詳細的描述,比如有張鴻洋的Android 異步消息處理機制 讓你深入理解 Looper、Handler、Message三者關系,這里也小小地提一下,為后面的內容做一些必要的鋪墊。一個線程只有一個Looper, 而一個Looper持有一個MessageQueue, 當調用Looper.prepare()時,Looper就與當前線程關聯起來了(在Activity里沒有顯示調用Looper.prepare()是因為系統自動在主線程里幫我們調用了),而Handler是與Looper的線程是綁定的,查看Handler類的源碼可以發現它幾個構造函數,其中有接收一個Looper參數的,也有不接收Looper參數的,從上面的代碼上看,我們沒有為Handler指定Looper,那么Handler就默認更當前線程(即主線程)的Looper關聯起來了,之所以啰嗦那么多就是因為這決定了Handler.handlerMessage(msg)方法體里的代碼到底在哪個線程里執行,我們再梳理一下,Looper.prepare調用決定了Looper與哪個線程關聯,間接決定了與這個Looper相關聯的Handler.handlerMessage(msg)方法體里的代碼執行的線程。(太啰嗦了)現在回到上面的代碼,我們的Handler是在主線程里的定義的,所以也默認跟主線程的Looper相關聯,即handlerMessage方法的代碼會在UI線程執行,因此更新TextView就不會報錯了。下面這張圖是弄清handlerMessage(msg)方法體里的代碼的執行線程的思路Android 多線程編程的總結

2、Handler.post(Runnable)

只要將上面代碼中的

mHandler.sendEmptyMessage(0x123);

改成

mHandler.post(new Runnable() { @Override public void run() { text.setText('Task Done!!'); } });

就可以了,可能有人看到new了一個Runnable就以為是又開了一個新線程,事實上并沒有開啟任何新線程,只是使run()方法體的代碼拋到與mHandler相關聯的線程中執行,經過上面的分析我們也知道mHandler是與主線程關聯的,所以更新TextView組件依然發生在主線程了。

3、Activity.runOnUIThread(Runnable)

將上面的代碼改成

runOnUiThread(new Runnable() { @Override public void run() { text.setText('Task Done!!'); } });

這個看起來跟上面的方法很像,差別就是這種方法不需要去定義Handler。

4、View.post(Runnable)

將上面的代碼改為

text.post(new Runnable() { @Override public void run() { text.setText('Task Done!!'); } });

這個看起來依舊是跟上面的方法很像,依然不用定義Handler。

5、AsyncTask

這種方法要改動上面整個開新線程的代碼,具體代碼在入門書籍上基本都有,這里就不附上了,思路就是在doInBackground(Params…) 方法里執行耗時邏輯,然后在onPostExecute(Result) 中將結果更新回UI組件。

使用哪種大多數情況我還是根據代碼風格和習慣來決定,上面這5種方法具體在效率上是否有巨大差異,我沒有深入研究,這方面有研究的兄弟希望可以在留言里交流一下

二、將任務從主線程拋到工作線程

正如前言所說,耗時任務不能在主線程去進行,需要另外開一個線程。分別有下面幾種方法:

1、Thread、Runnable

這個是最傳統的方法了,相信每個學過Java基礎的人都知道。無非就是繼承Thread類覆寫run()然后通過thread.start()或實現Runnable接口復寫run()然后New Thread(Runnable).start(),在上面的例子中就是通過這種最普通的方法去開新線程的,不過在實際開發中,這種開新線程的方法是很不被推薦的,理由如下:1)當你有多個耗時任務時就會開多個新線程,開啟新線程的以及調度多個線程的開銷是非常大的,這往往會帶來嚴重的性能問題,例如,你有100個耗時任務,那就開100個線程。2)如果在線程中執行循環任務,只能通過一個Flag來控制它的停止,如while(!iscancel){//耗時任務}。

2、HandlerThread

在正式介紹HandlerThread前,我們先來看看以下代碼:

protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); button = (Button) findViewById(R.id.button); button.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { mOtherHandler.sendEmptyMessage(0x124); } }); new Thread(new Runnable() { @Override public void run() { Looper.prepare();//在新線程中調用 mOtherHandler = new Handler() { //默認關聯新線程的Looper @Override public void handleMessage(Message msg) { if (msg.what == 0x124) { try { Log.d('HandlerThread', Thread.currentThread().getName());//打印線程名 Thread.sleep(5000);//模擬耗時邏輯 } catch (InterruptedException e) { e.printStackTrace(); } } } }; Looper.loop(); } }).start(); }

可以看到這里用的是第一種方法開啟新線程的,但是在新線程里初始化了Looper(因為不是在主線程,所以要我們自己調用Looper.prepare()和loop()),還定義了一個Handler ,前面我之所以那么啰嗦,就是為了讓你明白:這個Handler的handlerMessage(msg)方法體的代碼是在新線程(工作線程)中執行的,而不是主線程(忘了的話拉回去看前面的內容),所以我們只需要在Button的點擊事件中調用sendXXXMessage就可以讓耗時任務在新線程中執行了。

有意思的是,如果我們以非常快的速度連續點擊兩次Button,你會發現打印出來的兩條Log是以間隔5秒相繼出現的。這是因為每點一次按鈕并沒有開啟都開啟一個新線程,而只是發送了一條消息,我們在onCreate()里就已經把一個新線程開好了,然后調用Looper.loop()使這個線程一直處于循環狀態了,而我們每發一條消息,消息都會在MessageQueue里排隊。總而言之,不管我們點多少次按鈕,都只有一個工作線程,多個耗時任務在這個工作線程的隊列中排隊處理。思路如下圖Android 多線程編程的總結

鋪墊了這么多,可以把HandlerThread拉出來了,查看源碼,你會發現HandlerThread也是Thread的子類,那豈不是還是跟第一種方法一樣,說是也是,說不是也不是。其實呢,HandlerThread就是對上面的代碼的一種封裝,我們來看看它是怎么用的

handlerThread = new HandlerThread('MyNewThread');//自定義線程名稱 handlerThread.start(); mOtherHandler = new Handler(handlerThread.getLooper()){ @Override public void handleMessage(Message msg){ if (msg.what == 0x124){ try { Log.d('HandlerThread', Thread.currentThread().getName()); Thread.sleep(5000);//模擬耗時任務 } catch (InterruptedException e) { e.printStackTrace(); } } } };

這段代碼跟前面那一段代碼是完全等價的,HandlerThread的好處是代碼看起來沒前面的版本那么亂,相對簡潔一點。還有一個好處就是通過handlerThread.quit()或者quitSafely()使線程結束自己的生命周期。可能有人問了,那用以上方式執行完耗時任務后怎么更新UI組件了,很簡單,完全照著面前所說的將任務從工作線程拋到主線程的五種方法去做就可以了。可能又有人問了,那mOtherHandler.post(new Runnable())里的Runnable在哪個線程運行,還是工作線程,只不過這樣就避開了handlerMessage的步驟而已,跟前面的分析還是一樣的原理的。

3、AsyncTask

沒錯,又是它。具體的使用代碼就不貼上來了,到處都有。但值得一說的是,上面說過HandlerThread只開一條線程,任務都被阻塞在一個隊列中,那么就會使阻塞的任務延遲了,而AsyncTask開啟線程的方法asyncTask.execute()默認是也是開啟一個線程和一個隊列的,不過也可以通過asyncTask.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, 0)開啟一個含有5個新線程的線程池,也就是說有個5個隊列了,假如說你執行第6個耗時任務時,除非前面5個都還沒執行完,否則任務是不會阻塞的,這樣就可以大大減少耗時任務延遲的可能性,這也是它的優點所在,當你想多個耗時任務并發的執行,那你更應該選擇AsyncTask。

4、IntentService

最后再小小地提一下IntentService,相信很多人也不陌生,它是Service的子類,用法跟Service也差不多,就是實現的方法名字不一樣,耗時邏輯應放在onHandleIntent(Intent intent)的方法體里,它同樣有著退出啟動它的Activity后不會被系統殺死的特點,而且當任務執行完后會自動停止,無須手動去終止它。例如在APP里我們要實現一個下載功能,當退出頁面后下載不會被中斷,那么這時候IntentService就是一個不錯的選擇了。

筆者寫技術文章經驗很少,如有紕漏錯誤,歡迎指正交流!原文地址訪問:Android多線程編程的總結

標簽: Android
相關文章:
日本不卡不码高清免费观看,久久国产精品久久w女人spa,黄色aa久久,三上悠亚国产精品一区二区三区
日韩高清电影一区| 亚洲午夜黄色| aa亚洲婷婷| 欧美精品福利| 国产精品一国产精品k频道56| 日韩精品第二页| 久久亚洲二区| 视频一区二区不卡| 免费成人在线影院| 美女国产精品| 欧美亚洲网站| 国产精品一区二区av日韩在线| 日本中文字幕视频一区| 久久国产欧美日韩精品| 欧美亚洲tv| 91精品国产自产观看在线| 国产精品久av福利在线观看| 国产图片一区| 久久精品国产精品亚洲毛片| 伊人久久高清| 91精品精品| 欧美日韩国产免费观看视频| 亚洲免费成人av在线| 中文字幕日本一区二区| 亚洲理论在线| 国产一区二区三区四区五区| 亚洲欧洲美洲av| 欧美日韩中文一区二区| 亚洲精品人人| 久久中文欧美| 久久三级福利| 日本在线成人| 久久99精品久久久久久园产越南| 国产精品一区二区中文字幕| 久久久精品网| 欧美日韩第一| 中文无码日韩欧| 国产精品久久777777毛茸茸| 国产一区一一区高清不卡| 麻豆视频在线看| 久热re这里精品视频在线6| 亚洲精品在线国产| 久久中文在线| 中文字幕一区二区精品区| 国产丝袜一区| 日本不良网站在线观看| 日韩一区精品| 国产中文欧美日韩在线| 视频一区中文| 精品国产乱码久久久久久樱花| 久久精品国产www456c0m| 亚洲资源网站| 日韩亚洲一区在线| 久久国产福利| 精品资源在线| 婷婷视频一区二区三区| 国内在线观看一区二区三区| 午夜国产精品视频| 四虎成人av| 蜜臀精品一区二区三区在线观看 | av一区在线| 国产资源在线观看入口av| 亚洲精品麻豆| 成人在线视频区| 在线观看一区| 欧美日韩国产高清电影| 麻豆久久久久久| 国产字幕视频一区二区| 国产一区二区三区精品在线观看| 欧美午夜不卡| 国产精品成人a在线观看| **爰片久久毛片| 久久精品欧美一区| 国产精品久久久亚洲一区| 午夜一级在线看亚洲| 国产精品第一国产精品| 99riav1国产精品视频| 精品视频国产| 青青国产91久久久久久| 日韩午夜在线| 首页国产精品| 国产一区2区| 欧美有码在线| 性色一区二区| 91精品电影| 日韩一区电影| 国产精品一线| 国产日韩欧美中文在线| 久久99伊人| 欧美日韩尤物久久| 精品国产乱码久久久久久樱花 | 精品成av人一区二区三区| 亚洲日本国产| 成人美女视频| а√天堂8资源中文在线| 国产精品一区二区三区美女| 午夜在线精品| 亚洲午夜久久| av不卡在线看| 国产精品不卡| 深夜视频一区二区| 成人片免费看| 亚洲五月婷婷| 日本不卡免费高清视频在线| 国产精品1区| 免费久久精品视频| 日韩欧美三区| 亚洲精品观看| 四虎精品永久免费| 一区二区91| 蜜臀av亚洲一区中文字幕| 免费国产亚洲视频| 99国产精品久久久久久久| 久久中文视频| 鲁大师成人一区二区三区| 在线一区欧美| 国产亚洲在线| 日韩激情啪啪| 亚洲小说春色综合另类电影| 99热精品在线| 久久精品99国产精品| 久久精品99国产国产精| 国产欧美日韩一区二区三区四区| 精品伊人久久久| 波多野结衣久久精品| 九色porny丨国产首页在线| 亚洲精品一二三区区别| 亚洲欧洲一区| 日韩一级网站| 国产欧美一区二区三区精品观看| 国产亚洲观看| 国产精品一卡| 久久久男人天堂| 正在播放日韩精品| 亚洲制服少妇| 亚洲精品一级| 国产精品观看| 性欧美xxxx免费岛国不卡电影| 免费不卡中文字幕在线| 日韩在线观看不卡| 亚洲精品极品| 美女久久一区| 亚洲精品精选| 精品精品国产三级a∨在线| 免费在线观看一区| 精品国产不卡一区二区| 亚洲高清二区| 久久高清国产| 亚洲精品1区2区| 国产激情久久| 激情久久五月| 日韩一区二区三区免费视频| 国产精品巨作av| 欧美日本久久| 美女国产一区二区三区| 久久精品成人| 国产精品久久久久av蜜臀 | 国产精品成久久久久| 成人免费一区| 国产精品1区| 国产精品网在线观看| 中文字幕一区二区三区在线视频| 日本精品另类| 亚洲欧美在线专区| 亚洲一区日韩在线| 精品久久网站| 国产一区二区三区四区五区传媒| 麻豆久久一区二区| 在线亚洲国产精品网站| 国产午夜精品一区二区三区欧美| 午夜精品网站| 免费视频一区三区| 日本一区福利在线| 中文字幕系列一区| 欧美一区久久久| 欧美一区激情| 国产综合亚洲精品一区二| 国内精品福利| 久久av中文| 青青草91视频| 国产亚洲久久| 日韩精品亚洲aⅴ在线影院| 老司机精品久久| 99精品电影| 蜜桃av一区二区在线观看| 国产a亚洲精品| 高清不卡一区| 国产精品欧美日韩一区| 亚洲伊人影院| 久久av在线| 日韩毛片视频| 国产一区调教| 国产日韩欧美一区| 免费观看在线综合| 国产婷婷精品| 国产模特精品视频久久久久| 欧美香蕉视频| 日韩在线不卡| 国产va免费精品观看精品视频| 国产精品美女久久久久久不卡 |