導航:首頁 > 操作系統 > android線程開啟關閉

android線程開啟關閉

發布時間:2023-03-24 04:24:21

A. android 編程中怎麼關閉activity線程

在Activity開啟的子線程並不會自動隨Activity的destroy而關閉,所以必須手動去關閉子線程或者通過boolean的方式讓子線程結束運行。開啟的子線程有for循環的要更加註意。

1 package com.lsw;
2
3 import android.app.Activity;
4 import android.os.Bundle;
5 import android.os.Handler;
6 import android.os.Message;
7 import android.util.Log;
8 public class ThreadDemoActivity extends Activity {
9 private static final String TAG = "ThreadDemo";
10 private int count = 0;
11 private Handler mHandler = new MyHandler();
12 boolean stopThread=false;
13
14 private Runnable mRunnable = new Runnable() {
15
16 public void run() {
17
18 while (!stopThread)
19 {
20 count++;
21 try
22 {
23 Thread.sleep(2000);
24 }
25 catch (InterruptedException e)
26 {
27 // TODO Auto-generated catch block
28 e.printStackTrace();
29 }
30
31 //雖然Message的構造函數是public的,但是最好是使用Message.obtain( )或Handler.obtainMessage( )函數來獲取Message對象,因為Message的實現中包含了回收再利用的機制,可以提供效率。
32 Message message=mHandler.obtainMessage();
33 message.what=0;
34 message.obj=count;
35 mHandler.sendMessage(message);
36 }
37 }
38 };
39
40 @Override
41 public void onCreate(Bundle savedInstanceState) {
42 super.onCreate(savedInstanceState);
43 setContentView(R.layout.main);
44 //開啟子線程
45 new Thread(mRunnable).start();
46 }
47
48 protected void onDestroy() {
49 System.out.println("-----------onDestroy------");
50 stopThread=true;
51 super.onDestroy();
52 };
53
54 class MyHandler extends Handler{
55
56 @Override
57 public void handleMessage(Message msg)
58 {
59 // TODO Auto-generated method stub
60 Log.e(TAG, Thread.currentThread().getName() + " " +msg.obj);
61 setTitle("" +msg.obj);
62 }
63 }
64
65 }

