1如何正確使用Handler?
Handler的工作是依賴于Looper的,而Looper(與消息隊列)又是屬于某一個線程(ThreadLocal是線程內(nèi)部的數(shù)據(jù)存儲類,通過它可以在指定線程中存儲數(shù)據(jù),其他線程則無法獲取到),其他線程不能訪問。因此Handler就是間接跟線程是綁定在一起了。因此要使用Handler必須要保證Handler所創(chuàng)建的線程中有Looper對象并且啟動循環(huán)。因為子線程中默認是沒有Looper的,所以會報錯。正確的使用方法是:
2自定義控件優(yōu)化方案
1 為了加速你的view,對于頻繁調(diào)用的方法,需要盡量減少不必要的代碼。先從onDraw開始,需要特別注意不應(yīng)該在這里做內(nèi)存分配的事情(創(chuàng)建對象),因為它會導(dǎo)致GC,從而導(dǎo)致卡頓。在初始化或者動畫間隙期間做分配內(nèi)存的動作。不要在動畫正在執(zhí)行的時候做內(nèi)存分配的事情。
2 你還需要盡可能的減少onDraw被調(diào)用的次數(shù),大多數(shù)時候?qū)е耾nDraw都是因為調(diào)用了invalidate().因此請盡量減少調(diào)用invaildate()的次數(shù)。如果可能的話,盡量調(diào)用含有4個參數(shù)的invalidate()方法而不是沒有參數(shù)的invalidate()。沒有參數(shù)的invalidate會強制重繪整個view。
3 另外一個非常耗時的操作是請求layout。任何時候執(zhí)行requestLayout(),會使得Android UI系統(tǒng)去遍歷整個View的層級來計算出每一個view的大小。如果找到有沖突的值,它會需要重新計算好幾次。另外需要盡量保持View的層級是扁平化的,這樣對提高效率很有幫助。 如果你有一個復(fù)雜的UI,你應(yīng)該考慮寫一個自定義的ViewGroup來執(zhí)行他的layout操作。與內(nèi)置的view不同,自定義的view可以使得程序僅僅測量這一部分,這避免了遍歷整個view的層級結(jié)構(gòu)來計算大小。這個PieChart 例子展示了如何繼承ViewGroup作為自定義view的一部分。PieChart 有子views,但是它從來不測量它們。而是根據(jù)他自身的layout法則,直接設(shè)置它們的大小。
3談?wù)凙ndroid的事件分發(fā)機制
1、如果事件不被中斷,整個事件流向是一個類U型圖,我們來看下這張圖,可能更能理解U型圖的意思。所以如果我們沒有對控件里面的方法進行重寫或更改返回值,而直接用super調(diào)用父類的默認實現(xiàn),那么整個事件流向應(yīng)該是從Activity---->ViewGroup--->View 從上往下調(diào)用dispatchTouchEvent方法,一直到葉子節(jié)點(View)的時候,再由View--->ViewGroup--->Activity從下往上調(diào)用onTouchEvent方法。
2、dispatchTouchEvent 和 onTouchEvent 一旦return true,事件就停止傳遞了(到達終點)(沒有誰能再收到這個事件)??聪聢D中只要return true事件就沒再繼續(xù)傳下去了,對于return true我們經(jīng)常說事件被消費了,消費了的意思就是事件走到這里就是終點,不會往下傳,沒有誰能再收到這個事件了。
3、dispatchTouchEvent 和 onTouchEvent return false的時候事件都回傳給父控件的onTouchEvent處理。
點擊事件的穿透:我曾經(jīng)遇到這樣一個情況,就是有兩個布局疊加在一起,上層的ListView控件在滑動的時候,有可能會出發(fā)下層控件的點擊相應(yīng)效果。這個時候,就需要讓上層ViewGroup做相應(yīng)的事件攔截和處理,只允許上層中的view相應(yīng)相應(yīng)的事件,然后傳遞到上層的ViewGroup時候,就終止再繼續(xù)向下回傳了??梢詫⑸蠈拥腣iewGroup的onTouchEvent直接返回true,就可以阻止事件向下傳遞,但是同時又不影響上層view的事件處理。
4Android動畫有幾種,對其理解
1 視圖動畫。視圖移動、view自身的實際位置并未移動。
2幀動畫。就和放電影一樣,一幀一幀的播
3屬性動畫。視圖移動、其位置也會隨著移動。
4觸摸返回動畫。發(fā)生觸摸事件時有反饋效果。比如波紋效果
5揭露動畫。從某一個點向四周展開或者從四周向某一點聚合起來。
6轉(zhuǎn)場動畫 & 共享元素。比如切換activity。共享元素一般我們使用在轉(zhuǎn)換的前后兩個頁面有共同元素時。
7視圖狀態(tài)動畫。就是 View 在狀態(tài)改變時執(zhí)行的動畫效果
補間動畫使用完會自動被釋放掉,但是屬性動畫在使用完后一定要記得手動釋放資源。否則會造成內(nèi)存泄露。
5Android 內(nèi)存泄漏的原因以及解決方案
1 內(nèi)存泄漏指對象不再使用,本該被回收,卻因為有其他正在使用的對象持有該對象的引用,而無法被JVM回收
2 內(nèi)存泄漏的影響:
? ? a 應(yīng)用可用內(nèi)存減少,增加堆內(nèi)存壓力
? ? b 頻繁觸發(fā)GC,會降低了應(yīng)用的性能
? ? c 到一定程序會導(dǎo)致內(nèi)存溢出錯誤
3 Android開發(fā)中常見內(nèi)存泄漏及解決辦法
? ? a 靜態(tài)變量生命周期與應(yīng)用的生命周期一樣,如果靜態(tài)變量持有某個Activity的上下文,則對應(yīng)Activity無法釋放,導(dǎo)致內(nèi)存泄漏(單例模式) 解決辦法:使用Application的上下文
????b 匿名內(nèi)部類與非靜態(tài)內(nèi)部類因為都會持有外部類引用,當(dāng)執(zhí)行異步操作易導(dǎo)致內(nèi)存泄漏 解決辦法:將非靜態(tài)內(nèi)部類轉(zhuǎn)為靜態(tài)內(nèi)部類+WeakReferenct的方式
????c Handler消息隊列存在延時消息導(dǎo)致內(nèi)存泄漏 在onDestroy方法中調(diào)用Handler相應(yīng)的方法移除回調(diào)和刪除消息
????d 各種注冊的監(jiān)聽器忘記移除導(dǎo)致內(nèi)存泄漏 解決辦法:在onDestroy方法中取消注冊
????e 資源對象未關(guān)閉導(dǎo)致內(nèi)存泄漏,如(IO,數(shù)據(jù)庫,Bitmap等) 解決辦法:及時關(guān)閉資源
????f 屬性動畫未取消導(dǎo)致內(nèi)存泄漏(如無限輪播圖效果) 解決辦法:onDestroy方法中取消動畫
????g 其他解決辦法:使用AAC框架
4 內(nèi)存泄漏排查工具: AS Monitor,MAT,LeakCanary
5 擴展: Java內(nèi)存管理,GC
6 Handler引起的內(nèi)存泄漏 原因:該線程持有Handler的引用,而Handler也持有Activity的引用,這就導(dǎo)致了Activity不再使用時,GC回收不了Activity 解決:Handler持有的引用最好使用弱引用,在Activity被釋放的時候要記得清空Message,取消Handler對象的Runnable
7 單例模式引起的內(nèi)存泄漏 原因:構(gòu)建該單例的一個實例時需要傳入一個Context,如果此時傳入的是Activity,由于Context會被創(chuàng)建的實例一直持有,當(dāng)Activity進入后臺或者開啟設(shè)置里面的不保留活動時,Activity會被銷毀,但是單例持有它的Context引用,Activity沒法銷毀 解決:對于生命周期比Activity長的對象,要避免直接引用Activity的context,可以考慮使用ApplicationContext
8 非靜態(tài)內(nèi)部類創(chuàng)建靜態(tài)實例引起的內(nèi)存泄漏 原因:非靜態(tài)的內(nèi)部類會自動持有外部類的引用,創(chuàng)建的靜態(tài)實例就會一直持有的引用 解決:可以考慮把內(nèi)部類聲明為靜態(tài)的
9 非靜態(tài)匿名內(nèi)部類引起的內(nèi)存泄漏 原因:如果匿名內(nèi)部類被異步線程使用,可能會引起內(nèi)存泄漏 解決:可以考慮把內(nèi)部類聲明為靜態(tài)的
10 資源對象沒有關(guān)閉引起的內(nèi)存泄漏 原因:資源性對象比如Cursor、File、Bitmap、視頻等,系統(tǒng)都用了一些緩沖技術(shù),在使用這些資源之后沒有關(guān)閉 解決:處理完資源對象的邏輯記得關(guān)閉,最好是形成習(xí)慣現(xiàn)寫一開一關(guān)
11 集合對象沒有及時清理引起的內(nèi)存泄漏 原因:如果集合是static、不斷的往里面添加?xùn)|西、又忘記去清理,肯定會引起內(nèi)存泄漏 解決:集合里面的東西、有加入就應(yīng)該對應(yīng)有相應(yīng)的刪除