論AsyncTask線程與Runnable接口線程的區別,及二者的使用方法(含demo)

事先表述一下:在做過一次嵌入式實驗之後,發現安卓有一個輕量級異步類AsyncTask線程。AsyncTask每產生一個線程需要使用execute()或者executeOnExecuter()方法。其中execute()方式是產生串行線程,executeOnExecuter()方法產生並行線程。由於是輕量級類線程,封裝了線程池,AsyncTask並不能無限的產生子線程,它所產生的線程並行最大數取決於設備的CPU核心數(核心數x2+1)。

通常我們寫在子線程裏面的操作,可以稱爲耗時任務(顧名思義就是耗時間的任務)。(先記住這個)

ANR”是指程序在響應不靈敏的情況下,系統會彈出Dialog提示用戶是否“繼續等待”或者“強行關閉”。(這個也記住)

主線程在程序運行的時候被創建。

Android中主線程一般不宜寫入太多任務,也是爲了避免出現“ANR”情況,所以我們可以把耗時任務都寫進子線程。那麼主線程主要是幹嘛呢?我認爲主要是讓其負責前臺用戶界面更新及時響應用戶的操作(控件交互),所以主線程也稱爲UI線程

AsyncTask

在Android中創建子線程較爲常用的方法就是繼承AsyncTask這個類,然後調用execute()方法或者executeOnExecutor()方法創建,前者是串行子線程,後者是並行子線程。

線程中需要調用這幾個方法:doInBackground()、publicProgress()、onProgressUpdate()。

doInBackground()是必須存在的,就相當於Thread的run()方法必須重寫。

publicProgress()是幫onProgressUpdate()傳遞參數的。

onProgressUpdate()是更新數據的。

下面嘗試使用AsyncTask來創建子線程:

public class MainActivity extends Activity {

	private TextView task_1;
	private TextView task_2;
	private TextView task_3;
	private TextView task_4;
	private TextView task_5;

	private Button t_one;
	private Button t_two;
	private Button t_three;
	private Button t_four;
	private Button t_five;
	
	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_main);

		task_1 = (TextView)findViewById(R.id.task_1);
		task_2 = (TextView)findViewById(R.id.task_2);
		task_3 = (TextView)findViewById(R.id.task_3);
		task_4 = (TextView)findViewById(R.id.task_4);
		task_5 = (TextView)findViewById(R.id.task_5);

		t_one = (Button)findViewById(R.id.t_one);
		t_two = (Button)findViewById(R.id.t_two);
		t_three = (Button)findViewById(R.id.t_three);
		t_four = (Button)findViewById(R.id.t_four);
		t_five = (Button)findViewById(R.id.t_five);

		t_one.setOnClickListener(new OnClickListener() {
			public void onClick(View v) {
				create(1);
			}
		});
		t_two.setOnClickListener(new OnClickListener() {
			public void onClick(View v) {
				create(2);
			}
		});
		t_three.setOnClickListener(new OnClickListener() {
			public void onClick(View v) {
				create(3);
			}
		});
		t_four.setOnClickListener(new OnClickListener() {
			public void onClick(View v) {
				create(4);
			}
		});
		t_five.setOnClickListener(new OnClickListener() {
			public void onClick(View v) {
				create(5);
			}
		});
		
		
	}
	
	public void create(int i) {
		CreateTask createTask = new CreateTask(this,i);
        /////注意這裏,execute是串行的////////////////////////////
		createTask.execute();
	}
	
	//CreateTask繼承AsyncTask
	class CreateTask extends AsyncTask<Void, Integer, Integer>{

		private Context context;
		private int i;
		
		//獲取上下文
		CreateTask(Context context, int i){
			this.context = context;//this.context爲此類下的context
			this.i = i;
		}
		
		//在doinbackground()之前運行的一個函數
		protected void onPreExecute() {
			Toast.makeText(
					context,
					"進入子線程",
					Toast.LENGTH_SHORT).show();
		}
		
		//這是後臺運行方法,但不可以更新UI
		@Override
		protected Integer doInBackground(Void... params) {

			for(int i = 0; i < 10; i++) {
				publishProgress(i);
				try {
					Thread.sleep(200);
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
			}
			
			return null;
		}
		
		//跳出doinbackground()之後執行onpostExecute
		public void onPostExecute(Integer integer) {
			Toast.makeText(
					context,
					"子線程"+i+"執行完!",
					Toast.LENGTH_SHORT).show();
		}
		
		
		protected void onProgressUpdate(Integer... values) {//這裏的Integer... values適用於接收數組
			
			switch (i) {
			case 1:
				task_1.setText(""+values[0]);
				break;
			case 2:
				task_2.setText(""+values[0]);
				break;
			case 3:
				task_3.setText(""+values[0]);
				break;
			case 4:
				task_4.setText(""+values[0]);
				break;
			case 5:
				task_5.setText(""+values[0]);
				break;

			default:
				break;
			}
		}
		
	}


}

運行結果(這裏可以看出,這裏的子線程是串行的):

那麼我們使用executeOnExecutor()來創建並行線程:直接把代碼裏的 execute()改爲executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR)即可,運行結果如下:

問:這裏就是並行的子線程。心細的童鞋應該發現,到第4個子線程的時候,怎麼不併行了呢?

答:因爲線程並行的個數 取決於 設備的處理器的核心數(核心數x2+1)。

問:還有一點,大家都知道安卓UI更新需要在主線程(UI線程)進行對吧?爲什麼在這裏可以在子線程中更新呢?

答:因爲AsyncTask封裝了Handler線程池。Handler就是“幫助”子線程傳遞消息給主線程(UI線程)而進行更新UI的。

順便說一下線程池:線程池是用於控制線程開銷的,也就是阻止不必要的線程被創建,就比如有50個任務,每個任務內只需要執行0.01ms就可以執行完畢,如果創建50個線程進行執行,這無疑是一筆大開銷,影響設備性能(這裏能理解,得益於鍾院所說的溫室萬一有幾千個傳感器的問題,線程能少創建儘量少創建)。簡單點說,線程池是有固定數量的線程的,遏制線程過多被創建導致設備性能降低。據瞭解,AsyncTask的線程池只有5個子線程。

其實AsyncTask還有很多可深入的,下次再深入。

 

Runnable

在Java項目中,我們創建線程常用的就是Runnable接口,它不像AsyncTask的線程池那樣限制線程數量的創建。

先給個錯誤代碼給你們看看,看看找的出哪裏有錯。

	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_main);
		

		task_1 = (TextView)findViewById(R.id.task_1);
		task_2 = (TextView)findViewById(R.id.task_2);
		task_3 = (TextView)findViewById(R.id.task_3);
		task_4 = (TextView)findViewById(R.id.task_4);
		task_5 = (TextView)findViewById(R.id.task_5);

		t_one = (Button)findViewById(R.id.t_one);
		t_two = (Button)findViewById(R.id.t_two);
		t_three = (Button)findViewById(R.id.t_three);
		t_four = (Button)findViewById(R.id.t_four);
		t_five = (Button)findViewById(R.id.t_five);
		
		
		//創建子線程
		
		t_one.setOnClickListener(new OnClickListener() {
			public void onClick(View v) {
				Task_one task_one = new Task_one(1);
				new Thread(task_one).start();
			}
		});
		t_two.setOnClickListener(new OnClickListener() {
			public void onClick(View v) {
				Task_one task_two = new Task_one(2);
				new Thread(task_two).start();
			}
		});
		t_three.setOnClickListener(new OnClickListener() {
			public void onClick(View v) {
				Task_one task_three = new Task_one(3);
				new Thread(task_three).start();
			}
		});
		t_four.setOnClickListener(new OnClickListener() {
			public void onClick(View v) {
				Task_one task_four = new Task_one(4);
				new Thread(task_four).start();
			}
		});
		t_five.setOnClickListener(new OnClickListener() {
			public void onClick(View v) {
				Task_one task_five = new Task_one(5);
				new Thread(task_five).start();
			}
		});
			
	}

	class Task_one implements Runnable{
		
		private int i = 0;
		public Task_one(int i) {
			this.i = i;
		}
		
		public void run() {
			// TODO 自動生成的方法存根
			for(int j = 0; j <= 10; j++) {
				switch (i) {
				case 1:
					task_1.setText(""+j);
					break;
				case 2:
					task_2.setText(""+j);
					break;
				case 3:
					task_3.setText(""+j);
					break;
				case 4:
					task_4.setText(""+j);
					break;
				case 5:
					task_5.setText(""+j);
					break;

				default:
					break;
				}
			}
		}
	}

}