B. Android線程池ThreadPoolExecutor詳解

       傳統的多線程是通過繼承Thread類及實現Runnable介面來實現的,每次創建及銷毀線程都會消耗資源、響應速度慢,且線程缺乏統一管理,容易出現阻塞的情況,針對以上缺點,線程池就出現了。

       線程池是一個創建使用線程並能保存使用過的線程以達到復用的對象,簡單的說就是一塊緩存了一定數量線程的區域。

       1.復用線程:線程執行完不會立刻退出,繼續執行其他線程;
       2.管理線程:統一分配、管理、控制最大並發數;

       1.降低因頻繁創建&銷毀線程帶來的性能開銷,復用緩存在線程池中的線程;
       2.提高線程執行效率&響應速度,復用線程:響應速度;管理線程:優化線程執行順序,避免大量線程搶占資源導致阻塞現象;
       3.提高對線程的管理度;

       線程池的使用也比較簡單,流程如下:

       接下來通過源碼來介紹一下ThreadPoolExecutor內部實現及工作原理。

       線程池的最終實現類是ThreadPoolExecutor,通過實現可以一步一步的看到,父介面為Executor:

       其他的繼承及實現關系就不一一列舉了,直接通過以下圖來看一下:

       從構造方法開始看:

       通過以上可以看到,在創建ThreadPoolExecutor時,對傳入的參數是有要求的:corePoolSize不能小於0;maximumPoolSize需要大於0,且需要大於等於corePoolSize;keepAliveTime大於0;workQueue、threadFactory都不能為null。
       在創建完後就需要執行Runnable了,看以下execute()方法:

       在execute()內部主要執行的邏輯如下:
       分析點1:如果當前線程數未超過核心線程數,則將runnable作為參數執行addWorker(),true表示核心線程,false表示非核心線程;
       分析點2:核心線程滿了,如果線程池處於運行狀態則往workQueue隊列中添加任務,接下來判斷是否需要拒絕或者執行addWorker();
       分析點3:以上都不滿足時 [corePoolSize=0且沒有運行的線程,或workQueue已經滿了] ,執行addWorker()添加runnable,失敗則執行拒絕策略;
        總結一下:線程池對線程創建的管理,流程圖如下:

       在執行addWorker時,主要做了以下兩件事:
       分析點1:將runnable作為參數創建Worker對象w,然後獲取w內部的變數thread;
       分析點2:調用start()來啟動thread;
       在addWorker()內部會將runnable作為參數傳給Worker,然後從Worker內部讀取變數thread,看一下Worker類的實現:

       Worker實現了Runnable介面,在Worker內部,進行了賦值及創建操作,先將execute()時傳入的runnable賦值給內部變數firstTask,然後通過ThreadFactory.newThread(this)創建Thread,上面講到在addWorker內部執行t.start()後,會執行到Worker內部的run()方法,接著會執行runWorker(this),一起看一下:

       前面可以看到,runWorker是執行在子線程內部,主要執行了三件事:
       分析1:獲取當前線程,當執行shutdown()時需要將線程interrupt(),接下來從Worker內部取到firstTask,即execute傳入的runnable,接下來會執行;
       分析2:while循環,task不空直接執行;否則執行getTask()去獲取,不為空直接執行;
       分析3:對有效的task執行run(),由於是在子線程中執行,因此直接run()即可,不需要start();
       前面看到,在while內部有執行getTask(),一起看一下:

       getTask()是從workQueue內部獲取接下來需要執行的runnable,內部主要做了兩件事:
       分析1:先獲取到當前正在執行工作的線程數量wc,通過判斷allowCoreThreadTimeOut[在創建ThreadPoolExecutor時可以進行設置]及wc > corePoolSize來確定timed值;
       分析2:通過timed值來決定執行poll()或者take(),如果WorkQueue中有未執行的線程時,兩者作用是相同的,立刻返回線程;如果WorkQueue中沒有線程時,poll()有超時返回,take()會一直阻塞;如果allowCoreThreadTimeOut為true,則核心線程在超時時間沒有使用的話,是需要退出的;wc > corePoolSize時,非核心線程在超時時間沒有使用的話,是需要退出的;
       allowCoreThreadTimeOut是可以通過以下方式進行設置的:

       如果沒有進行設置,那麼corePoolSize數量的核心線程會一直存在。
        總結一下:ThreadPoolExecutor內部的核心線程如何確保一直存在,不退出?
       上面分析已經回答了這個問題,每個線程在執行時會執行runWorker(),而在runWorker()內部有while()循環會判斷getTask(),在getTask()內部會對當前執行的線程數量及allowCoreThreadTimeOut進行實時判斷,如果工作數量大於corePoolSize且workQueue中沒有未執行的線程時,會執行poll()超時退出;如果工作數量不大於corePoolSize且workQueue中沒有未執行的線程時,會執行take()進行阻塞,確保有corePoolSize數量的線程阻塞在runWorker()內部的while()循環不退出。
       如果需要關閉線程池,需要如何操作呢,看一下shutdown()方法:

       以上可以看到,關閉線程池的原理:a. 遍歷線程池中的所有工作線程;b. 逐個調用線程的interrupt()中斷線程(註:無法響應中斷的任務可能永遠無法終止)
       也可調用shutdownNow()來關閉線程池,二者區別:
       shutdown():設置線程池的狀態為SHUTDOWN,然後中斷所有沒有正在執行任務的線程;
       shutdownNow():設置線程池的狀態為STOP,然後嘗試停止所有的正在執行或暫停任務的線程,並返回等待執行任務的列表;
       使用建議:一般調用shutdown()關閉線程池;若任務不一定要執行完,則調用shutdownNow();
        總結一下:ThreadPoolExecutor在執行execute()及shutdown()時的調用關系,流程圖如下:

       線程池可以通過Executors來進行不同類型的創建,具體分為四種不同的類型,如下:

       可緩存線程池:不固定線程數量,且支持最大為Integer.MAX_VALUE的線程數量:

       1、線程數無限制
       2、有空閑線程則復用空閑線程,若無空閑線程則新建線程
       3、一定程度上減少頻繁創建/銷毀線程,減少系統開銷

       固定線程數量的線程池:定長線程池

       1、可控制線程最大並發數(同時執行的線程數)
       2、超出的線程會在隊列中等待。

       單線程化的線程池:可以理解為線程數量為1的FixedThreadPool

       1、有且僅有一個工作線程執行任務
       2、所有任務按照指定順序執行,即遵循隊列的入隊出隊規則

       定時以指定周期循環執行任務

       一般來說,等待隊列 BlockingQueue 有: ArrayBlockingQueue 、 LinkedBlockingQueue 與 SynchronousQueue 。
       假設向線程池提交任務時,核心線程都被佔用的情況下:
        ArrayBlockingQueue :基於數組的阻塞隊列,初始化需要指定固定大小。
       當使用此隊列時,向線程池提交任務,會首先加入到等待隊列中,當等待隊列滿了之後,再次提交任務,嘗試加入隊列就會失敗,這時就會檢查如果當前線程池中的線程數未達到最大線程,則會新建線程執行新提交的任務。所以最終可能出現後提交的任務先執行,而先提交的任務一直在等待。
        LinkedBlockingQueue :基於鏈表實現的阻塞隊列,初始化可以指定大小,也可以不指定。
       當指定大小後,行為就和 ArrayBlockingQueue一致。而如果未指定大小,則會使用默認的 Integer.MAX_VALUE 作為隊列大小。這時候就會出現線程池的最大線程數參數無用,因為無論如何,向線程池提交任務加入等待隊列都會成功。最終意味著所有任務都是在核心線程執行。如果核心線程一直被占,那就一直等待。
        SynchronousQueue :無容量的隊列。
       使用此隊列意味著希望獲得最大並發量。因為無論如何,向線程池提交任務,往隊列提交任務都會失敗。而失敗後如果沒有空閑的非核心線程,就會檢查如果當前線程池中的線程數未達到最大線程,則會新建線程執行新提交的任務。完全沒有任何等待,唯一制約它的就是最大線程數的個數。因此一般配合Integer.MAX_VALUE就實現了真正的無等待。
       但是需要注意的是, 進程的內存是存在限制的,而每一個線程都需要分配一定的內存。所以線程並不能無限個。

