1. 程式人生 > >android菜鳥筆記之UI執行緒阻塞

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中新增如下程式碼

@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();
				}
			}
		});
	}
啟動執行,當我們點選Button2後,等待片刻後嘗試再點選會出現以下


點選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);
		}
	}
最終結果: