Ⅰ android 中的「子線程」解析
Android 中線程可分為 主線程 和 子線程 兩類,其中主線程也就是 UI線程 ,它的主要這作用就是運行四大組件、處理界面交互。子線程則主要是處理耗時任務,也是我們要重點分析的。
首先 Java 中的各種線程在 Android 里是通用的,Android 特有的線程形態也是基於 Java 的實現的,所以有必要先簡單的了解下 Java 中的線程,本文主要包括以下內容:
在 Java 中要創建子線程可以直接繼承 Thread 類,重寫 run() 方法:
或者實現 Runnable 介面,然後用Thread執行Runnable,這種方式比較常用:
簡單的總結下:
Callable 和 Runnable 類似,都可以用來處理具體的耗時任務邏輯的,但是但具體的差別在哪裡呢?看一個小例子:
定義 MyCallable 實現了 Callable 介面,和之前 Runnable 的 run() 方法對比下, call() 方法是有返回值的哦,泛型就是返回值的類型:
一般會通過線程池來執行 Callable (線程池相關內容後邊會講到),執行結果就是一個 Future 對象:
可以看到,通過線程池執行 MyCallable 對象返回了一個 Future 對象,取出執行結果。
Future 是一個介面,從其內部的方法可以看出它提供了取消任務(有坑!!!)、判斷任務是否完成、獲取任務結果的功能:
Future 介面有一個 FutureTask 實現類,同時 FutureTask 也實現了 Runnable 介面,並提供了兩個構造函數:
用 FutureTask 一個參數的構造函數來改造下上邊的例子:
FutureTask 內部有一個 done() 方法,代表 Callable 中的任務已經結束,可以用來獲取執行結果:
所以 Future + Callable 的組合可以更方便的獲取子線程任務的執行結果,更好的控制任務的執行,主要的用法先說這么多了,其實 AsyncTask 內部也是類似的實現!
注意, Future 並不能取消掉運行中的任務,這點在後邊的 AsyncTask 解析中有提到。
Java 中線程池的具體的實現類是 ThreadPoolExecutor ,繼承了 Executor 介面,這些線程池在 Android 中也是通用的。使用線程池的好處:
常用的構造函數如下:
一個常規線程池可以按照如下方式來實現:
執行任務:
基於 ThreadPoolExecutor ,系統擴展了幾類具有新特性的線程池:
線程池可以通過 execute() 、 submit() 方法開始執行任務,主要差別從方法的聲明就可以看出,由於 submit() 有返回值,可以方便得到任務的執行結果:
要關閉線程池可以使用如下方法:
IntentService 是 Android 中一種特殊的 Service,可用於執行後台耗時任務,任務結束時會自動停止,由於屬於系統的四大組件之一,相比一般線程具有較高的優先順序,不容易被殺死。用法和普通 Service 基本一致,只需要在 onHandleIntent() 中處理耗時任務即可:
至於 HandlerThread,它是 IntentService 內部實現的重要部分,細節內容會在 IntentService 源碼中說到。
IntentService 首次創建被啟動的時候其生命周期方法 onCreate() 會先被調用,所以我們從這個方法開始分析:
這里出現了 HandlerThread 和 ServiceHandler 兩個類,先搞明白它們的作用,以便後續的分析。
首先看 HandlerThread 的核心實現:
首先它繼承了 Thread 類,可以當做子線程來使用,並在 run() 方法中創建了一個消息循環系統、開啟消息循環。
ServiceHandler 是 IntentService 的內部類,繼承了 Handler,具體內容後續分析:
現在回過頭來看 onCreate() 方法主要是一些初始化的操作, 首先創建了一個 thread 對象,並啟動線程,然後用其內部的 Looper 對象 創建一個 mServiceHandler 對象,將子線程的 Looper 和 ServiceHandler 建立了綁定關系,這樣就可以使用 mServiceHandler 將消息發送到子線程去處理了。
生命周期方法 onStartCommand() 方法會在 IntentService 每次被啟動時調用,一般會這里處理啟動 IntentService 傳遞 Intent 解析攜帶的數據:
又調用了 start() 方法:
就是用 mServiceHandler 發送了一條包含 startId 和 intent 的消息,消息的發送還是在主線程進行的,接下來消息的接收、處理就是在子線程進行的:
當接收到消息時,通過 onHandleIntent() 方法在子線程處理 intent 對象, onHandleIntent() 方法執行結束後,通過 stopSelf(msg.arg1) 等待所有消息處理完畢後終止服務。
為什麼消息的處理是在子線程呢?這里涉及到 Handler 的內部消息機制,簡單的說,因為 ServiceHandler 使用的 Looper 對象就是在 HandlerThread 這個子線程類里創建的,並通過 Looper.loop() 開啟消息循環,不斷從消息隊列(單鏈表)中取出消息,並執行,截取 loop() 的部分源碼:
dispatchMessage() 方法間接會調用 handleMessage() 方法,所以最終 onHandleIntent() 就在子線程中劃線執行了,即 HandlerThread 的 run() 方法。
這就是 IntentService 實現的核心,通過 HandlerThread + Hanlder 把啟動 IntentService 的 Intent 從主線程切換到子線程,實現讓 Service 可以處理耗時任務的功能!
AsyncTask 是 Android 中輕量級的非同步任務抽象類,它的內部主要由線程池以及 Handler 實現,在線程池中執行耗時任務並把結果通過 Handler 機制中轉到主線程以實現UI操作。典型的用法如下:
從 Android3.0 開始,AsyncTask 默認是串列執行的:
如果需要並行執行可以這么做:
AsyncTask 的源碼不多,還是比較容易理解的。根據上邊的用法,可以從 execute() 方法開始我們的分析:
看到 @MainThread 註解了嗎?所以 execute() 方法需要在主線程執行哦!
進而又調用了 executeOnExecutor() :
可以看到,當任務正在執行或者已經完成,如果又被執行會拋出異常!回調方法 onPreExecute() 最先被執行了。
傳入的 sDefaultExecutor 參數,是一個自定義的串列線程池對象,所有任務在該線程池中排隊執行:
可以看到 SerialExecutor 線程池僅用於任務的排隊, THREAD_POOL_EXECUTOR 線程池才是用於執行真正的任務,就是我們線程池部分講到的 ThreadPoolExecutor :
再回到 executeOnExecutor() 方法中,那麼 exec.execute(mFuture) 就是觸發線程池開始執行任務的操作了。
那 executeOnExecutor() 方法中的 mWorker 是什麼? mFuture 是什麼?答案在 AsyncTask 的構造函數中:
原來 mWorker 是一個 Callable 對象, mFuture 是一個 FutureTask 對象,繼承了 Runnable 介面。所以 mWorker 的 call() 方法會在 mFuture 的 run() 方法中執行,所以 mWorker 的 call() 方法在線程池得到執行!
同時 doInBackground() 方法就在 call() 中方法,所以我們自定義的耗時任務邏輯得到執行,不就是我們第二部分講的那一套嗎!
doInBackground() 的返回值會傳遞給 postResult() 方法:
就是通過 Handler 將最終的耗時任務結果從子線程發送到主線程,具體的過程是這樣的, getHandler() 得到的就是 AsyncTask 構造函數中初始化的 mHandler , mHander 又是通過 getMainHandler() 賦值的:
可以在看到 sHandler 是一個 InternalHandler 類對象:
所以 getHandler() 就是在得到在主線程創建的 InternalHandler 對象,所以
就可以完成耗時任務結果從子線程到主線程的切換,進而可以進行相關UI操作了。
當消息是 MESSAGE_POST_RESULT 時,代表任務執行完成, finish() 方法被調用:
如果任務沒有被取消的話執行 onPostExecute() ,否則執行 onCancelled() 。
如果消息是 MESSAGE_POST_PROGRESS , onProgressUpdate() 方法被執行,根據之前的用法可以 onProgressUpdate() 的執行需要我們手動調用 publishProgress() 方法,就是通過 Handler 來發送進度數據:
進行中的任務如何取消呢?AsyncTask 提供了一個 cancel(boolean mayInterruptIfRunning) ,參數代表是否中斷正在執行的線程任務,但是呢並不靠譜, cancel() 的方法注釋中有這么一段:
大致意思就是調用 cancel() 方法後, onCancelled(Object) 回調方法會在 doInBackground() 之後被執行而 onPostExecute() 將不會被執行,同時你應該 doInBackground() 回調方法中通過 isCancelled() 來檢查任務是否已取消,進而去終止任務的執行!
所以只能自己動手了:
AsyncTask 整體的實現流程就這些了,源碼是最好的老師,自己跟著源碼走一遍有些問題可能就豁然開朗了!
Ⅱ android中asynctask和thread的區別
Android平台很多應用使用的都是AsyncTask,而並非Thread和Handler去更新UI,這里Android123給大家說下他們到底有什麼區別,我們平時應該使用哪種解決方案。從Android 1.5開始系統將AsyncTask引入到android.os包中,過去在很早1.1和1.0 SDK時其實官方將其命名為UserTask,其內部是jdk 1.5開始新增的concurrent庫,做過j2ee的網友可能明白並發庫效率和強大性,比Java原始的Thread更靈活和強大,但對於輕量級的使用更為佔用系統資源。Thread是Java早期為實現多線程而設計的,比較簡單不支持concurrent中很多特性在同步和線程池類中需要自己去實現很多的東西,對於分布式應用來說更需要自己寫調度代碼,而為了Android UI的刷新google引入了Handler和Looper機制,它們均基於消息實現,有事可能消息隊列阻塞或其他原因無法准確的使用。 Android開發網推薦大家使用AsyncTask代替Thread+Handler的方式,不僅調用上更為簡單,經過實測更可靠一些,Google在Browser中大量使用了非同步任務作為處理耗時的I/O操作,比如下載文件、讀寫資料庫等等,它們在本質上都離不開消息,但是 AsyncTask相比Thread加Handler更為可靠,更易於維護,但AsyncTask缺點也是有的比如一旦線程開啟即 dobackground方法執行後無法給線程發送消息,僅能通過預先設置好的標記來控制邏輯,當然可以通過線程的掛起等待標志位的改變來通訊,對於某些應用Thread和Handler以及Looper可能更靈活。
Ⅲ 在Android中什麼是非同步執行
我來給你講解一下非同步的使用吧,
如果你不是開發人員,直接跳到第三,非同步的概念 和 同步的區別:
一、在你的Activity中寫一個內部類:
private class TestAsyncTask extends AsyncTask<String, Void, Boolean>
{
@Override
protected void onPreExecute()
{
//最先執行的就是這個。
}
@Override
protected Boolean doInBackground(String... params)
{
//這個是在後台執行的東西,就是說,它自動另外開了個線程運行,不影響你現在做的東西。
}
@Override
protected void onPostExecute(Boolean result)
{
if (result)
{
//後台執行的完畢後,它會用Result通知這里,就是執行這里了。
}
else
{
//所以最好判斷一下result,寫個else,判斷後台執行的東西是不是出問題了。
}
}
}
二,在你的onCreate的時候啟動這個非同步,啟動代碼如下:
new TestAsyncTask().execute("");
三,非同步 和 同步的區別
非同步的好處,就是把一些東西,特別是耗時間的東西扔到後台去運行了,doInBackground,程序可以繼續做自己的事情,防止程序卡在那裡失去響應。
同步執行的話,就是程序會呆板地從頭執行到尾,耗時間的東西不執行完,程序不會繼續往下走,等待時間長的話,有時候就會造成失去響應了。
我就是搞開發的,呵呵。我的代碼你直接貼進去就能用的。打字貼代碼辛苦啊~~望採納。也歡迎追問
Ⅳ android AsyncTask 中的幾個方法比如doInBackground 中後面幾個參數中間的幾個點是什麼意思
String... params表示的是可變參數列表,也就是說,這樣的方法能夠接受的參數個數是可變的,但不論多少,必須都是String類型的。比如
doInBackground("param1","param2","param3") ,或是doInBackground() 。實際上,在處理可變參數列表的時候,Java是轉化為數組來處理的,比如前面的例子,doInBackground傳進來三個參數,此時params實際上是一個String[3],可以通過params[0]來引用傳進來的實參"param1"。當doInBackground()沒有提供實參時params就為null。另外,對於可變參數列表的方法,我們可以直接傳遞一個數組代替,比如doInBackground(new String[]{"param1", "param2", "param3"}),效果和doInBackground("param1","param2","param3")是一樣的。
Ⅳ android AsyncTask 怎麼返回值給UI線程
可以看下以下幾點即可:
1.把AsyncTask單獨寫成類,不是其他Activity的子類。(這個顯然可以,記住,雖然AsyncTask不是某個Activity的子類,它依然只能在主線程中創建,其四大方法中的三個依然運行在主線程中,詳情見後面。)2.UI線程 通過AsyncTask和伺服器交互,需要根據結果 打開不同的新的activity,可以onPostExecute() 中返回值給主線程嗎?(這個問句就是錯的,onPostExecute()就是在主線程中執行的,不需要返回值給主線程,只需要在此方法中將結果存儲在某個變數中,就可以在主線程中獲取此值。例如,在AsyncTask中增加一個getResult方法)3.另外一個問題, 可以傳參數給AsyncTask,然後在onPostExecute 使用其他activity的 finish() 嗎?(Yes of course。這句問話依然來自你對onPostExecute 的誤解,再說一次,它就在主線程中執行。而Activity的finish方法在主線程中調用顯然沒問題。)簡單說AsyncTask,正好把上一個回答的部分結果拷貝過來給參考:
AsyncTask是一個輔助類,就是為了將Handler、Thread等封裝為一個非同步執行框架,供Android Coder可以方便的使用。其主要目的是為了「在其他線程中執行一個耗時操作,並隨時報告執行進度給UI線程,執行完成後將結果報告給UI線程」。
AsyncTask的使用方法其實Android developer中已經說得非常清楚了,因此擇重點翻譯一次:
AsyncTask使用時必須作為基類被擴展,子類至少重載一個方法doInBackground,另一個方法onPostExecute也經常被重載,代碼例子如下:
6private class DownloadFilesTask extends AsyncTask<URL, Integer, Long> {
protected Long doInBackground(URL... urls) {
int count = urls.length;
long totalSize = 0;
for (int i = 0; i < count; i++) {
totalSize += Downloader.downloadFile(urls[i]);
publishProgress((int) ((i / (float) count) * 100));
// Escape early if cancel() is called
if (isCancelled()) break;
}
return totalSize;
}
protected void onProgressUpdate(Integer... progress) {
setProgressPercent(progress[0]);
}
protected void onPostExecute(Long result) {
showDialog("Downloaded " + result + " bytes");
}
}
AsyncTask含有三個泛型參數: Params, 非同步任務執行所需的參數類型; Progress, 非同步任務執行進度的類型; Result, 非同步任務執行結果的類型。 這三個參數不必全部使用,不使用的參數置為Void即可,例如:
private class MyTask extends AsyncTask<Void, Void, Void> { ... }
AsyncTask的四個重要方法。當一個非同步任務被執行時,要經歷四步: onPreExecute(),在UI線程中執行,它會在非同步任務開始前執行,一般用來設置任務參數; doInBackground, 最重要的方法,在子線程中執行(事實上,只有它在子線程中執行,其他方法都在UI線程中執行)。當onPreExecute結束後,本方法立刻執行,它用來進行後台的耗時計算,非同步任務的參數會被傳給它,執行完成的結果會被送給第四步;執行途中,它還可以調用publishProgress 方法來通知UI線程當前執行的進度; onProgressUpdate, 當publishProgress 被調用後,它在UI線程中執行,刷新任務進度,一般用來刷新進度條等UI部件; onPostExecute, 當後台的非同步任務完成後,會在UI線程中被調用,並獲取非同步任務執行完成的結果。