C. Android 中如何停止一個線程

有三種方法可以結束線程:
1. 使用退出標志,使線程正常退出,也就是當run方法完成後線程終止
2. 使用interrupt()方法中斷線程
3. 使用stop方法強行終止線程(不推薦使用,可能發生不可預料的結果)
前兩種方法都可以實現線程的正常退出,也就是要談的優雅結束線程;第3種方法相當於電腦斷電關機一樣,是不安全的方法。

D. 安卓關閉線程 ANDROID 這樣的線程要怎麼stop()關閉掉。跪求解決!

線程執行結束後自動關閉,可以在線程中設置條件變數來使得線程自動關閉。

E. android 啟動一個線程後 怎麼關閉 並釋放內存

1、線程裡面的代碼運行完,就Game Over了。
2、如果線程中的代碼是個死循環。可使用標識來確定是否然代碼運行完
在子線程中運行代碼如下
boolean flag = true;
while(flag){
//正常運行的代碼
...
...
//更改flag標識的更改情況,false:跳出循環,線程運行完畢結束,true繼續循環
....
}

F. 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 整體的實現流程就這些了,源碼是最好的老師,自己跟著源碼走一遍有些問題可能就豁然開朗了!

G. android 強行關閉線程

這個好辦,這里有兩個線程吧,線程A 線程mSender。
設:讓線程A,監視線程mSender
第一步:
在A中定義一個變數:ExecutorService transThread = Executors.newSingleThreadExecutor();
解釋:transThread 官方名稱是「單一線程池變數」,他是做什麼的呢,它就像我們給A的一塊賽車場,用來玩遙控賽車。
第二步:
在A中再定義一個變數: Future transPending;

