說實話,之前在java開發的時候,很少涉及多線程的東西。由於開發的項目體量比較小,技術也比較差,所以更多的考慮的是功能,很少對併發做優化
如今藉着學習Android的機會,希望可以對多線程的知識有一個更好更全面地認識。哎,感覺自己技術基礎還是太差,好好加油吧
首先,安卓使用的時單線程模型:
當一個程序第一次啓動時,Android會同時啓動一個對應的主線程(Main Thread),主線程主要負責處理與UI相關的事件,如:用戶的按鍵事件,用戶接觸屏幕的事件以及屏幕繪圖事件,並把相關的事件分發到對應的組件進行處理。所以主線程通常又被叫做UI線程。
在開發Android應用時必須遵守單線程模型的原則: Android UI操作並不是線程安全的並且這些操作必須在UI線程中執行。
如果在非UI線程中直接操作UI線程,會拋出android.view.ViewRoot$CalledFromWrongThreadException:Only the original thread that created a view hierarchy can touch itsviews,這與普通的java程序不同。
由於UI線程負責事件的監聽和繪圖,因此,必須保證UI線程能夠隨時響應用戶的需求,UI線程裏的操作應該向中斷事件那樣短小,費時的操作(如網絡連接)需要另開線程,否則,如果UI線程超過5s沒有響應用戶請求,會彈出對話框提醒用戶終止應用程序。
如果在新開的線程中需要對UI進行設定,就可能違反單線程模型,因此android採用一種複雜的Message Queue機制保證線程間通信。
以上內容摘自http://www.cnblogs.com/nio-nio/archive/2012/07/23/2604900.html,是我在網上看到的相對比較容易理解的說明。讀完以上這幾段話之後,我對安卓單線程模式的理解:如果你要對UI進行操作那這些操作應該都放到主線程中;如果你要處理一些業務邏輯或者其它的比較費時的操作,那就把這些操作放到子線程中。那因爲大多數的UI更新都是基於業務邏輯的判斷,如何讓處理邏輯的子線程和處理UI的主線程通信呢,這就需要用到Handler
Handler是幹什麼的:
Handler可以分發Message對象和Runnable對象到主線程中,每個Handler實例,都會綁定到創建他的線程中(一般是位於主線程),它有兩個作用: (1): 安排消息或Runnable 在某個主線程中某個地方執行, (2)安排一個動作在不同的線程中執行。
關於Handler的使用,我們首先來看一個簡單例子:
我們假設有這麼一個需求:我們需要在程序中點擊一個開始按鈕,然後我們的程序會在後臺啓動一個定時任務(每隔一秒打印一個語句)。另外,我們還需要一個取消按鈕,點擊它終止後臺的定時任務.
我們先在界面中加入兩個按鈕,一個開始,一個取消。接下來是activity
package com.example.hander1;
import java.util.Date;
import android.app.Activity;
import android.os.Bundle;
import android.os.Handler;
import android.util.Log;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
public class MainActivity extends Activity {
Button startButton = null;
Button stopButton = null;
Handler handler = new Handler();
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
startButton = (Button) findViewById(R.id.startButton);
stopButton = (Button) findViewById(R.id.stopButton);
//爲button綁定onclicklistener
startButton.setOnClickListener(new ButtonOnclikListener());
stopButton.setOnClickListener(new ButtonOnclikListener());
}
Runnable going = new Runnable() {
@Override
public void run() {
//線程每次執行,打印日誌
Log.i("run", "going "+new Date().getSeconds());
//延時一秒鐘後,再次將線程加入隊列中
handler.postDelayed(going, 1000);
}
};
class ButtonOnclikListener implements OnClickListener{
@Override
public void onClick(View v) {
switch(v.getId()){
case R.id.startButton:
begin();
break;
case R.id.stopButton:
stop();
break;
default:
break;
}
}
}
//該方法將going添加到message queue中
void begin(){
handler.post(going);
}
//從message queue中移除going
void stop(){
handler.removeCallbacks(going);
}
}
下面是運行結果,程序啓動後是這樣
當我點擊啓動按鈕後,後臺每秒中打印一條語句
當我點擊取消按鈕後,後臺不再打印語句
對於這個程序的運行過程我是這樣理解的:
1、當我點擊啓動按鈕時:調用handler.post(going);
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.
也就是說,當我調用了handler.post(going);這個方法之後,handler會將我實現的Runnable對象加入到message queue中,系統會根據message queue來執行。後面一句很重要,runnable會在當前handler所屬的那個線程中執行,也就是說我的going會在程序的主線程中執行,而不是新開啓的其它線程。
· 2、在going的run方法中,我調用了handler.postDelayed(going, 1000);
postDelayed這個方法和前面的post方法類似,只不過第二個參數的作用是讓我的going延遲一秒鐘執行。
這樣其實就形成了一個死循環(我沒有規定跳出的條件),程序在我點擊啓動之後,每隔一秒鐘會在message queue中加入執行going的內容。
3、當我點擊取消按鈕時:調用handler.removeCallbacks(going);
雖在我在上面的寫法中形成了一個死循環,但是我可以通過handler.removeCallbacks(going)這個方法,將message queue這個隊列中的going去掉,這樣就不再執行going了。而且根據這個方法源碼中的註釋:Remove any pending posts of Runnable r that are in the message queue. 應該是會把message queue中所有未執行going全部去掉。
這個程序雖然很簡單,但是我在一次看到的時候,仍然陷入了誤區。由於沒有仔細閱讀API,我剛開始以爲handler.post()這個方法會新開啓一個線程。後來看到API的時候才明白原來這個程序,只有一個主線程,只不過是通過handler將實現的Runnable 對象加入到message queue中執行的。