Android多線程
一個Android的應用程序運行在一個獨立的進程中,運行在一個獨立的虛擬機(dvk)上。(進程名爲包名)
Android應用程序開啓後,默認開啓一個主線程(UI線程)
Activity,Service,BroadcastReceive組件運行在主線程中
Android 應用程序退出後,保留空UI線程,可以加快應用程序啓動速度。
用戶不能再UI主線程中做耗時的操作,一旦操作超過5s,應用程序拋出一個ANR(application not respond)
如何避免ANR錯誤?
將耗時的操作放入到子線程中。(耗時的操作包括:長時間的休眠,計數,聯網,複雜的運算)
只有主線程才能操作Widget控件。
如果在子線程中出現操作Widget控件,系統拋出CalledFromWrongThreadException異常。
系統爲什麼要這麼做?
避免出現同步問題。
Handler機制
作用
主要爲了解決非UI線程中不能更新Widget控件的問題
Handler機制剖析
子線程發送消息給底層的消息隊列。
handler.sendMessage(msg)
主線程查詢消息隊列,處理消息對象。
handlerMessage(msg)
MessageQueue 消息隊列
負責存儲消息對象
Looper
給UI線程安排代碼,一個UI線程只能有一個Looper對象,否則多個Looper對象都在UI線程上安排代碼,解決衝突就是個大問題。
Looper對象會線性安排在UI線程上執行的代碼,它通過一個隊列管理各個Handler對象提交的代碼。
Message消息對象
//從消息池中獲取消息對象
Message msg = handler.obtainMessage();
//在消息對象上綁定int類型數據
msg.arg1 = count;
//在消息對象上綁定其它類型數據
msg.setData(Bundle); //Bundler爲數據集(類似於HashMap容器)
import android.app.Activity;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.TextView;
/**
* @author Administrator
* 事件驅動的應用程序
*/
public class MainActivity extends Activity implements OnClickListener {
Button btnStart;
TextView tv;
int count = 0;
boolean isRun;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
btnStart = (Button) findViewById(R.id.button1);
btnStart.setOnClickListener(this);
findViewById(R.id.button2).setOnClickListener(this);
tv = (TextView) findViewById(R.id.textView1);
}
@Override
public void onClick(View v) {
// TODO Auto-generated method stub
if(v.getId() == R.id.button1){
btnStart.setEnabled(false);
count = 0;
isRun = true;
new Thread(new Runnable() {
@Override
public void run() {
// TODO Auto-generated method stub
// 修改Widget控件的值
// tv.setText(String.valueOf(count));
while (isRun) {
count++;
// 創建消息對象
Message msg = new Message();
msg.arg1 = count;
//設置消息類型
msg.what = 2;
// 發送消息 (發給底層的消息隊列)
handler.sendMessage(msg);
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
handler.sendEmptyMessage(1);
}
}).start();
}else if(v.getId() == R.id.button2){
isRun = false;
}
}
Handler handler = new Handler(){
//如果消息隊列中有消息,系統自動調用該方法處理消息
@Override
public void handleMessage(Message msg) {
// TODO Auto-generated method stub
super.handleMessage(msg);
if(msg.what == 2){
int count = msg.arg1;
tv.setText(String.valueOf(count));
}else if(msg.what == 1){
//修改按鈕屬性
btnStart.setEnabled(true);
}
}
};
}
向消息隊列發送消息,1000毫秒後執行Runnable對象中的代碼。
myHandler.postDelayed(new Runnable() {
@Override
public void run() {
// TODO Auto-generated method stub
Log.e("Test", "thread name = "+Thread.currentThread().getName());
}
}, 1000);
異步任務(Async Task)
概念
封裝多線程和Handler機制。給用戶提供重寫接口的方式,不需要用戶手動創建子線程和Handler對象。
異步任務的優點
Handler模式需要爲每一個任務創建一個新的線程,任務完成後通過Handler實例向UI主線程發送消息,完成界面的更新,這種方式對於整個過程的控制比較精細,但是也有缺點,代碼臃腫,在多個任務同時執行時,不易對線程進行精確的控制。爲了簡化操作,Android1.5提供了一個工具類AsyncTask,它是創建異步任務變的更加簡單,不再需要編寫任務線程和Handler實例就可完成任務。
異步任務的侷限性
多個異步任務不能同時執行,在某個時間內,只能執行一個異步任務。
執行異步任務的步驟:
- 1 execute(Params … params),執行一個異步任務,需要我們在代碼中調用此方法,觸發異步任務的執行。
- 2 onPreExecute(),在execute(Params… params
)被調用後立即執行,一般用來在執行後臺任務前對UI做一些標記。 - 3 doInBackground(Params…
params),在onPreExecute()完成後立即執行,用於執行較爲費時的操作,此方法將接受輸入參數和返回計算結果在執行過程中可以調用publishProgress(Progress…values)來更新進度信息。作用在非UI線程上。 - 4 onProgressUpdate(Progress… values),在調用publishProgress(Progress…
values)時,此方法被執行,直接將進度信息更新到UI組件上。 - 5 onPostExecute(Result
result)當後臺操作結束時,此方法將被調用,計算結果講座爲參數傳遞到方法中,直接將結果顯示到UI組件上。
import android.os.AsyncTask;
import android.util.Log;
import android.widget.TextView;
//Params 執行異步任務時,傳入的參數
//Progress 異步任務的進度
//Result 結果
public class MyTask extends AsyncTask<Void, Integer, Void> {
TextView tv;
public MyTask(TextView tv) {
// TODO Auto-generated constructor stub
this.tv = tv;
}
@Override
protected void onPreExecute() {
// TODO Auto-generated method stub
super.onPreExecute();
Log.d("Test", "onPre");
}
//該方法運行在非UI線程中
//該方法中放耗時操作
@Override
protected Void doInBackground(Void... params) {
// TODO Auto-generated method stub
int count = 0;
while(!isCancelled() && count < 10){
count++;
//發佈進度
//觸發系統自動調用onProgressUpdate
//相當於handler.sendMessage()
publishProgress(count);
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
return null;
}
//相當於handlerMessage()
//該方法運行在UI線程中
@Override
protected void onProgressUpdate(Integer... values) {
// TODO Auto-generated method stub
super.onProgressUpdate(values);
if(isCancelled()){
return;
}
//操作UI控件
int count = values[0];
tv.setText(String.valueOf(count));
}
//該放在在取消異步任務之後運行
@Override
protected void onCancelled() {
// TODO Auto-generated method stub
super.onCancelled();
}
@Override
protected void onPostExecute(Void result) {
// TODO Auto-generated method stub
super.onPostExecute(result);
Log.d("Test", "onPost");
}
}