事先表述一下:在做過一次嵌入式實驗之後,發現安卓有一個輕量級異步類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
本文可能有些地方難以理解,下方文章可能會對你有幫助: