Android知識點總結(二)

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
優化
  1. 佈局優化,儘量不嵌套,gone就不繪製;
  2. 列表adapter優化,使用converView,滑動停止在加載圖片;
  3. 背景和圖片的內存分配優化;
  4. 避免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.現在下載速度很慢,試從網絡協議的角度分析原因並優化
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章