看起來好像沒什麼毛病。

但是“意外停止”了:

我們來看看logcat: Only the original thread that created a view hierarchy can touch its views.這裏的大概意思是只有在主線程下纔可以操控控件的界面。我們回到代碼看一下,有沒有發現更新TextView是在run()方法裏面:

這是不可取的,因爲更新TextView就是更新UI,更新UI必須要在主線程(UI線程)中,不可在子線程中更新,而run()方法則是Runnable接口創建的子線程的重寫方法,所以違反Android機制導致程序意外停止。

那我們怎麼做?

寫在主線程的runOnUiThread()方法中更新:

public class MainActivity extends Activity {
	private TextView task_1;
	private TextView task_2;
	private TextView task_3;
	private TextView task_4;
	private TextView task_5;

	private Button t_one;
	private Button t_two;
	private Button t_three;
	private Button t_four;
	private Button t_five;

	public int Const1 = 0;
	public int Const2 = 0;
	public int Const3 = 0;
	public int Const4 = 0;
	public int Const5 = 0;
	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_main);
		

		task_1 = (TextView)findViewById(R.id.task_1);
		task_2 = (TextView)findViewById(R.id.task_2);
		task_3 = (TextView)findViewById(R.id.task_3);
		task_4 = (TextView)findViewById(R.id.task_4);
		task_5 = (TextView)findViewById(R.id.task_5);

		t_one = (Button)findViewById(R.id.t_one);
		t_two = (Button)findViewById(R.id.t_two);
		t_three = (Button)findViewById(R.id.t_three);
		t_four = (Button)findViewById(R.id.t_four);
		t_five = (Button)findViewById(R.id.t_five);
		
		
		//刷新UI
		new Thread(new Runnable() {
			
			@Override
			public void run() {
				// TODO 自動生成的方法存根
				try {
					while(true) {
						Thread.sleep(200);
						runOnUiThread(new Runnable() {
							
							@Override
							public void run() {
								task_1.setText(""+Const1);
								task_2.setText(""+Const2);
								task_3.setText(""+Const3);
								task_4.setText(""+Const4);
								task_5.setText(""+Const5);
							}
						});
					}
					
				} catch (InterruptedException e) {
				}
				
				
			}
		}).start();
		
		t_one.setOnClickListener(new OnClickListener() {
			public void onClick(View v) {
				Task_one task_one = new Task_one(1);
				new Thread(task_one).start();
			}
		});
		t_two.setOnClickListener(new OnClickListener() {
			public void onClick(View v) {
				Task_one task_two = new Task_one(2);
				new Thread(task_two).start();
			}
		});
		t_three.setOnClickListener(new OnClickListener() {
			public void onClick(View v) {
				Task_one task_three = new Task_one(3);
				new Thread(task_three).start();
			}
		});
		t_four.setOnClickListener(new OnClickListener() {
			public void onClick(View v) {
				Task_one task_four = new Task_one(4);
				new Thread(task_four).start();
			}
		});
		t_five.setOnClickListener(new OnClickListener() {
			public void onClick(View v) {
				Task_one task_five = new Task_one(5);
				new Thread(task_five).start();
			}
		});
			
	}

	class Task_one implements Runnable{
		
		private int i = 0;
		public Task_one(int i) {
			this.i = i;
		}
		
		public void run() {
			// TODO 自動生成的方法存根
			
			for(int j = 0; j <= 10; j++) {
				try {Thread.sleep(500);} catch (InterruptedException e) {}
				switch (i) {
				case 1:
					Const1 = j;
					break;
				case 2:
					Const2 = j;
					break;
				case 3:
					Const3 = j;
					break;
				case 4:
					Const4 = j;
					break;
				case 5:
					Const5 = j;
					break;

				default:
					break;
				}
			}
		}
	}

}

運行結果(5個子線程並行):

綜上,AsyncTask與Runnable各有好處,巧用哪個取決於項目需要。

本文兩個demo,稍後給出。http://t.cn/AiCwwXLp

本文可能有些地方難以理解,下方文章可能會對你有幫助:

論線程Thread與Runnable
Android的Handler的簡單理解和使用

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章