解釋:transPending就像我給A一個用來控制遙控賽車的遙控器一樣
第三步:
在A中定義個線程mSender對象:
private final Runnable mSender = new Runnable() {

public void run() {
}};
解釋:這是你寫的對象,他就相當於我給A的一個遙控賽車。
第四步:
在A中添加如下語句:transPending = transThread.submit(mSender);

解釋:這一步就相當於把賽車(mSender)放入場地(第一步中我們定義的transThread),並且用遙控器「transPending」來控制他。
(註:這里你是否會有疑惑,我沒有寫"mSender.start()"之類的語句怎麼讓我的賽車跑起來啊(運行run()方法),放心,有了「transPending = transThread.submit(mSender);」這一步,賽車是自動跑起來的,也就是mSender會自動調用run的。)

第五步:
現在你可以用遙控器「transPending」干很多事情了
transPending.cancel(true);無論線程現在是否運行中,立刻終止。
transPending.cancel(false);當前線程若沒有運行(比如掛起狀態)就終止它。
transPending所做的不止這些。

H. Android中的線程狀態 - AsyncTask詳解

在操作系統中,線程是操作系統調度的最小單元,同時線程又是一種受限的系統資源,即線程不可能無限制地產生,並且 線程的創建和銷毀都會有相應的開銷。 當系統中存在大量的線程時,系統會通過會時間片輪轉的方式調度每個線程,因此線程不可能做到絕對的並行。

如果在一個進程中頻繁地創建和銷毀線程,顯然不是高效的做法。正確的做法是採用線程池,一個線程池中會緩存一定數量的線程,通過線程池就可以避免因為頻繁創建和銷毀線程所帶來的系統開銷。

AsyncTask是一個抽象類,它是由Android封裝的一個輕量級非同步類(輕量體現在使用方便、代碼簡潔),它可以在線程池中執行後台任務,然後把執行的進度和最終結果傳遞給主線程並在主線程中更新UI。

AsyncTask的內部封裝了 兩個線程池 (SerialExecutor和THREAD_POOL_EXECUTOR)和 一個Handler (InternalHandler)。

其中 SerialExecutor線程池用於任務的排隊,讓需要執行的多個耗時任務,按順序排列 THREAD_POOL_EXECUTOR線程池才真正地執行任務 InternalHandler用於從工作線程切換到主線程

1.AsyncTask的泛型參數

AsyncTask是一個抽象泛型類。

其中,三個泛型類型參數的含義如下:

Params: 開始非同步任務執行時傳入的參數類型;

Progress: 非同步任務執行過程中,返回下載進度值的類型;

Result: 非同步任務執行完成後,返回的結果類型;

如果AsyncTask確定不需要傳遞具體參數,那麼這三個泛型參數可以用Void來代替。

有了這三個參數類型之後,也就控制了這個AsyncTask子類各個階段的返回類型,如果有不同業務,我們就需要再另寫一個AsyncTask的子類進行處理。

2.AsyncTask的核心方法

onPreExecute()

這個方法會在 後台任務開始執行之間調用,在主線程執行。 用於進行一些界面上的初始化操作,比如顯示一個進度條對話框等。

doInBackground(Params...)

這個方法中的所有代碼都會 在子線程中運行,我們應該在這里去處理所有的耗時任務。

任務一旦完成就可以通過return語句來將任務的執行結果進行返回,如果AsyncTask的第三個泛型參數指定的是Void,就可以不返回任務執行結果。 注意,在這個方法中是不可以進行UI操作的,如果需要更新UI元素,比如說反饋當前任務的執行進度,可以調用publishProgress(Progress...)方法來完成。

onProgressUpdate(Progress...)

