先看過Processes and Threads會有比較清楚的概念
當Adnroid Application 啟動後, 系統會建一個主要的thread 稱 "main thread" or "UI thread", 所有的components 皆跑在這個UI thread, system calls 也是透過UI thread dispatched給各個component, ex: onKeyDown, touch event.
UI thread 如因大量運算或等待而blocked, 預設超過5秒ANR(Application Not Responding)就會發生.
且Android UI components 並非thread-safe, 使用上要特別小心.
所以:
1. long time computation使用另外的thread, 不要寫在 UI Thread.
2. 不要在UI thread 之外使用UI component method.
透過Thread, Handler and AsyncTask perform asynchronous processing, 避免UI thread block.
* Threads
Android 提供以下的method, 可在其它的thread 下調用 UI thread.
Activity.runOnUiThread(Runnable)
View.post(Runnable) <-- used in example code.
View.postDelayed(Runnable, long)
View.post(Runnable) <-- used in example code.
View.postDelayed(Runnable, long)
或是使用Handler or AsyncTasks class 達到同樣的效果.
(1) message scheduling, post action at specific point.
(2) 將其它thread發出來的action 放入message queue中, 避免race condition.
處理message 需要override handleMessage(), 透過sendMessage() or sendEmptyMessage() method.
執行Runnanble則是使用post() method.
一個Activity 只需有一個Handler instance.
4 steps:
呼叫execute() 開始執行, 之後onPerExecute()接著自動被呼叫, 通常用來 initial status of task.
接著doInBackground() 被調用, 一般為long time computation時使用.
call publishProgress()後會調用onProgressUpdate(), onProgressUpdate()調用於UI thread, 一般用來update progress bar, UI animate.
publishProgress() trigger時間點是無法預期的.
doInBackground() 結束後會trigger onPostExecute()調用於UI thread, 用來返回結果.
* Thread, Handler 在Avtivity 結束後 thread就結束, 但AsyncTack則否.
Ans:
Internally, AsyncTask uses fixed thread pool.
(See http://developer.android.com/reference/java/util/concurrent/ThreadPoolExecutor.html)
(See http://developer.android.com/reference/java/util/concurrent/ThreadPoolExecutor.html)
So, even if AsyncTask finished, thread does not die. But thread in thread pool can be killed.
Examples
Layout XML file:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical" >
<Button
android:id="@+id/button1"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/run" />
<ProgressBar
android:id="@+id/progressBar1"
style="?android:attr/progressBarStyleHorizontal"
android:layout_width="match_parent"
android:layout_height="60dp" />
</LinearLayout>
Thread example code:
package com.samchen.samdemos.threads;
import java.io.IOException;
import java.net.URL;
import java.net.URLConnection;
import android.app.Activity;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.opengl.GLSurfaceView;
import android.os.Bundle;
import android.os.SystemClock;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.ProgressBar;
import android.widget.Toast;
public class DemoThreads extends Activity {
final boolean isThread = true;
final String TAG = "DemoThreads";
private ProgressBar mProgress;
private int mProgressStatus = 0;
boolean isRunning = false;
/** Called when the activity is first created. */
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.demothreads);
Button btnRun = (Button) findViewById(R.id.button1);
mProgress = (ProgressBar) findViewById(R.id.progressBar1);
btnRun.setOnClickListener(new Button.OnClickListener() {
@Override
public void onClick(View arg0) {
if(isRunning){
Log.d(TAG,"thread is
runing");
Toast.makeText(getApplicationContext(),
getString(R.string.running),
Toast.LENGTH_SHORT).show();
return;
}else
isRunning = true;
if (isThread) {
// thread example
new Thread(new Runnable() {
public void run() {
while (mProgressStatus < 100) {
// update the bar
status
mProgress.post(new Runnable() {
public void run() {
mProgress.setProgress(mProgressStatus);
Log.d(TAG, "update
mProgressStatus : "
+ mProgressStatus);
}
});
// do something long
SystemClock.sleep(500);
mProgressStatus++;
}
}
}).start();
} else {
// non-thread example
while (mProgressStatus < 100) {
// update the bar status
mProgress.post(new Runnable() {
public void run() {
mProgress.setProgress(mProgressStatus);
}
});
// do something long
//SystemClock.sleep(500);
//if use SystemClock.sleep, system
will be crash.
try{
Thread.sleep(500);
}catch (InterruptedException
e){
Log.d(TAG,"InterruptedException!!!!!!!");
e.printStackTrace();
}
mProgressStatus++;
}
}
}
});
}
}
Handler example:
package com.samchen.samdemos.threads;
import java.io.IOException;
import java.net.URL;
import java.net.URLConnection;
import android.app.Activity;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.opengl.GLSurfaceView;
import android.os.Bundle;
import android.os.Handler;
import android.os.SystemClock;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.ProgressBar;
import android.widget.Toast;
public class DemoHandler extends Activity {
final String TAG = "DemoHandler";
private ProgressBar mProgress;
private int mProgressStatus = 0;
private Handler mHandler;
boolean isRunning = false;
/** Called when the activity is first created. */
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.demothreads);
Button btnRun = (Button) findViewById(R.id.button1);
mProgress = (ProgressBar) findViewById(R.id.progressBar1);
mHandler = new Handler();
btnRun.setOnClickListener(new Button.OnClickListener() {
@Override
public void onClick(View arg0) {
if (!isRunning) {
startProgress(mProgress);
isRunning = true;
} else{
Log.d(TAG, "thread is
runing");
Toast.makeText(getApplicationContext(),
getString(R.string.running),
Toast.LENGTH_SHORT).show();
}
}
});
}
public void startProgress(View view) {
// Do something long
Runnable runnable = new Runnable() {
@Override
public void run() {
for (mProgressStatus = 0; mProgressStatus <= 100; mProgressStatus++) {
//final int value = i;
SystemClock.sleep(500);
mHandler.post(new Runnable() {
@Override
public void run() {
Log.d(TAG, "update
mProgressStatus : "
+ mProgressStatus);
mProgress.setProgress(mProgressStatus);
}
});
}
}
};
new Thread(runnable).start();
}
}
AsyncTask example:
package com.samchen.samdemos.threads;
import android.app.Activity;
import android.opengl.GLSurfaceView;
import android.os.AsyncTask;
import android.os.Bundle;
import android.os.Handler;
import android.os.SystemClock;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.ProgressBar;
import android.widget.Toast;
public class DemoAsyncTask extends Activity {
final String TAG = "DemoAsyncTask";
private ProgressBar mProgress;
private int mProgressStatus = 0;
boolean isRunning = false;
private updateTask mTask;
/** Called when the activity is first created. */
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.demothreads);
mProgress = (ProgressBar) findViewById(R.id.progressBar1);
Button btnRun = (Button) findViewById(R.id.button1);
btnRun.setOnClickListener(new Button.OnClickListener() {
@Override
public void onClick(View arg0) {
if (!isRunning) {
mTask = new updateTask();
mTask.execute();
isRunning = true;
} else {
Log.d(TAG, "thread is
runing");
Toast.makeText(getApplicationContext(),
getString(R.string.running), Toast.LENGTH_SHORT)
.show();
}
}
});
}
private class updateTask extends AsyncTask<Void, Integer, Void> {
protected void onPostExecute(Void result) {
}
protected void onProgressUpdate(Integer... value) {
Log.d(TAG, "onProgressUpdate : " + value);
mProgress.setProgress(value[0]);
}
protected void onPreExecute() {
// TODO Auto-generated method stub
mProgressStatus = 0;
}
protected Void doInBackground(Void... params) {
// TODO Auto-generated method stub
while (mProgressStatus < 100) {
Log.d(TAG, "doInBackground mProgressStatus
: "
+ mProgressStatus);
// do something long
publishProgress(mProgressStatus);
SystemClock.sleep(500);
mProgressStatus++;
}
return null;
}
}
}
Ref: