在寫開發的過程中,難免會進行一些像下載檔案、讀取檔案之類的耗時操作。
如果將這些操作放在主執行緒執行的話,很容易造成UI介面卡住。直到操作執行完之後,UI畫面才會更新。
Android底層有個機制,只要主執行緒被耗時的操作卡住超過5秒以上,系統就會跳出ANR(應用程式沒有回應)提示。
且使用者也會因為這個狀況,憤而刪除掉APP、或是在Google Play商店給一顆星的評價。
因此,通常很耗時間的操作,都會額外再開一個執行緒來做處理。處理完成後再透過主執行緒呈現結果。
使用方式1
這裡直接使用new Thread的方式開啟執行緒,並透過Thread.sleep(1000)來模擬耗時操作,如下所示:
public class MainActivity extends AppCompatActivity {
private Button btnThread;
private TextView textView;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
textView = (TextView) findViewById(R.id.textView);
btnThread = (Button) findViewById(R.id.btnThread);
btnThread.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
// 按下按鈕模擬執行耗時操作
threadRun();
}
});
}
private void threadRun() {
// 開啟執行緒,並延遲一秒
new Thread(new Runnable() {
@Override
public void run() {
Log.e(TAG, "Thread" + Thread.currentThread().getName());
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
// UI更新時,切回主執行緒
runOnUiThread(
new Runnable() {
@Override
public void run() {
Log.e(TAG, "Thread" + Thread.currentThread().getName());
textView.setText("Thread finished...");
}
});
}
}).start();
}
}
可以透過Logcat看到執行緒的切換狀況如下:
E/MainActivity: ThreadBackground Worker
E/MainActivity: Threadmain
使用方式2
這裡使用Handler及HandlerThread,同樣也是透過Thread.sleep(1000)來模擬耗時操作。
首先先初始化HandlerThread物件,接著將HandlerThread的Looper物件,在Handler初始化時傳入即可,如下:
public class MainActivity extends AppCompatActivity {
private static final String TAG = MainActivity.class.getSimpleName();
private Button btnThread;
private TextView textView;
private Handler mHandler;
private HandlerThread mBackgroundThread;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mBackgroundThread = new HandlerThread("Background Worker");
mBackgroundThread.start();
mHandler = new Handler(mBackgroundThread.getLooper());
textView = (TextView) findViewById(R.id.textView);
btnThread = (Button) findViewById(R.id.btnThread);
btnThread.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
// 按下按鈕模擬執行耗時操作
threadRun();
}
});
}
private void threadRun() {
mHandler.post(new Runnable() {
@Override
public void run() {
Log.e(TAG, "Thread" + Thread.currentThread().getName());
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
runOnUiThread(
new Runnable() {
@Override
public void run() {
Log.e(TAG, "Thread" + Thread.currentThread().getName());
textView.setText("Thread finished...");
}
}
);
}
});
}
}
可以透過Logcat看到執行緒的切換狀況如下:
E/MainActivity: ThreadBackground Worker
E/MainActivity: Threadmain
使用方式3
這裡使用AsyncTask的方式,同樣也是透過Thread.sleep(1000)來模擬耗時操作,如下:
public class MainActivity extends AppCompatActivity {
private static final String TAG = MainActivity.class.getSimpleName();
private Button btnThread;
private TextView textView;
private MyAsyncTask asyncTask;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
textView = (TextView) findViewById(R.id.textView);
btnThread = (Button) findViewById(R.id.btnThread);
btnThread.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
// 按下按鈕模擬執行耗時操作
threadRun();
}
});
}
private void threadRun() {
asyncTask = new MyAsyncTask();
asyncTask.execute();
}
private class MyAsyncTask extends AsyncTask<Void, Void, String> {
@Override
protected void onPreExecute() {
textView.setText("AsyncTask Start");
Log.e(TAG, "AsyncTask start... Thread: " + Thread.currentThread().getName());
}
@Override
protected String doInBackground(Void... params) {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
Log.e(TAG, e.toString());
}
Log.e(TAG, "AsyncTask doInBackground... Thread: " + Thread.currentThread().getName());
return "Thread finished...";
}
@Override
protected void onProgressUpdate(Void... values) {
textView.setText("AsyncTask running...");
}
@Override
protected void onPostExecute(String result) {
textView.setText(result);
Log.e(TAG, "AsyncTask onPostExecute... Thread: " + Thread.currentThread().getName());
}
}
}
透過觀察Logcat訊息,可以看到執行緒切換的狀況:
E/MainActivity: AsyncTask start... Thread: main
E/MainActivity: AsyncTask doInBackground... Thread: AsyncTask #1
E/MainActivity: AsyncTask onPostExecute... Thread: main