當前位置:首頁 » 安卓系統 » android開線程

android開線程

發布時間: 2023-05-29 07:06:08

A. Android線程啟動start()和run()的區別

         在java中有兩種啟動線程的方法,一種是start()方法,而另外一種是run()方法,但是在安卓開發中,用run()方法可能會出現一些問題,所以本文做以下區別:

1,run()方法,開啟線程,實際還是在當前線程運行,線程的執行的順序,按照程序的順序執行,實際上是沒有意義的,比如在主線程中請求網路,如果用run()方法,會阻塞主線程,導致界面沒有反應.

2,start()方法,只有執行了start()方法線程會執行,這個方法是真正意義上的多線程,不會阻塞主線程,是開啟另一個線程執行操作.

B. Android線程池的使用

在Android中有主線程和子線程的區分。主線程又稱為UI線程,主要是處理一些和界面相關的事情,而子線程主要是用於處理一些耗時比較大的一些任務,例如一些網路操作,IO請求等。如果在主線程中處理這些耗時的任務,則有可能會出現ANR現象(App直接卡死)。

線程池,從名字的表明含義上我們知道線程池就是包含線程的一個池子,它起到新建線程、管理線程、調度線程等作用。

既然Android中已經有了線程的概念,那麼為什麼需要使用線程池呢?我們從兩個方面給出使用線程池的原因。

在Android中線程池就是ThreadPoolExecutor對象。我們先來看一下ThreadPoolExecutor的構造函數。

我們分別說一下當前的幾個參數的含義:
第一個參數corePoolSize為 核心線程數 ,也就是說線程池中至少有這么多的線程,即使存在的這些線程沒有執行任務。但是有一個例外就是,如果在線程池中設置了allowCoreThreadTimeOut為true,那麼在 超時時間(keepAliveTime) 到達後核心線程也會被銷毀。
第二個參數maximumPoolSize為 線程池中的最大線程數 。當活動線程數達到這個數後,後續添加的新任務會被阻塞。
第三個參數keepAliveTime為 線程的保活時間 ,就是說如果線程池中有多於核心線程數的線程,那麼在線程沒有任務的那一刻起開始計時,如果超過了keepAliveTime,還沒有新的任務過來,則該線程就要被銷毀。同時如果設置了allowCoreThreadTimeOut為true,該時間也就是上面第一條所說的 超時時間
第四個參數unit為 第三個參數的計時單位 ,有毫秒、秒等。
第五個參數workQueue為 線程池中的任務隊列 ,該隊列持有由execute方法傳遞過來的Runnable對象(Runnable對象就是一個任務)。這個任務隊列的類型是BlockQueue類型,也就是阻塞隊列,當隊列的任務數為0時,取任務的操作會被阻塞;當隊列的任務數滿了(活動線程達到了最大線程數),添加操作就會阻塞。
第六個參數threadFactory為 線程工廠 ,當線程池需要創建一個新線程時,使用線程工廠來給線程池提供一個線程。
第七個參數handler為 拒絕策略 ,當線程池使用有界隊列時(也就是第五個參數),如果隊列滿了,任務添加到線程池的時候的一個拒絕策略。

可以看到FixedThreadPool的構建調用了ThreadPoolExecutor的構造函數。從上面的調用中可以看出FixedThreadPool的幾個特點:

可以看到CacheThreadPool的構建調用了ThreadPoolExecutor的構造函數。從上面的調用中可以看出CacheThreadPool的幾個特點:

可以看到ScheledThreadPoolExecutor的構建調用了ThreadPoolExecutor的構造函數。從上面的調用中可以看出ScheledThreadPoolExecutor的幾個特點:

可以看到SingleThreadExecutor的構建調用了ThreadPoolExecutor的構造函數。從上面的調用中可以看出SingleThreadExecutor的幾個特點:

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

D. Android中的線程和線程池

一、除了Thread外,扮演線程角色的還有:AsyncTask和IntentService,同時HandlerThread也扮演特殊的線程。

      IntentService:內部採用HandlerThread來執行,像一個後台線程,同時是一個服務,不容易被系統殺死。

