標籤:
參考來源:郭霖.第一行代碼(Android) https://www.gitbook.com/book/hzj163/android-thread/details一.進程
進程是正在啟動並執行程式的執行個體,作業系統中資源分派和保護的基本單位
二.線程
線程是進程中能夠並發執行的實體,是進程的組成部分,也是處理器調度和指派的基本單位,一個進程可以同時包含多個線程,這些線程共用進程所獲得的記憶體空間和資源,可以為完成某一項任務而協同工作,提高完成任務的速度和效率
三.建立線程1. 繼承Thread類
建立
class TestThread extends Thread{ @Override public void run() { /*要在多線程裡啟動並執行代碼*/ }}
啟動
TestThread myThread=new TestThread;myThread.start();//調用繼承於Thread的start方法
2. 實現Runnable介面
使用繼承的方式耦合性有點高,而且只能繼承一個父類,而介面可以實現多個。所以我們可以使用介面的方式。
TestThread myTestThread=new TestThread();//產生一個Runnable介面實現對象Thread ti=new Thread(myTestThread);//將這個對象作為建構函式參數傳遞給Thread對象ti.start();
3. 使用匿名類
如過你不想麻煩的建立一個新類,可以使用匿名類的方法
new Thread(new Runnable() {@Overridepublic void run() {// 處理具體的邏輯 }}).start();
四.線程池//TODO
在物件導向編程中,建立和銷毀對象是很費時間的,因為建立一個對象要擷取記憶體資源或者其它更多資源。在Java中更是如此,虛擬機器將試圖跟蹤每一個對象,以便能夠在對象銷毀後進行記憶體回收。所以提高服務程式效率的一個手段就是儘可能減少建立和銷毀對象的次數,特別是一些很耗資源的對象建立和銷毀。如何利用已有對象來服務就是一個需要解決的關鍵問題,其實這就是一些"池化資源"技術產生的原因。
使用線程池減少了建立和銷毀線程的次數,每個背景工作執行緒都可以被重複利用,可執行多個任務。而且可以根據系統的承受能力,調整線程池中工作線線程的數目,防止因為消耗過多的記憶體,而把伺服器累趴下(每個線程需要大約1MB記憶體,線程開的越多,消耗的記憶體也就越大,最後死機)。
五.線程同步(安全執行緒)
多線程訪問時,採用了加鎖機制,當一個線程訪問該類的某個資料時,進行保護,其他線程不能進行訪問直到該線程讀取完,其他線程才可使用。保證資料在任何時刻,最多有一個線程訪問,以保證資料的完整性。
在Java裡面,通過synchronized關鍵字保證線程同步。
Java中的每一個對象都有一個內部鎖,如果一個方法用synchronized關鍵字聲明,那麼對象的鎖將保護整個方法。
public synchronized void method(){ // method body}
線程不安全
不提供資料訪問保護,有可能出現多個線程先後更改資料造成所得到的資料是髒資料
六.Android中的UI主線程
當應用啟動,系統會建立一個主線程(main thread)。這個主線程負責向UI組件分發事件(包括繪製事件),也是在這個主線程裡,你的應用和Android的UI組件發生互動。所以main thread也叫UI thread也即UI線程。
如果所有的工作都在UI線程,做一些比較耗時的工作比如訪問網路或者資料庫查詢,都會阻塞UI線程,導致事件停止分發(包括繪製事件)。對於使用者來說,應用看起來像是卡住了,更壞的情況是,如果UI線程blocked的時間太長(大約超過5秒),使用者就會看到ANR(application not responding)的對話方塊。所有這部份工作最後用非同步線程來完成。
Andoid UI toolkit並不是安全執行緒的,所以你不能從非UI線程來操縱UI組件。你必須把所有的UI操作放在UI線程裡,所有View和ViewGroup都只能在UI主線程中運行。如果View或者ViewGroup在次線程中運行,將會拋出【Only the original threadthat created a view hierarchy can touch its views】。
所以Android的單執行緒模式有兩條原則:
1. 不要阻塞UI線程。
2. 不要在UI線程之外訪問Android UI toolkit(主要是這兩個包中的組件:android.widget and android.view)。
七.線程和線程之間的通訊:Android訊息機制
但UI需要根據線程的執行情況來改變時(比如串連上網路後對UI內容載入),由於次線程不能直接修改UI,,它通過訊息機制告訴主線程中的UI什麼時候該修改。
Android中訊息機制由以下部分組成:
1. Message
Message是線上程之間傳遞的訊息,它可以在內部攜帶少量的資訊,用於在不同線程之間交換資料。
Message 使用 what欄位攜帶String類型,使用 arg1和 arg2欄位來攜帶一些整型資料,使用 obj欄位攜帶一個 Object對象。
2. Handler
Handler顧名思義也就是處理者的意思,它主要是用於發送和處理訊息的。發送訊息一般是使用 Handler的 sendMessage()方法,而發出的訊息經過一系列地處理後,最終會傳遞到 Handler的 handleMessage()方法中。
食用方法
首先需要在主線程當中建立一個 Handler 對象,並重寫handleMessage()方法。
然後當子線程中需要進行 UI 操作時,就建立一個 Message 對象,通過 Handler 將這條訊息發送出去。
之後這條訊息會被添加到 MessageQueue 的隊列中等待被處理,而 Looper 則會一直嘗試從 MessageQueue 中取出待處理訊息,最後分發回 Handler的 handleMessage()方法中。
由於 Handler 是在主線程中建立的,所以此時handleMessage()方法中的代碼也會在主線程中運行,於是我們在這裡就可以安心地進行 UI 操作了。
//在主線程中,建立Handler對象,重寫handleMessage方法 private Handler handler = new Handler() { @Override public void handleMessage(Message msg) { switch (msg.what) { case UPDATE_TEXT:// 在這裡可以進行UI 操作 text.setText("Nice to meet you"); break; default: break; } }//在子線程中new Thread(new Runnable() {@Overridepublic void run() { Message message = new Message();//建立Message對象 message.what = UPDATE_TEXT; handler.sendMessage(message); // 將Message 對象發送出去 } }).start();
3. MessageQueue
MessageQueue 是訊息佇列的意思,它主要用於存放所有通過 Handler 發送的訊息。這部分訊息會一直存在於訊息佇列中,等待被處理。每個線程中只會有一個 MessageQueue對象。
4. Looper
Looper 是每個線程中的 MessageQueue 的管家,調用 Looper 的 loop()方法後,就會進入到一個無限迴圈當中,然後每當發現MessageQueue 中存在一條訊息,就會將它取出,並傳遞到 Handler的 handleMessage()方法中。每個線程中也只會有一個 Looper 對象。
八.多線程非同步非同步方法呼叫A對方法B調用後,不用管B是否運行,A繼續運行。AsyncTask藉助 AsyncTask,即使你對非同步訊息處理機制完全不瞭解,也可以十分簡單地從子線程切換到主線程。當然,AsyncTask 背後的實現原理也是基於非同步訊息處理機制的,只是 Android 幫我們做了很好的封裝而已。
基本用法
AsyncTask 是一個抽象類別,所以如果我們想使用它,就必須要建立一個子類去繼承它。在繼承時我們可以為AsyncTask類指定三個泛型參數,這三個參數的用途如下。
1. Params
初始化參數類型
2. Progress
進度參數類型
3. Result
傳回值參數類型。
因此,一個最簡單的自訂 AsyncTask 就可以寫成如下方式:
class DownloadTask extends AsyncTask<Void, Integer, Boolean> { ……}
一個栗子
class DownloadTask extends AsyncTask<Void, Integer, Boolean> { //這個方法會在背景工作開始執行之前調用,用於進行一些介面上的初始化操作 @Override protected void onPreExecute() { progressDialog.show(); // 顯示進度對話方塊 } /*這個方法中的所有代碼都會在子線程中運行,我們應該在這裡去處理所有的耗時任務。 任務一旦完成就可以通過 return 語句來將任務的執行結果返回,如果 AsyncTask的第三個泛型參數指定的是 Void,就可以不返回任務執行結果。 注意,在這個方法中是不可以進行 UI操作,如果需要更新 UI元素,比如說反饋當前任務的執行進度,可以調用 publishProgress(Progress...)方法來完成。*/ @Override protected Boolean doInBackground(Void... params) {// try { while (true) { int downloadPercent = doDownload(); // 這是一個虛構的方法 publishProgress(downloadPercent); if (downloadPercent >= 100) { break; } } } catch (Exception e) { return false; } return true; } /* 當在背景工作中調用了 publishProgress(Progress...)方法後,這個方法就會很快被調用, 方法中攜帶的參數就是在背景工作中傳遞過來的。在這個方法中可以對 UI 進行操作,利用參數中的數值就可以對介面元素進行相應地更新。*/ @Override protected void onProgressUpdate(Integer... values) { // 在這裡更新下載進度 progressDialog.setMessage("Downloaded " + values[0] + "%"); } /* 當背景工作執行完畢並通過 return語句進行返回時,這個方法就很快會被調用。 返回的資料會作為參數傳遞到此方法中,可以利用返回的資料來進行一些 UI 操作,比如 說提醒任務執行的結果,以及關閉掉進度條對話方塊等。*/ @Override protected void onPostExecute(Boolean result) { progressDialog.dismiss(); // 關閉進度對話方塊// 在這裡提示下載結果 if (result) { Toast.makeText(context, "Download succeeded", Toast.LENGTH_SHORT).show(); } else { Toast.makeText(context, " Download failed", Toast.LENGTH_SHORT).show(); } }}
Android線程筆記