android 的執行緒模式:當一個 android 的應用運行後,就會有一個 UI 的 main 線程啟動 , 這是一個非常重要的線程,它負責把事件指派到相應的控制項,其中就包括螢幕繪圖事件,它同樣是使用者與 android 控制項 互動的線程。比如,當你在螢幕上的 EditText 上輸入文字, UI 線程會把這個事件分發給剛輸入文字的
EditText ,緊接會向事件隊列發送一個更新 ( invalidate )請求。 UI 線程會把這個請求移出事件隊列並通知 EditText 在螢幕上重新繪製自身。
這種單線執行緒模式就會使得 android 的應用程式效能低下, 如果在這個單線程裡執行一些耗時的操作, 比如訪問資料庫, 或是從網路端下載圖片, 就會會阻塞整個使用者介面。 比如如下操作:
Bitmap b = loadImageFromNetwork();
這個操作非常耗時, 在這種情況下你會發現 , 介面僵死在那裡並且 android 在系統 5 秒中後沒有反應,會顯示一個關閉或等待的錯誤。
也許我們可以使用一個新的 Thread 來解決它
new Thread(new Runnable() { public void run() { Bitmap b = loadImageFromNetwork(); mImageView.setImageBitmap( b ); } }).start();
但這樣會發生一些很難察覺的錯誤, 因為我們知道 UI 線程不是安全執行緒的。當然有很多種方法來處理這個問題:
android 提供了幾種在其他線程中訪問 UI 線程的方法。
• Activity.runOnUiThread( Runnable )
• View.post( Runnable )
• View.postDelayed( Runnable, long )
• Hanlder
new Thread( new Runnable() { public void run() { final Bitmap b = loadImageFromNetwork(); mImageView.post( new Runnable() { mImageView.setImageBitmap( b ); }); } }).start();
這種方法比較繁瑣,同時當你需要實現一些很複雜的操作並需要頻繁地更新UI 時這會變得更糟糕。為瞭解決這個問題,android 提供了一個工具類:AsyncTask ,它使建立需要與使用者介面互動的長時間啟動並執行任務變得更簡單。
就拿載入網狀圖片舉個例子:
ublic class CanvasImageTask extends AsyncTask<ImageView, Void, Bitmap>{ private ImageView gView ; protected Bitmap doInBackground(ImageView... views) { Bitmap bmp = null ; ImageView view = views[0]; // 根據iconUrl擷取圖片並渲染,iconUrl的url放在了view的tag中。 if (view.getTag() != null) { try { URL url = new URL(view.getTag().toString()); HttpURLConnection conn = (HttpURLConnection)url.openConnection(); conn.setDoInput(true); conn.connect(); InputStream stream = conn.getInputStream(); bmp = BitmapFactory.decodeStream(stream); stream.close(); } catch (Exception e) { Log.v("img", e.getMessage()); return null; } } this.gView = view; return bmp; } protected void onPostExecute(Bitmap bm) { if (bm != null) { this.gView.setImageBitmap(bm); this.gView = null ; } } }在Activity中直接調用if(!img.isDrawingCacheEnabled() || !holder.image.getTag().equals(imgpath)){ img.setImageResource(R.drawable.icon_app); img.setTag(imgpath); try{ new CanvasImageTask().execute(img); img.setDrawingCacheEnabled(true); }catch (Exception e) { Log.e("error", "RejectedExecutionException in content_img: " + imgpath);
這樣圖片載入使用非同步線程便不會進行堵塞發生錯誤,我們還可以使用 callback 在圖片載入完後進行回調
public class CanvasImageTaskCall extends AsyncTask<ImageView, Void, Bitmap> implements Callback{ private ImageView gView ; protected Bitmap doInBackground(ImageView... views) { Bitmap bmp = null ; ImageView view = views[0]; // 根據iconUrl擷取圖片並渲染,iconUrl的url放在了view的tag中。 if (view.getTag() != null) { try { URL url = new URL(view.getTag().toString()); HttpURLConnection conn = (HttpURLConnection)url.openConnection(); conn.setDoInput(true); conn.connect(); InputStream stream = conn.getInputStream(); bmp = BitmapFactory.decodeStream(stream); stream.close(); } catch (Exception e) { e.printStackTrace(); Log.v("img", e.getMessage()); Message msg = new Message(); msg.what = 0; handleMessage(msg); return null; } } this.gView = view; return bmp; } protected void onPostExecute(Bitmap bm) { if (bm != null) { this.gView.setImageBitmap(bm); this.gView.setTag(bm); this.gView = null ; Message msg = new Message(); msg.what = 1; handleMessage(msg); } } public boolean handleMessage(Message msg) { // TODO Auto-generated method stub return false; } }
在 Activity 中直接調用
new CanvasImageTaskCall(){ @Override public boolean handleMessage(Message msg) { switch (msg.what) { case 0: Log.i("test", "圖片載入失敗"); break; case 1: Log.i("test", "圖片載入成功"); break; default: break; } saveButton.setTextColor(Color.WHITE); saveButton.setClickable(true); bitmap = (Bitmap) imageView.getTag(); return super.handleMessage(msg); } }.execute(img);