二、HandlerThread的run方法是一個無限循環

三、IntentService中任務是排隊執行的

四、AsyncTask 

1、Android1.6之前串悄段桐行執行任務,1.6時候採用線程池裡的並行,Android3.0開始又開始串列(為了避免並發錯誤),單任可以並行。

2、AsyncTask必須在UI線程調用(不過這個不是絕對的,和版本有關燃腔系,API 16之前,API 16到 22, API 22以後) 參考一

原因:內部有靜態Handler,採用UI線程的Looper來處理消息,這就是為什麼AsyncTask必須在UI線程調用,因為子線程默認沒有Looper無法創建下面的Handler,程序會直接Crash

3、AsyncTask中有兩個線程池和一個Handler,一個線程池用啟坦於任務排隊,一個線程池用於真正的執行任務,InternalHandler用於將

執行環境從線程池切換到主線程

AsyncTask串列與並行

五、線程池

線程池中多餘的線程是如何回收的

E. android創建子線程

你是要怎麼個意思?
創建子線程即就是在你的程序體(也就是MainThread)中創建子線程:
1、匿名內部類對Thread類進行覆寫:new
Thread(){
覆寫run方法
}.start();
2、或者
new
Thread(new
Runnable(){········}).start();套用兩層匿名內部類也可以
3、再或者就是將當前類繼承Runnable然後在當前類里覆寫上run方法,最後在需要開線程的地方寫上new
Thread(this).start();
####別忘了在更新UI的時候跳回主線程就行了,用runOnUiThread
和handle機制都可以####

F. android創建子線程

創建後台線程的方法有多種,這里說三種,可以回去試試

1、使用Android系統工具類 AsyncTask(Params,Progress,Result)
AsyncTask是一個輕量級線程,三個泛型參數分別是 Params傳入參數,int型Progress為進度條進度,Result為返回值
要使用AsyncTask,必須繼承之並復寫其中的幾個重要的函數。
onPreExecute(), 該方法將在執行實際的後台操作前被UI thread調用。可以在該方法中做一些准備工作,如在界面上顯示一個進度條。
doInBackground(Params...), 將在onPreExecute 方法執行後馬上執行,該方法運行在後台線程中。這里將主要負責執行那些很耗時的後台計算工作。可以調用 publishProgress方法來更新實時的任務進度。該方法是抽象方法,子類必須實現。
onProgressUpdate(Progress...),在publishProgress方法被調用後,UI thread將調用這個方法從而在界面上展示任務的進展情況,例如通過一個進度條進行展示。
onPostExecute(Result), 在doInBackground 執行完成後,onPostExecute 方法將被UI thread調用,後台的計算結果將通過該方法傳遞到UI thread.

註:Task必須在UI線程中創建,並調用並且只能調用一次execute方法,該方法的參數為傳入的泛型Params。
其餘函數最好不要手動調用,容易造成線程崩潰。多次調用Task,容易造成線程池溢出。

2、使用Handler和HandlerThread
誤區: Handler handler = new Handler ();
handler.post(r);
這種做法看似創建了一個線程,但實際上handler只是直接調用Runnable中的run() 方法,而不執行線程的start()函數,所以這兩句代碼執行後,程序仍然在UI線程中執行。所以我們引入HandlerThread,因為HandlerThread中有一個Looper對象,用以循環消息隊列。
為了使用Looper,必須子類化Handler,並復寫它的構造函數。
class MyHandler extends Handler{
public MyHandler() {}
public MyHandler(Looper looper){
super (looper);
}

public void handleMessage(Message msg){
//....這里運行耗時的過程
}
}
}
handleMessage(Message msg)函數用以接收消息,msg則是從UI線程中發出的消息,可以傳遞各種對象,根據這些對象和數值進行操作。
有了Handler子類,則可以在UI線程中進行創建和初始化
HandlerThread handlerThread = new HandlerThread( "backgroundThread" );
handlerThread.start();
MyHandler myHandler = new MyHandler(handlerThread.getLooper());
Message msg = myHandler.obtainMessage();
//....此處對msg進行賦值,可以創建一個Bundle進行承載
msg.sendToTarget();
之後如果需要調用線程,只要對handler傳入msg,就可以執行相應的過程了
最後,很重要的一點,HandlerThread 不隨Activity的生命周期結束而消亡,所以必須復寫Ondestory(),調用HandlerThread .stop()

