背景
知其然要知其所以然袒炉,為什么會有Handler的出現(xiàn)?舉個例子,假設我們在一個Thread中直接刷新某個TextView沈条,并且每毫秒就刷新一次,那么TextView的繪制會瘋掉诅炉,而且用戶體驗也不好蜡歹。所以為了控制UI的刷新頻率,Android規(guī)定非UI線程不能直接控制UI組件汞扎,只能通過Handler來處理季稳。
概念解析
非UI線程計算出結(jié)果后,將結(jié)果封裝到Message里澈魄,調(diào)用Handler對象將消息放到MessageQueue消息隊列里景鼠,然后由Looper按照自己的節(jié)奏從隊列中取出消息,交給Handler對象的handleMessage方法處理痹扇。
實例化Message
通常使用Message類里的靜態(tài)方法obtain()铛漓,該方法有多個重載版本可供選擇;它的創(chuàng)建并不一定是直接創(chuàng)建一個新的實例鲫构,而是先從Message Pool(消息池)中看有沒有可用的Message實例浓恶,存在則直接取出返回這個實例。如果Message Pool中沒有可用的Message實例结笨,則才用給定的參數(shù)創(chuàng)建一個Message對象包晰。對于Message對象,一般并不推薦直接使用它的構(gòu)造方法得到炕吸,而是建議通過使用Message.obtain()這個靜態(tài)的方法或者Handler.obtainMessage()獲取伐憾。除了上面這種方式,也可以通過Handler對象的obtainMessage()獲取一個Message實例赫模。
[圖片上傳失敗...(image-911b1-1533626470342)]
Message可以傳遞什么值
可以直接賦值一個對象給message.obj树肃,直接賦值個flag給message.what(通常用于區(qū)分各個Message),也可以message.setData(bundle),而Bundle對象里可以放各種對象陪毡。
Handler對象發(fā)送定義好的Message
[圖片上傳失敗...(image-e6ba85-1533626470342)]
通過上圖可以看到,
sendEmptyMessage(int what):直接發(fā)送一個flag
sendEmptyMessageAtTime(int what,long uptimeMillis):指定時間發(fā)送
sendEmptyMessageDelayed(int what,long uptimeMillis):延遲發(fā)送
sendMessageAtFrontOfQueue(Message msg):把消息發(fā)到隊列前面
MessageQueue和Looper
Looper是MessageQueue(消息隊列)的管理者兵多。主線程創(chuàng)建時劣像,會創(chuàng)建一個默認的Looper對象乡话,而Looper對象的創(chuàng)建,將自動創(chuàng)建一個MessageQueue驾讲。其他非主線程蚊伞,不會自動創(chuàng)建Looper,要需要的時候吮铭,通過調(diào)用prepare函數(shù)來實現(xiàn)时迫。
為什么Handler會引起的內(nèi)存溢出
1.Android App啟動的時候,Android Framework 為主線程創(chuàng)建一個Looper對象,這個Looper對象將貫穿這個App的整個生命周期,它實現(xiàn)了一個消息隊列(Message Queue),并且開啟一個循環(huán)來處理Message對象。而Framework的主要事件都包含著內(nèi)部Message對象,當這些事件被觸發(fā)的時候,Message對象會被加到消息隊列中執(zhí)行谓晌。
2.當一個 Handler被實例化時,它將和主線程Looper對象的消息隊列相關聯(lián),被推到消息隊列中的Message對象將持有一個 Handler的引用以便于當Looper處理到這個Message的時候,Framework執(zhí)行Handler的 handleMessage(Message)方法掠拳。
3.在 Java 語言中,非靜態(tài)匿名內(nèi)部類將持有一個對外部類的隱式引用,而靜態(tài)內(nèi)部類則不會。
當 Activity被finish()掉,Message將存在于消息隊列中長達10分鐘的時間才會被執(zhí)行到纸肉。這個Message持有一個對Handler的引用,Handler也會持有一個對于外部類 (SampleActivity)的隱式引用,這些引用在Message被執(zhí)行前將一直保持,這樣會導致Activity的上下文不被垃圾回收機制回收, 同時也會泄露應用程序的資源(views and resources)溺欧。
在實際開發(fā)中,如果內(nèi)部類的生命周期和Activity的生命周期不一致(比如上面那種,Activity finish()之后要等10分鐘,內(nèi)部類的實例才會執(zhí)行),則在Activity中要避免使用非靜態(tài)的內(nèi)部類,這種情況,就使用一個靜態(tài)內(nèi)部類,同時持有一個對Activity的WeakReference。
使用實例
//定義該類柏肪,使用WeakRefrence是為了防止內(nèi)存泄露
private static class MyHandler extends Handler {
private final WeakReference<OrderProFragment> mFragment;
private MyHandler(OrderProFragment activity) {
mFragment = new WeakReference<OrderProFragment>(activity);
}
@Override
public void handleMessage(Message msg) {
OrderProFragment fragment = mFragment.get();
if(fragment!=null){
fragment.handle(msg);
}
}
};
//定義處理消息方法
private void handle(Message msg){
if (msg.what == RUNNING) {
baseShowLog("mHandler:RUNNING");
} else if (msg.what == FINISH) {
baseShowLog("mHandler:FINISH");
}
}
實例化該內(nèi)部類:
MyHandler mMyHandler=new MyHandler(this);
在非UI線程中發(fā)送消息
private void outsideRun(){
mPayLeftTime--;
if (mPayLeftTime == 0) {
mMyHandler.sendEmptyMessage(FINISH);
} else if (mPayLeftTime > 0) {
mMyHandler.sendEmptyMessage(RUNNING);
}
}
最后別忘了
@Override
protected void onDestroy() {
super.onDestroy();
if(mMyHandler!=null)mMyHandler.removeCallbacksAndMessages(null);
}