1.什么是Handler?
Handler是android 中的一種功能處理機制,它既是一套UI更新機制,也是一種消息處理機制
常用場景:
通知主線程更新UI
MVP中進行傳遞數(shù)據(jù)
...
每一個Handler實例是與擁有MessageQueue( 消息隊列 )的線程關聯(lián)的。每當你創(chuàng)建一個Handler,系統(tǒng)會默認綁定到對應的線程,并且它擁有MessageQueue。
像對于一些新手小白在做應用時,通常會用一些封裝好的網(wǎng)絡框架去做后臺服務器的數(shù)據(jù)請求。我們知道請求數(shù)據(jù)是一個耗時操作,是不能放在主線程去執(zhí)行的,否則就等著ANR吧。他們呢就會在使用這些框架時,將獲取的數(shù)據(jù)直接進行UI的更新。于是就會出現(xiàn)這個錯誤
ERROR/javaBinder(1029):android.view.ViewRoot$CalledFromWrongThreadException:Only the original thread that created a view hierarchy can touch its views.
這個錯誤意思就是不能在非主線程更新UI。
我們常常聽到這句話,Android主線程是線程不安全的。——所以要在主線程來更新
為什么呢?因為子線程可能會有多個,多個線程同時操作一個控件可能會有沖突發(fā)生,所以android就限定了只有主線程可以操作UI。子線程想操作UI就必須要通知主線程來更新。
這里可能有人會問,什么是線程安全,什么是線程不安全?
簡單的說,也就是你的代碼如果對于線程來說原子操作或者多個線程之間的切換不會導致執(zhí)行結(jié)果存在二義性,也就是說我們不用考慮同步的問題也就是線程安全的。
線程安全問題往往都是由全局變量及靜態(tài)變量引起的。
舉個大學里老師常說的例子:
比如一個 ArrayList 類,在添加一個元素的時候,它可能會有兩步來完成:1. 在 Items[Size] 的位置存放此元素;2. 增大 Size 的值。在單線程運行的情況下,如果 Size = 0,添加一個元素后,此元素在位置 0,而且 Size=1; 而如果是在多線程情況下,比如有兩個線程,線程 A 先將元素存放在位置 0。但是此時 CPU 調(diào)度線程A暫停,線程 B 得到運行的機會。線程B也向此 ArrayList 添加元素,因為此時 Size 仍然等于 0 (注意哦,我們假設的是添加一個元素是要兩個步驟哦,而線程A僅僅完成了步驟1),所以線程B也將元素存放在位置0。然后線程A和線程B都繼續(xù)運行,都增加 Size 的值。 那好,現(xiàn)在我們來看看 ArrayList 的情況,元素實際上只有一個,存放在位置 0,而 Size 卻等于 2。這就是“線程不安全”了。
Handler其實如果用熟悉了之后,能做很多有趣強大的事情。比如很多商場里的廣告Banner輪播圖,輪播時間也就是那個時延,都可以用它來實現(xiàn),也很方便。
這里它的基本用法我就不講解了,不過里面有一個需要提及下:
Message message=new Message(); message.what=0; message.obj=bean; handler.sendMessage(message);而有的人這樣寫Message message=handler.obtainMessage(); message.what=0; message.obj=bean; handler.sendMessage(message);那么這兩個message有什么區(qū)別呢,還是那句話,點進去看源碼。。/** * Returns a new {@link android.os.Message Message} from the global message pool. More efficient than * creating and allocating new instances. The retrieved message has its handler set to this instance (Message.target == this). * If you don't want that facility, just call Message.obtain() instead. */ public final Message obtainMessage() { return Message.obtain(this); }/** * Same as {@link #obtain()}, but sets the value for the <em>target</em> member on the Message returned. * @param h Handler to assign to the returned Message object's <em>target</em> member. * @return A Message object from the global pool. */ public static Message obtain(Handler h) { Message m = obtain(); m.target = h; return m; }上面的注釋已經(jīng)說的很清楚,它是從整個Messge池中返回一個新的Message實例,在許多情況下使用它,因為它能避免分配新的對象那么通過調(diào)用obtainMessage方法獲取Message對象就能避免創(chuàng)建對象,從而減少內(nèi)存的開銷了。既然Handler這么好,是不是隨便使用,多多使用啊?我只能說好好用它,因為它還會可能導致你內(nèi)存泄漏,這個詞大家應該并不陌生。Java使用有向圖機制,通過GC自動檢查內(nèi)存中的對象(什么時候檢查由虛擬機決定),如果GC發(fā)現(xiàn)一個或一組對象為不可到達狀態(tài),則將該對象從內(nèi)存中回收。也就是說,一個對象不被任何引用所指向,則該對象會在被GC發(fā)現(xiàn)的時候被回收;另外,如果一組對象中只包含互相的引用,而沒有來自它們外部的引用(例如有兩個對象A和B互相持有引用,但沒有任何外部對象持有指向A或B的引用),這仍然屬于不可到達,同樣會被GC回收。一句話就是,該釋放的沒被釋放常出現(xiàn)的場景: 當使用內(nèi)部類來創(chuàng)建Handler的時候,Handler對象會隱式地持有一個外部類對象(通常是一個Activity)的引用。而此時有一個耗時后臺操作,比如網(wǎng)絡請求,然后通過消息機制通知Handler,然后Handler把數(shù)據(jù)更新到界面。突然,如果用戶在網(wǎng)絡請求過程中關閉了Activity,正常情況下,Activity不再被使用,它就有可能在GC檢查時被回收掉,但由于這時線程尚未執(zhí)行完,而該線程持有Handler的引用(不然它怎么發(fā)消息給Handler?),這個Handler又持有Activity的引用,就導致該Activity無法被回收(即內(nèi)存泄露),直到網(wǎng)絡請求結(jié)束。內(nèi)存泄漏的危害:內(nèi)存泄露的危害就是會使虛擬機占用內(nèi)存過高,導致OOM(內(nèi)存溢出)
對于Android應用來說,就是你的用戶打開一個Activity,使用完之后關閉它,內(nèi)存泄露;又打開,又關閉,又泄露;反復幾次,程序占用內(nèi)存超過系統(tǒng)限制,F(xiàn)C。
解決方法: 1.我們知道,靜態(tài)的內(nèi)部類不會持有外部類的引用,所以你需要將Handler聲明為靜態(tài)類,在Handler中增加一個對Activity的弱引用(WeakReference)。static class MyHandler extends Handler { WeakReference<Activity> mWeakReference; public MyHandler(Activity activity) { mWeakReference=new WeakReference<Activity>(activity); } @Override public void handleMessage(Message msg) { final Activity activity=mWeakReference.get(); if(activity!=null) { if (msg.what == 1) { adapter.notifyDataSetChanged(); } } } }2.在關閉Activity的時候停掉你的后臺線程。如果你的Handler是被delay的Message持有了引用,使用removeCallbacks()方法,把消息對象從隊列移除。最常使用第一種方法。
這就是簡單的一些Handler的介紹,希望對你們有用。
本人原創(chuàng),圖有雷同,純屬巧合,如轉(zhuǎn)載或CV請標明出處,尊重原創(chuàng),謝謝!
新聞熱點
疑難解答