3、使用線程同步 synchronized、 wait()、 notify()
使用線程同步機制synchronized實現多線程操作,相對來說比較復雜,但也是靈活性最佳、效率最高的一種做法,在產品開發當中也使用得最廣。本人水平相當有限,也只學得一點皮毛。
synchronized相當於一把鎖,當線程運行的時候,會同時有幾個線程訪問一個對象,對其進行操作或者修改。這可能引起很不良的後果(例如改變判定條件,或者在別的線程還沒使用完的時候對象已經被析構等等),所以必須對一些對象進行加鎖,保證它在同一時間只允許被一個線程訪問。
synchronized使用方法有兩種:
<1> 同步方法在方法名前加入synchronized關鍵字,則該方法在同一時間內只允許一個線程訪問它,其代碼邏輯較為簡單,但使用起來不太靈活,並且大大降低效率,耗時太長的同步方法甚至會使得多線程失去原本的意義成為單線程
<2>同步參數 對某一個代碼塊加鎖,並且以synchronized(obj)的方式,表明該代碼塊內的obj對象是線程同步的。這個做法使得代碼靈活性大大加強,縮小同步代碼塊的范圍,並且可以在不同的函數體和類內進行同步,唯一遺憾的是實現起來比較復雜,容易產生一些問題

而wait()和notify(),必須在synchronized鎖的代碼塊內執行,否則系統將會報錯。
有了以上基礎,就可以很容易的創建後台線程了

Private Runnable backgroundRunnable = new Runnable () {
@Override
public void run() {
if(isFinish){
//.....
break;
}
for(;;){
synchronized(this){
//....寫耗時過程
wait();
}
}
}
}

UI線程中,就是一般的創建線程過程了
Thread backgroundThread = new Thread (backgroundRunnable);
backgroundThread.start();
這樣,在後台線程中會不斷的循環,當執行完一次過程以後就會wait()休眠。然後在OnTouchListener或者OnClickListener內相應的位置執行
synchronized(backgroundRunnable){
backgroundRunnable.notify();
}
當用戶觸摸屏幕產生一個event的時候,線程就會被喚醒,執行下一次循環。
最後,還是內存安全問題,必須復寫Activity中的OnDestory()方法,將標志量isFinish設為false,並且backgroundThread .stop()

G. android 最多能開多少線程最好開幾個

android系統對應用程序資源的限輪核制僅僅是以進程為單位的,你的這個問題可以轉化為:一個進程最多可以開多少線程?最臘鍵掘好開幾個?

其實這個沒有上限的,因為資源都限制在這個進程里,你開多少線程都亮芹最多用這些資源。至於開多少最好,完全取決你的需求,合理開線程,不卡,高效是最終目標。

熱點內容
飯團文件夾 發布:2024-05-20 10:56:18 瀏覽:574
win10系統重置密碼是多少 發布:2024-05-20 10:47:51 瀏覽:991
java加包 發布:2024-05-20 10:39:12 瀏覽:713
網吧里的電影怎麼傳伺服器 發布:2024-05-20 10:31:12 瀏覽:809
新年解壓糖 發布:2024-05-20 09:50:55 瀏覽:55
以太坊價值在哪裡存儲 發布:2024-05-20 09:46:34 瀏覽:642
cgipython配置 發布:2024-05-20 09:29:06 瀏覽:865
在我的世界伺服器中隱身 發布:2024-05-20 09:07:46 瀏覽:972
加西貝拉壓縮機好嗎 發布:2024-05-20 08:58:56 瀏覽:757
eve腳本航 發布:2024-05-20 08:56:59 瀏覽:591