android菜鳥筆記之UI執行緒阻塞
最近在學習Android,有些還是需要記錄下來,方便以後查詢
首先
當一個應用程式啟動之後,android系統會為這個應用程式建立一個主執行緒。這個執行緒非常重要,它負責渲染檢視,分發事件到響 應監聽器並執行, 對介面進行輪詢的監聽。因此,一般也叫做“UI”執行緒。android系統不會給應用程式的多個元素元件,建立多個執行緒來執行。一個檢視(activity)中的多個view元件執行在同一個UI執行緒當中。因此,多個view元件的監聽器的執行可能會相互影響。
例如:當在UI執行緒當中執行耗時操作,比如訪問網路,訪問資料庫等。則會導致UI執行緒阻塞。當UI執行緒阻塞,則螢幕會出現卡死情況。這樣使用者體驗非常差。當執行緒阻塞超過5秒以後,android系統有可能進行干預,彈出對話方塊詢問是否關閉應用程式。
新建一個android project Application,
開啟activity_main.xml,新增button1與button2
在MainActivity.java的onCreate中新增如下程式碼
啟動執行,當我們點選Button2後,等待片刻後嘗試再點選會出現以下@Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); //建立動畫按鈕 Button btn1 = (Button)findViewById(R.id.button1); TranslateAnimation translateAnimation = new TranslateAnimation(0, 150, 0, 0); translateAnimation.setRepeatCount(30);//執行次數 translateAnimation.setDuration(2000);//設定越少運動速度越快 btn1.setAnimation(translateAnimation); Button btn2 = (Button)findViewById(R.id.button2); btn2.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { // TODO Auto-generated method stub try { Thread.sleep(5000); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } }); }
點選OK將退出程式
Android提供了兩個解決方案給我們
android的UI規則
there are simply two rules to Android's single thread model;
Do not block the UI thread
不要阻塞UI執行緒
Do not access the Android UI toolkit from outside the UI thread
不要在UI執行緒之外的其他執行緒中,對檢視當中的元件進行設定
//經典異常:
Only the original thread that created a view hierarchy can touch its views.
只有建立view的那個執行緒才能對其進行修改
解決方案1:post
調整onCreate程式碼如下:
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//建立動畫按鈕
Button btn1 = (Button)findViewById(R.id.button1);
TranslateAnimation translateAnimation = new TranslateAnimation(0, 150, 0, 0);
translateAnimation.setRepeatCount(30);//執行次數
translateAnimation.setDuration(2000);//設定越少運動速度越快
btn1.setAnimation(translateAnimation);
Button btn2 = (Button)findViewById(R.id.button2);
btn2.setOnClickListener(new OnClickListener() {
@Override
public void onClick(final View v) {
// TODO Auto-generated method stub
new Thread(new Runnable(){
@Override
public void run() {
System.out.println("執行緒開始");
// TODO Auto-generated method stub
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
int i = 20;
v.post(new Runnable(){
@Override
public void run() {
System.out.println("post執行緒開始");
// TODO Auto-generated method stub
TextView tView = (TextView)v;
tView.setText("" + 100);
}
});
System.out.println("執行緒結束");
}
}).start();
}
});
}
在日誌中看到它把修改Button2的放到了主控制之後執行,但其實Post操作還是在主執行緒中的,並不是新建一個執行緒進行操作。
而且Sleep(5000)變成了只針對Button2執行。之前是對全域性進行操作。
如圖:其實View.Post方式,類似於生產者與消費者的模式。每次UI執行緒都會在任務佇列中查詢一次操作,同時View.Post在裡面放入自己的操作。
以上程式碼相當複雜,可讀性差,所以官方提供了另一個方法來處理這種問題。
解決方案2:AsyncTask
官方提供了以下片段:
private class DownloadImageTask extends AsyncTask<string void="" bitmap="">{
/*the system calls this to perform work in a workerdelivers it the parameters given to AsyncTask.execute()*/
protected Bitmap doInBackground(String... urls){
return loadImageFromNetwork(urls[0]);
}
/*The system calls this to perform work in the UI thread and delivers* the result from doInBackground()*/
protected void onFostExecute(Bitmap result){
mImageView.setImageBitmap(result);
}
}
</string>
由於要寫一個內部類所以需要定義全域性變數
private Button btn2 = null;
修改程式碼如下
private Button btn2 = null;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//建立動畫按鈕
Button btn1 = (Button)findViewById(R.id.button1);
TranslateAnimation translateAnimation = new TranslateAnimation(0, 150, 0, 0);
translateAnimation.setRepeatCount(30);//執行次數
translateAnimation.setDuration(2000);//設定越少運動速度越快
btn1.setAnimation(translateAnimation);
btn2 = (Button)findViewById(R.id.button2);
btn2.setOnClickListener(new OnClickListener() {
@Override
public void onClick(final View v) {
// TODO Auto-generated method stub
new ChangeButtonText().execute();
}
});
}
private class ChangeButtonText extends AsyncTask<String, Void, Integer>{
protected Integer doInBackground(String... urls){
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
int i = 100;
return i;
}
protected void onFostExecute(Integer result){
btn2.setText("" + result);
}
}
最終結果: