2010年11月19日 星期五

Android筆記_Thread的使用

Thread?Thread是什麼?之前沒有使用過,問問維基百科,他說:線程是計算機科學中的一個術語。指運行中的程序的調度單位。一個線程指的是進程中一個單一順序的控制流。也被稱為輕量進程(lightweight processes)。它是系統獨立調度和分派的基本單位。同一進程中的多個線程將共享該進程中的全部系統資源,比如文件描述符和信號處理等等。 一個進程可以有很多線程,每個線程并行執行不同的任務。

所以意思就是當主程式在進行時,同時又需要去執行別的任務,就可以另起一個Thread(線程),將要執行的內容在背後運作,等到執行完成後,再把結果秀出來。

而在網路上有提到於Android於Thread有一個重要的限制「Android為了考量安全性和執行效能,不允許副執行緒直接更改主畫面的資料」,也就是說在我們另起的Thread都沒辦法做出如:將畫面變色與改變文字內容等畫面上的行為,而解決方法就是利用"Handler"來做溝通的橋樑。所以整個流程是:
1. 主執行緒開始執行。
2. 需要Thread(副執行緒)運行比較久的程序,開始執行Thread。
3. Thread於背景執行(通常附帶時間控管,如Thread.sleep(1000);)
4. Thread完成,傳送訊息告知Handler。
5. 執行Handler內的邏輯程式,改變畫面內容。



接下來就實作Android課程的作業,題目是:在畫面上安排一個文字方塊(TextView)與按鈕(Button),當按下按鈕後,會進行倒數,並且將按鈕設定為不可按(setEnabled(false)),最後倒數結束後恢復按鈕,並且顯示"時間到"。

老師提示這個功能要利用Thread來進行,所以最後完成的程式流程是:
1. 定義全域變數。
2. 在onCreate()內,註冊按鈕按下的監聽(btn0.setOnClickListener)。
3. 在onClick()中,執行副執行緒(thread.start();),並將按鈕設定為不可按(btn0.setEnabled(false);)。
4. 執行thread的run()函式。

5. run()函式執行完畢後,送出訊息(handler.sendEmptyMessage),主程序若有Handler,則會攔截到該訊息(可於Handler內由Message判斷哪一個Thread送出,以執行不同的內容)。
6. 進行時間倒數的邏輯程式,並將倒數結果放置到畫面中(lab1.setText)。
7. 若是尚未倒數到零,則再次執行thread,若小於零則將按鈕改為可按,並顯示"時間到"。


在第4步驟發送訊息也可以定義Message的what來傳送:
Message message=new Message();
message.what=1;
handler.sendMessage(message);//發送消息

最後看一下執行畫面:

完整程式碼:

package ola.OlaTeach_RunThread;
import android.app.Activity;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.TextView;

public class OlaTeach_RunThread extends Activity implements Runnable {
/** Called when the activity is first created. */

public int Count_i;
Thread thread;
Button btn0;

@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);

OlaTeach_RunThread.this.setContentView(R.layout.main);
btn0 = (Button) OlaTeach_RunThread.this.findViewById(R.id.btn1);

btn0.setOnClickListener(new Button.OnClickListener(){
@Override
public void onClick(View v) {
btn0.setEnabled(false);
EditText txt1 = (EditText) OlaTeach_RunThread.this.findViewById(R.id.txt1);
Count_i=Integer.parseInt(txt1.getText().toString());
thread = new Thread(OlaTeach_RunThread.this);
thread.start();
}
});
}

private Handler handler = new Handler(){

@Override
public void handleMessage(Message msg) {
// TODO Auto-generated method stub
super.handleMessage(msg);
TextView lab1 = (TextView) OlaTeach_RunThread.this.findViewById(R.id.lab1);
Count_i--;
lab1.setText(String.valueOf(Count_i));
Button btn0 = (Button) OlaTeach_RunThread.this.findViewById(R.id.btn1);

if (Count_i<=0)
{
lab1.setText("時間到");
btn0.setEnabled(true);
}
else
{
thread = new Thread(OlaTeach_RunThread.this);
thread.start();
}
}
};

