1.ANR application not responding
在主線程這做了耗時操作。
主要原因是:應用程序的響應性是Activity Manager和WindowManager系統服務監視的。
(1)主線程被IO操作阻塞;
(2)主線程中存在耗時的計算;
以下這些操作是發生在主線程中:
- Acitivity的所有生命週期回調都是執行在主線程
- Service默認是執行在主線程。
- BroadcastReceivew的onReceive回調是執行在主線程的。
- 沒有使用子線程的looper的handler的HandleMessage.Post(Runnable)執行在主線程。
- AnsyncTask的回調中除了doinbackgroun,其他都是執行在主線程。
解決:
- Asynctask處理耗時IO操作
- 使用Thread或HandlerThread提高優先級;
- 使用handler來處理工作線程耗時任務;
2 OOM
當前佔用的內存加上我們申請的內存資源超過了Dalvik虛擬機的最大內存限制就會拋出out of mermory異常。
容易混淆的概念:
- 內存溢出 OOM 嚴重會造成程序崩潰
- 內存抖動 在短時間內大量對象被創建,會被馬上釋放,瞬間產生的對象會佔用大量的內存區域,疊加在一起就會觸發內存壓力,觸發gc。
- 內存泄漏 進程中的某些對象,沒有被用的,直接間接應用到其他沒有回收的對象,導致gc不能起作用。
解決
- bitmap:圖片顯示,listview滑動的時候不進行網絡請求;及時的釋放內存;圖片壓縮;inBitmap屬性;捕獲異常;
- listview:convertview /LRU最近最少使用;避免在onDraw方法裏面創建對象;謹慎使用多進程;
3.bitmap
- recycle
- LRU 最近最少使用三級緩存,LinkedhashMap,緩存滿的時候就移除對象,並添加新的對象trimToSize(maxsize)方法;put(),同步代碼;removew(key),同步代碼;<LRU泛型通過一些方法進行添加刪除>
public void trimToSize(int maxSize) {
while (true) {
K key;
V value;
synchronized (this) {
if (size < 0 || (map.isEmpty() && size != 0)) {
throw new IllegalStateException(getClass().getName()
+ ".sizeOf() is reporting inconsistent results!");
}
if (size <= maxSize) {
break;
}
Map.Entry<K, V> toEvict = map.eldest();
if (toEvict == null) {
break;
}
key = toEvict.getKey();
value = toEvict.getValue();
map.remove(key);
size -= safeSizeOf(key, value);
evictionCount++;
}
entryRemoved(true, key, value, null);
}
}
- 計算InSampleSize
- 縮略圖 根據InSampleSize值,thumnail(),inJustDecodeBounds=false 不加載到內存中,然後計算出合適的inSampleSize,再設置inJustDecodeBounds=true;
- 三級緩存 :網絡緩存 內存緩存 本地緩存(SD)
4.UI卡頓
60fps 每秒60幀—>16ms ;overdraw過度繪製,大量的重疊背景,減少紅色;
原因分析:
- 人爲在UI線程中做輕微耗時操作,導致UI線程卡頓;
- 佈局layout過於複雜,無法在16ms內完成渲染;
- 同一時間動畫執行次數太多,導致CPU或者GPU負載過重;
- VIew過度繪製;
- view頻繁的measure layout,導致measure、layout累計耗時過多,整個View頻繁的重新渲染;
- 內存頻繁觸發GC過多,導致暫時阻塞渲染操作;
- 冗餘資源邏輯等導致加載或者執行緩慢;
- anr
優化
- 佈局優化,儘量不嵌套,gone就不繪製;
- 列表adapter優化,使用converView,滑動停止在加載圖片;
- 背景和圖片的內存分配優化;
- 避免ANR
5 內存泄漏
內存分配策略
(1)靜態存儲區:方法區 ,靜態數據,成員變量。整個運行期都存在
(2)棧區:方法變量。存放函數內的局部變量,形參和函數返回值。方法結束之後會自動釋放,不需要手動管理。容量有限。系統自動分配回收。
(3)堆區:new 出來的。動態的。不使用垃圾回收機制會回收。全局得,(完全二叉樹)
如何管理內存
java中內存泄漏:
無用的對象持續佔有內存,或者無用的內存得不到及時釋放,造成內存空間的泄漏;
android中的內存泄漏
單例;匿名內部類;handler;避免使用static變量;資源沒有關閉造成的;AsyncTask造成的;
6 內存管理
分配機制;回收機制;
特點
更少的佔用內存;在合適的時候,合適的釋放系統資源;在系統內存緊張情況下,釋放大部分不重要的資源,爲將來內存提供可用的內存空間;合理的生命週期中保存或者還原重要數據。
內存優化方法
- 當service完成任務後,儘量停止它。intentService自動釋放
- UI不可見時候,釋放一些只有UI使用的資源;
- 內存緊張儘可能多的釋放掉一些非重要資源
- 避免濫用Bitmap導致的內存浪費,recycle();軟引用,LRU
- 使用針對內存優化過的數據容器。少使用枚舉。正常的2倍多
- 避免使用依賴注入的框架
- 使用ZIP對齊的APK
- 使用多進程
7.冷啓動
啓動應用之前,系統中沒有該應用的任何進程信息。
熱啓動:
用戶使用返回鍵退出應用,然後馬上又重新啓動應用。
冷啓動時間計算:
從創建進程開始計算,到完成視圖的第一次繪製(activyt內容對用戶可見)爲止。
冷啓動流程:
Zygote進程中fork創建一個新的進程,創建和初始化Applicatin、創建MainActiviy.inflate佈局、當onCreate() onStart() onResume()方法都走完。contentView的measure onlayout ondraw走完。
冷啓動優化:
- 減少onCreate()方法的工作量;
- 不要讓Application參與業務的操作;
- 不要在Application進行耗時操作;
- 不要用靜態變量的方式在Application中保存數據;
- 減少佈局複雜性 mainThread不做初始化,初始化放在子線程中
8.其他優化
- 不要用靜態變量存儲數據;
- sharepreference問題:不能跨進程同步;文件過大;
- 內存對象的序列化,Serializeble Java會產生大量的臨時變量,Parcelable Android <要保存在磁盤上的數據不能序列化>
- 避免UI中做繁重的操作
9.架構設計
- MVC 耦合性好;可擴展性好;
- MVP View <–> Presenter–> Model;
- MVVM View<–同步—>ViewModel<---->Model。View:不做任何業務邏輯數據處理,對應Activity和XML,負責View的繪製以及用戶交互;Model:實體模型;ViewModel:負責完成View與Model間的交互,負責業務邏輯。
10. Android 插件化
來源:65536/64K
解決的問題:動態加載apk(DexClassLoader 加載jar文件的字節碼,PathClassLoader加載文件目錄下的apk),資源加載(AssetManager,Method),代碼加載(綁定activity生命週期,通過反射);
11.Android 熱更新
- Dexposed AndFix Nuwa
- DexClassLoader
- PathClassLoader
熱修復機制:dexElements,ClassLoader會遍歷這個數組
12.進程保活
進程優先級 高到低
- Foreground process 前臺進程
- Visible process 可見進程
- Service process 服務進程 數據計算,音樂播放…
- Background process 後臺進程
- Empty process 緩存作用
回收策略
Low memory killer:評分機制。分數高的進程判定爲bad進程,殺死並釋放內存。
oom_ODJ:判別進程優先級,越大,優先級越低就越容易被回收。
進程保活方案:
- 利用系統廣播拉活
- 利用系統service拉活
- 利用Native進程拉活<Android5.0以後失效> fork進行監控主進程,利用native拉活
- 利用JobScheduler機制拉活<Android5.0以後>
- 利用賬號同步機制拉活<新版本應該也失效了>
13.final finally finalize區別和用法
- final 關鍵字,屬性不可改變,方法不可以覆蓋(只能使用),類不可以繼承;
- finally 異常處理語句中的一部分,表示總是執行;(不管異常是否執行)
- finalize Object類中的一個方法,在垃圾收集器執行的時候調用被回收對象的此方法,供垃圾收集時的其他資源回收,例如關閉文件。
14.retrofit的優勢
15.view和surfaceview區別
(1)view在UI線程中去更新自己,在非UI線程更新會報錯,當在主線程更新View時如果耗時過長也會出錯;
surfaceview在一個子線程中更新畫面;
(2)surfaceview不支持平移,縮放,旋轉等動畫,Android7.0及以上的支持;
(3)View適用於主動更新。SurfaceView適用於被動更新,比如頻繁的刷新。
(4)SurfaceView可以控制刷新頻率。SurfaceView底層利用雙緩存機制,繪圖時候不會出現閃爍問題;(雙緩衝技術把要處理的圖片在內存中處理好之後,把要畫的東西畫到一個內存區域,然後整體的一次性畫出來,將其顯示在屏幕上)
SurfaceView 使用步驟:
- 首先要繼承SurfaceView,實現SurfaceHolder.Callback接口
- 重寫方法:
surfaceChangeed:surface大小或者格式發生變化的時候,在surfaceCreated調用後該函數至少會被調用一次;
surfaceCreated:Surface創建時觸發,一般在這個函數開啓繪圖線程(新的線程,不要在這個線程中繪製Surface); - 利用getHolder()獲取SurfaceHolder對象,調用SurfaceHolder.addCallback添加回調
- SurfaceHolder.lockCanvas獲取Canvas對象並鎖定畫布,調用Canvas繪圖,SurfaceHolder.unlockCanvasAndPost結束鎖定畫布,提交改變;
16.hashMap
https://note.youdao.com/web/#/file/recent/note/WEB5aa71cf3a9799350357e3fc77ea56748/
public class HashMapTestTest {
HashMap<String, String> mStringStringHashMap = new HashMap<>();
HashMap<Integer, String> mIntegerStringHashMap = new HashMap<>();
@Test
public void test() {
mStringStringHashMap.put(null, "A");
System.out.println("獲取:" + mStringStringHashMap.get(null));//A key可以爲空
mStringStringHashMap.put(null, "B");
System.out.println("獲取:" + mStringStringHashMap.get(null));//B value會變,不是覆蓋,next值改變了
mStringStringHashMap.put("A", "C");
mStringStringHashMap.put("A", "C");
mStringStringHashMap.put("A", "D");
System.out.println("獲取:" + mStringStringHashMap.get("A"));//D
mIntegerStringHashMap.put(null, "A");
mIntegerStringHashMap.put(null, null);
System.out.println("獲取:" + mIntegerStringHashMap.get(null));//null key value都可以爲空
}
}
元素越多碰撞機率就越高。
hashmap底層是數組加鏈表實現的,加載因子0.75f容量的0.75就開始擴容,擴大一倍,(數組大小是16 hashmap中的元素超過16*0.75=12個的時候就開始擴容到2 * 16=32 )
HashMap和ArrayMap各自優勢:
- (1)查找效率
hashmap因爲是根據其hashcode計算出index。所以其查找效率是隨着數組增大而增加的。ArrayMap使用的是二分查找,所以,當數組長度每增加一倍時候,就需要多進行一次判斷,效率下降。 - (2)擴容數量
- hashmap是雙倍擴容;ArrayMap每次擴容的時候,如果size長度大於8時申請size*1.5倍,大於4小於8申請8個,小於4時申請4個;
- (3)擴容效率
hashmap每次擴容的時候重新計算每個數組成員的位置,然後放到新的位置;
ArrayMap則是直接使用System.arraycopy;(直接調用的是c層代碼)
所以效率上arrayMap更佔優勢。 - (4)內存耗費
- arrayMap能夠重複利用因爲數據擴容而遺留下來的數組空間,方便下一個ArrayMap的使用
總結:數據量小的時候使用arrayMap數據量大的時候使用hashMap;
16.抽象類和接口的區別
抽象類要被子類繼承不能被實例化,接口要被類實現;
接口只能做方法聲明,抽象類可以做方法聲明,也可以做方法實現;
參數 | 抽象類 | 接口 |
---|---|---|
默認方法的實現 | 他可以有默認的方法實現 | 接口完全抽象,不存在方法的實現 |
實現 | 子類使用extends關鍵字來繼承抽象類。如果子類不是抽象類的話,它需要提供抽象類中所有聲明的方法的實現。 | 子類使用關鍵字implements來實現接口,它需要提供接口中所有聲明的方法的實現。 |
構造器 | 抽象類可以有構造器 | 接口不能有構造器 |
與正常的Java類的區別 | 不能實例化抽象類 | 完全不能的類型 |
訪問修飾符 | 抽象方法可以有public protected default這些修飾符 | 接口默認修飾符是public,不可以使用其他修飾符 |
main方法 | 可以有main方法並且可以運行它 | 接口沒有main方法,不能運行它 |
多繼承 | 抽象方法可以繼承一個類和實現多個接口 | 接口只可以繼承一個或者多個其他接口 |
速度 | 比接口速度快 | 稍微慢一些,需要去尋找在類中實現的方法; |
添加新方法 | 如果你往抽象類中增加新的方法,可以給他提供默認的實現。不需要改變現有的代碼 | 往接口中添加方法,必須改變實現該接口的類。 |
- 1.android內部是怎麼實現發送延時消息
- 2.怎麼減小so包的體積
- 3.怎麼給一個應用加固
- 4.怎麼從trace.log中找到ANR的原因及出現的地方
- 5.什麼是NDK庫?
- 6.如何在jni中註冊native函數,有幾種註冊方式?
- 7.Binder怎麼實現進程間通信的
- 8.怎麼檢測內存泄漏的
- 9.現在下載速度很慢,試從網絡協議的角度分析原因並優化