當在後台任務中調用了publishProgress(Progress...)方法後,這個方法就很快會被調用,方法中攜帶的參數就是在後台任務中傳遞過來的。 在這個方法中可以對UI進行操作,在主線程中進行,利用參數中的數值就可以對界面元素進行相應的更新。

onPostExecute(Result)

當doInBackground(Params...)執行完畢並通過return語句進行返回時,這個方法就很快會被調用。返回的數據會作為參數傳遞到此方法中, 可以利用返回的數據來進行一些UI操作,在主線程中進行,比如說提醒任務執行的結果,以及關閉掉進度條對話框等。

上面幾個方法的調用順序:

onPreExecute() --> doInBackground() --> publishProgress() --> onProgressUpdate() --> onPostExecute()

如果不需要執行更新進度則為onPreExecute() --> doInBackground() --> onPostExecute(),

除了上面四個方法,AsyncTask還提供了onCancelled()方法, 它同樣在主線程中執行,當非同步任務取消時,onCancelled()會被調用,這個時候onPostExecute()則不會被調用 ,但是要注意的是, AsyncTask中的cancel()方法並不是真正去取消任務,只是設置這個任務為取消狀態,我們需要在doInBackground()判斷終止任務。就好比想要終止一個線程,調用interrupt()方法,只是進行標記為中斷,需要在線程內部進行標記判斷然後中斷線程。

3.AsyncTask的簡單使用

這里我們模擬了一個下載任務,在doInBackground()方法中去執行具體的下載邏輯,在onProgressUpdate()方法中顯示當前的下載進度,在onPostExecute()方法中來提示任務的執行結果。如果想要啟動這個任務,只需要簡單地調用以下代碼即可:

4.使用AsyncTask的注意事項

①非同步任務的實例必須在UI線程中創建,即AsyncTask對象必須在UI線程中創建。

②execute(Params... params)方法必須在UI線程中調用。

③不要手動調用onPreExecute(),doInBackground(Params... params),onProgressUpdate(Progress... values),onPostExecute(Result result)這幾個方法。

④不能在doInBackground(Params... params)中更改UI組件的信息。

⑤一個任務實例只能執行一次,如果執行第二次將會拋出異常。

先從初始化一個AsyncTask時,調用的構造函數開始分析。

這段代碼雖然看起來有點長,但實際上並沒有任何具體的邏輯會得到執行,只是初始化了兩個變數,mWorker和mFuture,並在初始化mFuture的時候將mWorker作為參數傳入。mWorker是一個Callable對象,mFuture是一個FutureTask對象,這兩個變數會暫時保存在內存中,稍後才會用到它們。 FutureTask實現了Runnable介面,關於這部分內容可以看這篇文章。

mWorker中的call()方法執行了耗時操作,即result = doInBackground(mParams);,然後把執行得到的結果通過postResult(result);,傳遞給內部的Handler跳轉到主線程中。在這里這是實例化了兩個變數,並沒有開啟執行任務。

那麼mFuture對象是怎麼載入到線程池中,進行執行的呢?

接著如果想要啟動某一個任務,就需要調用該任務的execute()方法,因此現在我們來看一看execute()方法的源碼,如下所示:

調用了executeOnExecutor()方法,具體執行邏輯在這個方法裡面:

可以 看出,先執行了onPreExecute()方法,然後具體執行耗時任務是在exec.execute(mFuture),把構造函數中實例化的mFuture傳遞進去了。

exec具體是什麼?

從上面可以看出具體是sDefaultExecutor,再追溯看到是SerialExecutor類,具體源碼如下:

終於追溯到了調用了SerialExecutor 類的execute方法。SerialExecutor 是個靜態內部類,是所有實例化的AsyncTask對象公有的,SerialExecutor 內部維持了一個隊列,通過鎖使得該隊列保證AsyncTask中的任務是串列執行的,即多個任務需要一個個加到該隊列中,然後執行完隊列頭部的再執行下一個,以此類推。

在這個方法中,有兩個主要步驟。

①向隊列中加入一個新的任務,即之前實例化後的mFuture對象。

②調用 scheleNext()方法,調用THREAD_POOL_EXECUTOR執行隊列頭部的任務。

由此可見SerialExecutor 類僅僅為了保持任務執行是串列的,實際執行交給了THREAD_POOL_EXECUTOR。

THREAD_POOL_EXECUTOR又是什麼?

實際是個線程池,開啟了一定數量的核心線程和工作線程。然後調用線程池的execute()方法。執行具體的耗時任務,即開頭構造函數中mWorker中call()方法的內容。先執行完doInBackground()方法,又執行postResult()方法,下面看該方法的具體內容:

該方法向Handler對象發送了一個消息,下面具體看AsyncTask中實例化的Hanlder對象的源碼:

在InternalHandler 中,如果收到的消息是MESSAGE_POST_RESULT,即執行完了doInBackground()方法並傳遞結果,那麼就調用finish()方法。

如果任務已經取消了,回調onCancelled()方法,否則回調 onPostExecute()方法。

如果收到的消息是MESSAGE_POST_PROGRESS,回調onProgressUpdate()方法,更新進度。

InternalHandler是一個靜態類,為了能夠將執行環境切換到主線程,因此這個類必須在主線程中進行載入。所以變相要求AsyncTask的類必須在主線程中進行載入。

到此為止,從任務執行的開始到結束都從源碼分析完了。

AsyncTask的串列和並行

從上述源碼分析中分析得到,默認情況下AsyncTask的執行效果是串列的,因為有了SerialExecutor類來維持保證隊列的串列。如果想使用並行執行任務,那麼可以直接跳過SerialExecutor類,使用executeOnExecutor()來執行任務。

四、AsyncTask使用不當的後果

1.)生命周期

AsyncTask不與任何組件綁定生命周期,所以在Activity/或者Fragment中創建執行AsyncTask時,最好在Activity/Fragment的onDestory()調用 cancel(boolean);

2.)內存泄漏

3.) 結果丟失

屏幕旋轉或Activity在後台被系統殺掉等情況會導致Activity的重新創建,之前運行的AsyncTask(非靜態的內部類)會持有一個之前Activity的引用,這個引用已經無效,這時調用onPostExecute()再去更新界面將不再生效。

自己是從事了七年開發的Android工程師,不少人私下問我,2019年Android進階該怎麼學,方法有沒有?

沒錯,年初我花了一個多月的時間整理出來的學習資料,希望能幫助那些想進階提升Android開發,卻又不知道怎麼進階學習的朋友。【 包括高級UI、性能優化、架構師課程、NDK、Kotlin、混合式開發(ReactNative+Weex)、Flutter等架構技術資料 】,希望能幫助到您面試前的復習且找到一個好的工作,也節省大家在網上搜索資料的時間來學習。

閱讀全文

與android線程開啟關閉相關的資料

熱點內容
安卓儲物櫃怎麼取出貨物 瀏覽:903
文件夾中怎樣查找相似照片 瀏覽:968
linux中斷線程 瀏覽:567
古裝三極古裝劇有哪些 瀏覽:462
智能控制技術pdf 瀏覽:225
安卓手機如何設置微信來電 瀏覽:380
android撥打分機號 瀏覽:423
成都通宵私人電影院哪家好 瀏覽:566
iphone照片視頻加密 瀏覽:266
四叉樹碰撞檢測演算法邊界 瀏覽:484
電影票怎麼驗票真偽 瀏覽:901
我的世界伺服器如何生成地皮 瀏覽:407
app投資模式是什麼意思 瀏覽:231
kotlin編譯器插件 瀏覽:919
西安少兒編程培訓班 瀏覽:416
什麼畫動漫app不要錢 瀏覽:771
如何做一個免費伺服器 瀏覽:976
然後查找伺服器地址 瀏覽:409
clion破解Linux 瀏覽:750
加密貨幣制定法規 瀏覽:6