android 多线程 场景,精选Android初中级面试题 (三): 深探Handler,多线程,Bitmap
碼個蛋(codeegg) 第 930 次推文
作者:Focusing
鏈接:https://juejin.im/post/5c85cead5188257c6703af47
Handler
1、談談消息機制Handler作用 ?有哪些要素 ?流程是怎樣的 ?
參考回答:負責跨線程通信,這是因為在主線程不能做耗時操作,而子線程不能更新UI,所以當子線程中進行耗時操作后需要更新UI時,通過Handler將有關UI的操作切換到主線程中執行。
具體分為四大要素:
Message(消息):需要被傳遞的消息,消息分為硬件產生的消息(如按鈕、觸摸)和軟件生成的消息。
MessageQueue(消息隊列):負責消息的存儲與管理,負責管理由 Handler發送過來的Message。讀取會自動刪除消息,單鏈表維護,插入和刪除上有優勢。在其next()方法中會無限循環,不斷判斷是否有消息,有就返回這條消息并移除。
Handler(消息處理器):負責Message的發送及處理。主要向消息池發送各種消息事件(Handler.sendMessage())和處理相應消息事件(Handler.handleMessage()),按照先進先出執行,內部使用的是單鏈表的結構。
Looper(消息池):負責關聯線程以及消息的分發,在該線程下從 MessageQueue獲取 Message,分發給Handler,Looper創建的時候會創建一個 MessageQueue,調用loop()方法的時候消息循環開始,其中會不斷調用messageQueue的next()方法,當有消息就處理,否則阻塞在messageQueue的next()方法中。當Looper的quit()被調用的時候會調用messageQueue的quit(),此時next()會返回null,然后loop()方法也就跟著退出。
具體流程如下:
在主線程創建的時候會創建一個Looper,同時也會在在Looper內部創建一個消息隊列。而在創鍵Handler的時候取出當前線程的Looper,并通過該Looper對象獲得消息隊列,然后Handler在子線程中通過MessageQueue.enqueueMessage在消息隊列中添加一條Message。
通過Looper.loop() 開啟消息循環不斷輪詢調用 MessageQueue.next(),取得對應的Message并且通過Handler.dispatchMessage傳遞給Handler,最終調用Handler.handlerMessage處理消息。
2、一個線程能否創建多個Handler,Handler跟Looper之間的對應關系 ?
參考回答:一個Thread只能有一個Looper,一個MessageQueen,可以有多個Handler。
以一個線程為基準,他們的數量級關系是:Thread(1) : Looper(1) : MessageQueue(1) : Handler(N)。
3、軟引用跟弱引用的區別
參考回答:
軟引用(SoftReference):如果一個對象只具有軟引用,則內存空間充足時,垃圾回收器就不會回收它;如果內存空間不足了,就會回收這些對象的內存。只要垃圾回收器沒有回收它,該對象就可以一直被程序使用。
弱引用(WeakReference):如果一個對象只具有弱引用,那么在垃圾回收器線程掃描的過程中,一旦發現了只具有弱引用的對象,不管當前內存空間足夠與否,都會回收它的內存。
兩者之間根本區別在于:只具有弱引用的對象擁有更短暫的生命周期,可能隨時被回收。而只具有軟引用的對象只有當內存不夠的時候才被回收,在內存足夠的時候,通常不被回收。
推薦文章:Java中的四種引用類型:強引用、軟引用、弱引用和虛引用(https://segmentfault.com/a/1190000015282652)
4、Handler 引起的內存泄露原因以及最佳解決方案
泄露原因:Handler 允許我們發送延時消息,如果在延時期間用戶關閉了 Activity,那么該 Activity 會泄露。這個泄露是因為 Message 會持有 Handler,而又因為 Java 的特性,內部類會持有外部類,使得 Activity 會被 Handler 持有,這樣最終就導致 Activity 泄露。
解決方案:將 Handler 定義成靜態的內部類,在內部持有Activity的弱引用,并在Acitivity的onDestroy()中調用handler.removeCallbacksAndMessages(null)及時移除所有消息。
點擊播放 GIF 0.0M
5、為什么系統不建議在子線程訪問UI?
參考回答:Android的UI控件不是線程安全的,如果在多線程中并發訪問可能會導致UI控件處于不可預期的狀態。
這時你可能會問為何系統不對UI控件的訪問加上鎖機制呢?因為:
加鎖機制會讓UI訪問邏輯變的復雜
加鎖機制會降低UI的訪問效率,因為加鎖會阻塞某些線程的執行
6、Looper死循環為什么不會導致應用卡死?
參考回答:
主線程的主要方法就是消息循環,一旦退出消息循環,那么你的應用也就退出了,Looer.loop()方法可能會引起主線程的阻塞,但只要它的消息循環沒有被阻塞,能一直處理事件就不會產生ANR異常。
造成ANR的不是主線程阻塞,而是主線程的Looper消息處理過程發生了任務阻塞,無法響應手勢操作,不能及時刷新UI。
阻塞與程序無響應沒有必然關系,雖然主線程在沒有消息可處理的時候是阻塞的,但是只要保證有消息的時候能夠立刻處理,程序是不會無響應的。
7、使用Handler的postDealy后消息隊列會有什么變化?
參考回答:如果隊列中只有這個消息,那么消息不會被發送,而是計算到時喚醒的時間,先將Looper阻塞,到時間就喚醒它。但如果此時要加入新消息,該消息隊列的對頭跟delay時間相比更長,則插入到頭部,按照觸發時間進行排序,隊頭的時間最小、隊尾的時間最大。
8、可以在子線程直接new一個Handler嗎?怎么做?
參考回答:不可以,因為在主線程中,Activity內部包含一個Looper對象,它會自動管理Looper,處理子線程中發送過來的消息。而對于子線程而言,沒有任何對象幫助我們維護Looper對象,所以需要我們自己手動維護。所以要在子線程開啟Handler要先創建Looper,并開啟Looper循環
推薦文章:Android異步消息處理機制完全解析,帶你從源碼的角度徹底理解(https://blog.csdn.net/guolin_blog/article/details/9991569)
9、Message可以如何創建?哪種效果更好,為什么?
參考回答:可以通過三種方法創建:
直接生成實例Message m = new Message
通過Message m = Message.obtain
通過Message m = mHandler.obtainMessage()
后兩者效果更好,因為Android默認的消息池中消息數量是10,而后兩者是直接在消息池中取出一個Message實例,這樣做就可以避免多生成Message實例。
線程
1、線程池的好處?四種線程池的使用場景,線程池的幾個參數的理解?
使用線程池的好處是減少在創建和銷毀線程上所花的時間以及系統資源的開銷,解決資源不足的問題。如果不使用線程池,有可能造成系統創建大量同類線程而導致消耗完內存或則“過度切換”的問題,歸納總結就是:
重用存在的線程,減少對象創建、消亡的開銷,性能佳。
可有效控制最大并發線程數,提高系統資源的使用率,同時避免過多資源競爭,避免堵塞。
提供定時執行、定期執行、單線程、并發數控制等功能。
Android中的線程池都是直接或間接通過配置ThreadPoolExecutor來實現不同特性的線程池.Android中最常見的類具有不同特性的線程池分別為:
newCachedThreadPool:只有非核心線程,最大線程數非常大,所有線程都活動時會為新任務創建新線程,否則會利用空閑線程 ( 60s空閑時間,過了就會被回收,所以線程池中有0個線程的可能 )來處理任務.
優點:任何任務都會被立即執行(任務隊列SynchronousQuue相當于一個空集合);比較適合執行大量的耗時較少的任務.
newFixedThreadPool:只有核心線程,并且數量固定的,所有線程都活動時,因為隊列沒有限制大小,新任務會等待執行,當線程池空閑時不會釋放工作線程,還會占用一定的系統資源。
優點:更快的響應外界請求
newScheduledThreadPool:核心線程數固定,非核心線程(閑著沒活干會被立即回收數)沒有限制.
優點:執行定時任務以及有固定周期的重復任務
newSingleThreadExecutor:只有一個核心線程,確保所有的任務都在同一線程中按序完成
優點:不需要處理線程同步的問題
通過源碼可以了解到上面的四種線程池實際上還是利用 ThreadPoolExecutor 類實現的
總結
以上是生活随笔為你收集整理的android 多线程 场景,精选Android初中级面试题 (三): 深探Handler,多线程,Bitmap的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: ggplot2箱式图两两比较_作图技巧0
- 下一篇: 如何理性客观地看待人工智能热潮