Handler:是一個消息分發對象,進行發送和處理消息,而且其 Runnable 對象與一個線程的 MessageQueue 關聯。
做用:調度消息,將一個任務切換到某個指定的線程中去執行。git
倘若子線程容許訪問 UI,則在多線程併發訪問狀況下,會使得 UI 控件處於不可預期的狀態。
傳統解決辦法:加鎖,但會使得UI訪問邏輯變的複雜,其次下降 UI 訪問的效率。github
採用單線程模型處理 UI 操做,經過 Handler 切換到 UI 線程,解決子線程中沒法訪問 UI 的問題。網絡
建立一個 handler,經過 handler.post/postDelay,投遞建立的 Runnable,在 run 方法中進行更新 UI 操做。多線程
new Thread(new Runnable() {
@Override
public void run() {併發
/** 耗時操做 */ handler.post(new Runnable() { @Override public void run() { /** 更新UI */ } });
}
}).start();ide
建立一個handler,重寫 handleMessage 方法,根據 msg.what 信息判斷,接收對應的信息,再在這裏更新 UI。post
private Handler handler = new Handler(){this
@Override public void handleMessage(Message msg) { super.handleMessage(msg); switch (msg.what) { //判斷標誌位 case 1: /** 獲取數據,更新UI */ break; } }
};.net
public class WorkThread extends Thread {線程
@Override
public void run() { super.run(); /** 耗時操做 */
Message msg =Message.obtain(); //從全局池中返回一個message實例,避免屢次建立message(如new Message)
msg.obj = data;
msg.what=1; //標誌消息的標誌
handler.sendMessage(msg);
}
new WorkThread().start();
Handler 被做爲 Activity 引用,若是爲非靜態內部類,則會引用外部類對象。當 Activity finish 時,Handler可能並未執行完,從而引發 Activity 的內存泄漏。故而在全部調用 Handler 的地方,都用靜態內部類。
當 Activity finish 時,在 onDestroy 方法中釋放了一些資源。此時 Handler 執行到 handlerMessage 方法,但相關資源已經被釋放,從而引發空指針的異常。
避免
則使用以下方式建立 handler 對象:
/** 爲避免handler形成的內存泄漏 一、使用靜態的handler,對外部類不保持對象的引用 二、但Handler須要與Activity通訊,因此須要增長一個對Activity的弱引用 */ private static class MyHandler extends Handler { private final WeakReference<Activity> mActivityReference; MyHandler(Activity activity) { this.mActivityReference = new WeakReference<Activity>(activity); } @Override public void handleMessage(Message msg) { super.handleMessage(msg); MainActivity activity = (MainActivity) mActivityReference.get(); //獲取弱引用隊列中的activity switch (msg.what) { //獲取消息,更新UI case 1: byte[] data = (byte[]) msg.obj; activity.threadIv.setImageBitmap(activity.getBitmap(data)); break; } } }
並在 onDesotry 中銷燬:
@Override protected void onDestroy() { super.onDestroy(); //避免activity銷燬時,messageQueue中的消息未處理完;故此時應把對應的message給清除出隊列 handler.removeCallbacks(postRunnable); //清除runnable對應的message //handler.removeMessage(what) 清除what對應的message }
public class MainActivity extends AppCompatActivity { ImageView threadIv; ImageView runnableIv; SendThread sendThread; PostRunnable postRunnable; private final MyHandler handler = new MyHandler(this); @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); threadIv = (ImageView) findViewById(R.id.thread_iv); runnableIv = (ImageView) findViewById(R.id.runnable_iv); sendThread = new SendThread(handler); sendThread.start(); postRunnable = new PostRunnable(handler); postRunnable.setRefreshUI(new PostRunnable.RefreshUI() { @Override public void setImage(byte[] data) { runnableIv.setImageBitmap(getBitmap(data)); } }); new Thread(postRunnable).start(); } /** 爲避免handler形成的內存泄漏 一、使用靜態的handler,對外部類不保持對象的引用 二、但Handler須要與Activity通訊,因此須要增長一個對Activity的弱引用 / private static class MyHandler extends Handler { private final WeakReference<Activity> mActivityReference; MyHandler(Activity activity) { this.mActivityReference = new WeakReference<Activity>(activity); } @Override public void handleMessage(Message msg) { super.handleMessage(msg); MainActivity activity = (MainActivity) mActivityReference.get(); //獲取弱引用隊列中的activity switch (msg.what) { //獲取消息,更新UI case 1: byte[] data = (byte[]) msg.obj; activity.threadIv.setImageBitmap(activity.getBitmap(data)); break; } } } private Bitmap getBitmap(byte[] data) { return BitmapFactory.decodeByteArray(data, 0, data.length); } @Override protected void onDestroy() { super.onDestroy(); //避免activity銷燬時,messageQueue中的消息未處理完;故此時應把對應的message給清除出隊列 handler.removeCallbacks(postRunnable); //清除runnable對應的message //handler.removeMessage(what) 清除what對應的message } }
public class PostRunnable implements Runnable { private Handler handler; private RefreshUI refreshUI; byte[] data = null; public PostRunnable(Handler handler) { this.handler = handler; } @Override public void run() { /** 耗時操做 */ final Bitmap bitmap = null; HttpClient httpClient = new DefaultHttpClient(); HttpGet httpGet = new HttpGet("http://i3.17173cdn.com/2fhnvk/YWxqaGBf/cms3/FNsPLfbkmwgBgpl.jpg"); HttpResponse httpResponse = null; try { httpResponse = httpClient.execute(httpGet); if (httpResponse.getStatusLine().getStatusCode() == 200) { data = EntityUtils.toByteArray(httpResponse.getEntity()); } } catch (IOException e) { e.printStackTrace(); } //返回結果給UI線程 handler.post(new Runnable() { @Override public void run() { refreshUI.setImage(data); } }); } public interface RefreshUI { public void setImage(byte[] data); } public void setRefreshUI(RefreshUI refreshUI) { this.refreshUI = refreshUI; } }
public class SendThread extends Thread { private Handler handler; public SendThread(Handler handler) { this.handler = handler; } @Override public void run() { super.run(); /** 耗時操做 */ byte[]data=null; HttpClient httpClient = new DefaultHttpClient(); HttpGet httpGet = new HttpGet("https://d36lyudx79hk0a.cloudfront.net/p0/descr/pc27/3095587d8c4560d8.png"); HttpResponse httpResponse = null; try { httpResponse = httpClient.execute(httpGet); if(httpResponse.getStatusLine().getStatusCode()==200){ data= EntityUtils.toByteArray(httpResponse.getEntity()); } } catch (IOException e) { e.printStackTrace(); } //返回結果給UI線程 doTask(data); } /** 經過handler返回消息 @param data */ private void doTask(byte[] data) { Message msg =Message.obtain(); //從全局池中返回一個message實例,避免屢次建立message(如new Message) msg.obj = data; msg.what=1; //標誌消息的標誌 handler.sendMessage(msg); } }
本文發表於我的博客:http://lavnfan.github.io/,歡迎指教。