前言:
android線程通訊機制是android應用開發的基礎課程,對於很多初學android的朋友可能還沒有完全理解,所以,今天我就做一下知識小結吧。
一、安全執行緒
可能有java基本的朋友都知道什麼叫安全執行緒。
安全執行緒:如果你的代碼在所在的進程中有多個(兩個或兩個以上)的線程同時執行,若每次啟動並執行結果和使用單線程模式啟動並執行結果一致,並且變數的值也和預期的一樣,這樣就叫安全執行緒。或者說:一個類或者程式所提供的介面對於線程來說是原子操作或者多個線程之間的切換不會導致該介面的執行結果存在二義性,也就是說我們不用考慮同步的問題。
在android系統中,當一個android應用啟動的時候,會開啟一個Lliunx進程和主線程,這個主線程我們有稱UI線程。UI線程是負責顯示,處理UI以及相關事件的線程。
android的UI操作不是安全執行緒,就是說要UI線程才能處理UI控制項,而且當UI線程阻塞超過5秒的時候,系統會報出Force Close的強制關閉的提示。所以,如果要進行比較耗時的IO操作的時候,android系統就會出現假死的狀態,甚至出現FC提示,就如下載檔案、開啟大檔案等等。
二、線程間通訊實現
面對以上問題,我們為了讓應用在使用者面前展現更加流暢和平滑的效果(就是更好的使用者體驗),我們需要多線程操作,所以,我們要考慮的是,如果讓UI線程和子線程互動。
強大的google開發的android系統裡面已經提供了很優秀的線程通訊機制。
1、多線程任務開發可以通過以下幾個方式實現:
1.1、Handler+Message+Thread
1.2、HandlerThread
1.3、AsyncTask
2、如果子線程的資料想通知到UI線程中,可以一下的實現方法:
2.1、上述的三種方法
2.2、Activity.runOnUIThread(Runnable)
2.3、View.post(Runnable)
2.4、View.postDelayed(Runnable, long)
三、分析1、Handler+Message+Thread(Runnable)
Handler
1、發送訊息到訊息佇列
2、發送Runnable任務到訊息佇列
3、從訊息佇列中接受訊息並在當前線程處理訊息
4、從訊息佇列中接受Runnable任務並執行
5、按時間計劃(指定時間、延遲時間、每隔時間段)來執行任務或者處理訊息
Handler中分發訊息的一些方法
post(Runnable)
postAtTime(Runnable,long)
postDelayed(Runnable long)
sendEmptyMessage(int)
sendMessage(Message)
sendMessageAtTime(Message,long)
sendMessageDelayed(Message,long)
以上post類方法允許你排列一個Runnable對象到主線程隊列中,
sendMessage類方法, 允許你安排一個帶資料的Message對象到隊列中,等待更新.
Message
封裝了一系列的參數的訊息實體類
Thread(Runnable)
子線程
訊息通知簡單例子:
@Overrideprotected void onCreate(Bundle savedInstanceState) {// TODO Auto-generated method stubsuper.onCreate(savedInstanceState);/* 子線程執行耗時任務 */new Thread() {public void run() {// 執行耗時的任務try {Thread.currentThread().sleep(7000);} catch (Exception err) {}// 通過Handler獲得Message對象Message msg = new MyHandler().obtainMessage();msg.obj = "任務執行完畢";// 發送到Handler,在UI線程裡處理Messagemsg.sendToTarget();}}.start();}/* 重寫handleMessage方法,接受並處理Message訊息 */public class MyHandler extends Handler {public void handleMessage(Message msg) {// 處理接受到的MessageSystem.out.println("接受到訊息:" + msg.obj + ",並成功處理");}}
注意:不能在子線程操作UI線程中的UI控制項,因為之前也說過UI線程是線程不安全的,有的人可能操作了UI但是也沒有報錯。這是因為此時UI資源沒有被爭奪,沒有產生死結,但是也存在這個問題,所以要編寫高品質的代碼就要編寫健壯性強的代碼。所以只能在Handler裡面來操作UI。
如果使用post(Runnable)、postAtTime(Runnable,long)、postDelayed(Runnable long)方法,裡面的Runnable可以對UI操作,因為最後UI線程接受到Runnable的任務時,是直接執行run()方法,所以本質上在UI線程執行任務。
2、HandlerThread
Handy class for starting a new thread that has a looper. The looper can then be used to create handler classes. Note that start() must still be called.
1、本身繼承Thread,就是一個子線程
2、和Handler+Message+Thread的區別是:在Handler類裡面處理Message訊息可以是耗時操作,但是不能對UI操作。就是說把handleMessage方法在子線程執行了,而不是在UI線程執行,所以這個工具類應用不廣,只是多執行緒接受到的Message對象。
下面是一個簡單的例子:
public void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);HandlerThread hThread = new HandlerThread("myThread");hThread.start();myhandler myhandler = new myhandler(hThread.getLooper());Message msg = myhandler.obtainMessage();msg.sendToTarget();// 把 Message發送到目標對象,目標對象就是產生msg的目標對象。}class myhandler extends Handler {//必須調用Handler(Looper looper)方法public myhandler(Looper looper) {super(looper);}public void handleMessage(Message msg) {Log.e("這是新線程", "》》》》》》》》》》》》》》》》》新線程的測試");}}
3、AsyncTask
官方的描述(這個我就不翻譯了):
AsyncTask enables proper and easy use of the UI thread. This class allows to perform background operations and publish results on the UI thread without having to manipulate threads and/or handlers.
為瞭解決多線程執行任務的問題,Android 1.5提供了一個工具類:AsyncTask,它使建立需要與使用者介面互動的長時間啟動並執行任務變得更簡單。相對來說AsyncTask更輕量級一些,適用於簡單的非同步處理,不需要藉助線程和Handler即可實現。其實說白了,這個工具類只是對線程和Handler的一個封裝,具體內部如何?我將會在提高篇解析。
這個工具類使用很簡單主要有以下四個步驟:
1、繼承或者實現AsyncTask類(因為是abstract 修飾的抽象類別)
2、實現以下方法
(1)onPreExecute(), 該方法將在執行實際的後台操作前被UI thread調用。可以在該方法中做一些準備工作,如在介面上顯示一個進度條。
(2)doInBackground(Params...), 將在onPreExecute 方法執行後馬上執行,該方法運行在後台線程中。這裡將主要負責執行那些很耗時的後台計算工作。可以調用 publishProgress方法來更新即時的任務進度。該方法是抽象方法,子類必須實現。
(3)onProgressUpdate(Progress...),在publishProgress方法被調用後,UI thread將調用這個方法從而在介面上展示任務的進展情況,例如通過一個進度條進行展示。
(4)onPostExecute(Result), 在doInBackground 執行完成後,onPostExecute 方法將被UI thread調用,背景計算結果將通過該方法傳遞到UI thread.
3、在UI線程中執行個體化定義好的AsyncTask或者其子類
4、調用execute(Params...)開始執行任務
為了正確使用AsyncTask類,必須遵循一下準則:
1、AsyncTask執行個體必須在UI線程中建立
2、execute方法必須在UI線程中調用
3、不允許手動調用onPreExecute(), onPostExecute(Result),doInBackground(Params...), onProgressUpdate(Progress...)這幾個方法
4、AsyncTask的execute(Params...)執行方法只能執行一次,就是一個執行個體只能執行一次,執行多次會出現異常。需要說明AsyncTask不能完全取代線程,在一些邏輯較為複雜或者需要在後台反覆執行的邏輯就可能需要線程來實現了。
下面有個簡單的例子:
public void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);//執行個體化AsyncTaskAsyncTask task = new DownloadFilesTask();//開始執行任務try {task.execute(new URL("www.hclab.cn"));} catch (MalformedURLException e) {e.printStackTrace();}}/*一個載入URL的非同步任務*/private 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"); } }
四、總結
這篇博文只是對android多執行緒任務和子線程和UI線程的通訊實現方法做了簡單的介紹。所以說,只是適合新手過目,這個也是android應用開發的基本功吧。通常學習某方面的知識的時候不要只局限於表面,聰明的人可能會問,為什麼會這樣實現的?為什麼一定要按照的步驟實現?為什麼會有這麼多中實現方法,內部會不會是一樣的實現原理呢?等等。
對,其實我想告訴初學者的一點學習建議(個人看法,適合自己的學習方法才是最重要):學習android api的時候,首先要學會用,用熟了,再去研究其源碼或者內部底層實現原理。如果一下子就告訴你這個類的實現原理,可能會混淆你的總體邏輯思想,會越來越亂,連這個類到底是可以做什麼都糊塗了。我個人就是這樣,在使用新知識的時候,多問問為什麼,多想想方方面面,帶著問題,再去源碼或者實現原理找答案。當你弄懂了,會感覺很爽,哈哈。這樣的階層更加清晰。
所以,這就是我分基礎篇(使用方法)和提高篇(內部原理解析)的原因了(提高篇之後會出)。