@Override
public void run() {
// TODO Auto-generated method stub
try {
Thread.sleep(1000);

Message message=new Message();
message.what=1;
handler.sendMessage(message);//發送消息


//handler.sendEmptyMessage(0);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}

14 則留言:

匿名 提到...

請問有這個範例的 layout xml檔案嗎? (謝謝)
by Android 初學者

ola的家 提到...

只有擺三個簡單的東西,檔案如下:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
>
<EditText
android:text="10"
android:id="@+id/txt1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
/>

<Button
android:text="開始倒數"
android:id="@+id/btn1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
/>

<TextView
android:id="@+id/lab1"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="@string/hello"
android:textSize="20sp"
/>


</LinearLayout>

匿名 提到...

好用心 :D

ola的家 提到...

:D

匿名 提到...

OLA大大您好~想要請問一下 一開始的thread.start()
就會跳到run
然後每次run玩都會跑去 handleMessag 檢查一次 然後檢查完又再
thread.start() 跑run 這樣 請問我說的有沒有錯???

然後請問是只要thread.start() 都會跑下面public的函式嗎??
(ex 假如我下面有兩個public得函是它就都會跑兩個這樣??)

謝謝!!

ola的家 提到...

以這個例子來說,就類似你說的:於onCreate的thread.start();觸發run函式,因為於run中撰寫handler.sendMessage(message);,所以觸發handleMessage函式,再經由判斷是決定是否要再執行一次thread.start();來觸發run。

至於第二個問題,就要看你怎麼指定thread,在這個例子裡建構thread是寫thread = new Thread(OlaTeach_RunThread.this);,表示run要實做在OlaTeach_RunThread.this這個上面,所以當被觸發thread.start();時,就會執行在OlaTeach_RunThread.this上的run函式。

但實際在操作的時候,可能在一個activity上面要觸發很多個thread,那麼你可以這樣寫:

Thread thread = new Thread(){
@Override
public void run() {
//要做的事情與要執行的Handler.sendMessage
}

};
thread.start();
thread = null;
-----------------
也就是new一個thread起來後,直接複寫run這個方法,並不實做在別的地方,這樣可能會更好操作。

匿名 提到...

非常感謝您的回答!! 但是我還是有一個小小的疑問:

thread = new Thread(OlaTeach_RunThread.this)
和您下面回答舉的例子
Thread thread = new Thread(){
xxxx
}
是不是上面得會整個重新執行OlaTeach_RunThread
而下面的thread只會執行{xxxx} ?

還有另一個疑問:
因為您onCreate裡的程式在 thread.start()就沒了
但是如果假設在onCreate裡thread.start()後面還有一些指令A

則是不是會同時執行: thread執行run 然後原本的程式執行剩下的(thread.start()後面的)指令A

還是說thread也會執行指令A 然後再執行 run ???

真的很感謝您 (我是android新手很多都不懂麻煩您了~)

ola的家 提到...

先說第二題,你可能要看一下thread的概念,當activity啟動時,會有一個thread來做這件事情,但是當事情都在同一個thread中處理時,大家就會去排隊,一件處理完才會處理下一件,這時候比較好的方法是,將比較需要花時間的事情開另一個thread來處理,所以就執行程序來說,thread.start()執行後新的thread就會開始執行,而原本在thread.start()後面的程式碼也會繼續運作,就像是兩個人分別做自己的事情。

而第一題,你可能有點誤解:
Thread thread = new Thread(OlaTeach_RunThread.this);的意思是將thread的run函式實做在OlaTeach_RunThread.this中,而你也可以選擇直接實做run函式,也就是:
Thread thread = new Thread(){
@Override
public void run() {
//要做的事情與要執行的Handler.sendMessage
}
};

兩個方式所產生的效果是一樣的,你可以設想如果現在有兩個thread都指定要實做在OlaTeach_RunThread.this中,那麼就會進到同一個run函式裡面,所以最簡單的方法就是利用第二種寫法來解決。

匿名 提到...

感謝您!! 我了解了!!
謝謝您不厭其煩得為我解釋XD

匿名 提到...

大大請問
如果要用Thread來寫幾秒重複執行某段程式該怎麼寫才比較好

ola的家 提到...

就如本篇的程式碼,用Thread.sleep(你需要的毫秒);來控制要間隔的時間。

李佳紋 提到...

版大您好,最近剛開始撰寫Android程式,想請教你如果在傳輸檔案的同時抓取到其所需時間為幾毫秒,透過Thread該如何撰寫才能把時間顯示在螢幕上?

匿名 提到...

大大不錯說得清楚......不像一些人藏東藏西....讚

匿名 提到...

大大還是忍不住要給你稱讚一番....真誠又幾句話點重點....好像的

張貼留言