在實際應用中經常會遇到比較耗時任務的處理,比如網絡連接,數據庫操作等情況時,如果這些操作都是放在主線程(UI線程)中,則會造成UI的假死現象(android4.0后也不許放在UI線程),這可以使用AsyncTask和Handler兩種異步方式來解決這種問題。
AsyncTask(異步任務處理)
在使用AsyncTask時處理類需要繼承AsyncTask,提供三個泛型參數,並且重載AsyncTask的四個方法(至少重載一個)。
三個泛型參數:
1.Param 任務執行器需要的數據類型
2.Progress 后台計算中使用的進度單位數據類型
3.Result 后台計算返回結果的數據類型
在設置參數時通常是這樣的:String... params,這表示方法可以有0個或多個此類型參數;有時參數可以設置為不使用,用Void...即可。
四個方法:
1.onPreExecute() 執行預處理,它運行於UI線程,可以為后台任務做一些准備工作,比如繪制一個進度條控件。
2.doInBackground(Params...) 后台進程執行的具體計算在這里實現,doInBackground(Params...)是AsyncTask的關鍵,此方法必須重載。在這個方法內可以使用publishProgress(Progress...)改變當前的進度值。
3.onProgressUpdate(Progress...) 運行於UI線程。如果在doInBackground(Params...) 中使用了publishProgress(Progress...),就會觸發這個方法。在這里可以對進度條控件根據進度值做出具體的響應。
4.onPostExecute(Result) 運行於UI線程,可以對后台任務的結果做出處理,結果就是doInBackground(Params...)的返回值。此方法也要經常重載,如果Result為null表明后台任務沒有完成(被取消或者出現異常)。
示例代碼如下:
<textarea cols="87" rows="15" name="code" class="Java">
// AsyncTask異步方式下載圖片
class DownImageTask extends AsyncTask<String, Integer, Bitmap>{
// 執行預處理
@Override
protected void onPreExecute() { super.onPreExecute(); // 顯示進度條 progressBar.setVisibility(View.VISIBLE); progressBar.setMax(100); }
// 后台進程的執行
@Override
protected Bitmap doInBackground(String... params) {
try { URL url = new URL(params[0]); HttpURLConnection conn = (HttpURLConnection) url .openConnection(); InputStream inputStream = conn.getInputStream(); bitmap = BitmapFactory.decodeStream(inputStream); // 進度條的更新,我這邊只是用一個循環來示范,在實際應用中要使用已下載文件的大小和文件總大小的比例來更新 for (int i = 1; i <= 10; i++) { publishProgress(i * 10); Thread.sleep(200); } inputStream.close(); }
catch (Exception e) { e.printStackTrace(); } return bitmap; }
// 運行於UI線程,對后台任務的結果做出處理,doInBackground方法執行的結果作為此方法的參數
@Override
protected void onPostExecute(Bitmap result) { super.onPostExecute(result); ImageView imageView = (ImageView) findViewById(R.id.image); imageView.setImageBitmap(result); progressBar.setVisibility(View.GONE); }
// 運行於UI線程,如果在doInBackground(Params...)中使用了publishProgress(Progress...),就會觸發此方法 @Override protected void onProgressUpdate(Integer... values) { super.onProgressUpdate(values); progressBar.setProgress(values[0]); } }</textarea>
1.Handler的定義
主要接受子線程發送的數據,並用此數據配合主線程更新UI。當 應用程序啟動時,Android首先會開啟一個主線程 (UI線程), 主線程為管理界面中的UI控件,進行事件分發,比如說點擊一個 Button ,Android會分發事件到Button上,來響應你的操作。如果進行一個耗時的操作,例如聯網讀取數據,或者讀取本地較大的一個文件的時候,你不能把 這些操作放在主線程中,如果你放在主線程中的話,界面會出現假死現象,如果5秒鍾還沒有完成的話,會收到Android系統的一個錯誤提示“強制關閉”。 這個時候我們需要把這些耗時的操作,放在一個子線程中,因為子線程涉及到UI更新,Android主線程是線程不安全的,也就是說更新UI只能在主線程中更新,子線程中操作是危險的。Handler可以解決這個復雜的問題 ,由於Handler 運行在主線程中(UI線程)中,它與子線程可以通過Message對象來傳遞數據,這個時候Handler就承擔着接受子線程傳過來的(子線程用 sedMessage()方法傳遞)Message對象(里面包含數據),把這些消息放入主線程隊列中,配合主線程進行更新UI。
2.Handler的特點
Handler可以分發Message對象和Runnable對象到主線程中, 每個Handler實例,都會綁定到創建他的線程中(一般是位於主線程中)
兩個作用: (1)安排消息或Runnable 在某個主線程中某個地方執行(2)安排一個動作在不同的線程中執行
Handler中分發消息的方法:
post(Runnable)
postAtTime(Runnable,long)
postDelayed(Runnable,long)
sendEmptyMessage(int)
sendMessage(Message)
sendMessageAtTime(Message,long)
sendMessageDelayed(Message,long)
*以上post開頭的方法表示把一個Runnable對象放到主線程隊列中,而這個Runnable對象會在調用此方法的Handler對象所在的線程執行,通常就是主線程(UI線程)。
*當需要在不同於主線程(UI線程)中執行時則需要Handler對象去構造一個Message對象並且發送到隊列中。
3.Handler的使用
<textarea cols="88" rows="15" name="code" class="Java">
class MyOnclickListener implements OnClickListener {
@Override public void onClick(View v) {
switch (v.getId()) { // 響應Handler異步方式
case R.id.downbtn1: // 顯示進度對話框,這里也可以使用進度條,在handleMessage方法中更新進度
dialog = ProgressDialog.show(DownLoadImageActivity.this, "", "正在下載,請稍等···");
// 新建一個子線程來發送消息
new Thread() { @Override public void run() { try { // 讓ProgressDialog顯示一會兒。。。。 Thread.sleep(2000); URL url = new URL(PATH);
// 建立網絡連接 HttpURLConnection conn = (HttpURLConnection) url .openConnection(); InputStream inputStream = conn.getInputStream();
// 獲取圖片數據 bitmap = BitmapFactory.decodeStream(inputStream); inputStream.close(); Message message = new Message(); message.what = 1;
// 發送消息到消息隊列中 handler.sendMessage(message); } catch (Exception e) { Message message = new Message(); message.what = -1; handler.sendMessage(message); e.printStackTrace(); } } }.start(); break;
// 響應AsyncTask異步方式
case R.id.downbtn2: new DownImageTask().execute(PATH); break; } } } // Handler異步方式下載圖片 private Handler handler = new Handler() { public void handleMessage(android.os.Message msg) { ImageView imageView;
switch (msg.what) {
case 1: // 下載成功 imageView = (ImageView) findViewById(R.id.image); dialog.dismiss(); imageView.setImageBitmap(bitmap); break;
case -1: // 下載失敗使用默認圖片 imageView = (ImageView) findViewById(R.id.image); dialog.dismiss(); imageView.setBackgroundResource(R.drawable.icon); break; } }; };</textarea>