在上一個例子中,最終我們發現,其實用到的線程只有一個,那就是程式的主線程(UI線程)。那麼怎麼把那個例子改成用建立的線程來實現呢,今天我嘗試了一下,寫了下面這個小程式。
當然,首先要聲明一下,今天的這個例子並不是推薦的寫法,而是我為了學習多線程而寫的例子(貌似更常用的是AsyncTask,而不是Thread和Handler去更新UI)。
在今天的這個例子中,我用到了Looper,先說說Looper是什麼
在API中是這麼解釋Lopper的:Class used to run a message loop for a thread。我的理解是Looper是用來控制message queue的類
Looper常用的幾個用法有:
Looper.prepare() 安卓的主線程中會預設調用這個方法來建立訊息佇列。但是,如果我們自己建立的線程,如果需要訊息佇列,則需要手動調用這個方法。
Looper.loop() 這個方法用在prepare()方法之後,調用該方法之後,進入訊息迴圈。
Looper.getMainLooper() 我在代碼中用到了這個方法,這個方法的作用是獲得主線程的Looper執行個體
Looper.myLooper() 這個方法用到擷取當前線程的Looper執行個體
今天這個例子和第二篇中實現的功能一下,我只不過改了一個寫法而已,下面是代碼:
package com.example.handler2;import android.app.Activity;import android.os.Bundle;import android.os.Handler;import android.os.Looper;import android.os.Message;import android.util.Log;import android.view.View;import android.view.View.OnClickListener;import android.widget.Button;import android.widget.ProgressBar;public class MainActivity extends Activity {Button startButton = null;Button stopButton = null;ProgressBar progressbar = null;Thread counter = null;//擷取主線程的looperLooper looper = Looper.getMainLooper();@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);startButton = (Button) findViewById(R.id.startButton);stopButton = (Button) findViewById(R.id.stopButton);progressbar = (ProgressBar) findViewById(R.id.progressBar);//為button綁定onclicklistenerstartButton.setOnClickListener(new ButtonOnclickListener());stopButton.setOnClickListener(new stopOnclickListener());}class ButtonOnclickListener implements OnClickListener{public void onClick(View v) {progressbar.setVisibility(View.VISIBLE);counter = new Thread(){int i = 1;@Overridepublic void run() {// TODO Auto-generated method stubi += 10;Message msg = handler.obtainMessage();msg.arg1 = i;//讓線程延遲一秒try {Thread.sleep(1000);} catch (Exception e) {e.printStackTrace();}Log.i("run", "run "+i+"%");Log.i("run", Thread.currentThread().getName());msg.sendToTarget(); }};counter.start();}}class stopOnclickListener implements OnClickListener{public void onClick(View v) {//從message queue 中去掉runhandler.removeCallbacks(counter);//讓progressbar置成隱藏progressbar.setVisibility(View.GONE);}}//將handler與主線程關聯Handler handler = new Handler(looper){public void handleMessage(android.os.Message msg) {int i =msg.arg1;//根據message中傳來的參數控制進度條progressbar.setProgress(i);Log.i("run", Thread.currentThread().getName());if(i<100){handler.post(counter);}else{handler.removeCallbacks(counter);progressbar.setVisibility(View.GONE);}};};}
這段代碼,和第二篇中最大的差別就是,當我點擊啟動按鈕後,調用的不是主線程的run,而是我建立的線程。
但是因為安卓不允許我們在主線程之外的線程中對UI進行修改,所以我在建立的線程中只是進行計數,然後將計數的結果通過message傳遞到主線程中,在主線程中更新進度條。
遺留問題:
本來這個例子到這裡就結束了,但是,我為了深入瞭解一下就在我建立的線程的run中和主線程Handler的handlerMessage方法中列印了當前線程的名稱:
結果如下:
按照我最初的理解,在日誌中應該是主線程和我的線程交替寫入日誌,但是實際的情況是在第一次是counter線程,後面列印的都是主線程
請問各位,有誰知道這是為什麼嗎?
我看到在API中Handler的post方法是這麼說明的:Causes the Runnable r to be added to the message queue. The runnable will be run on the thread to which this handler is attached.
難道是,我使用了post方法,就相當於把counter線程的run中的代碼拷貝中主線程中去執行了嗎?如果有誰知道麻煩為我解答一下,不甚感激!