Android知識點彙總

Android 進階

Android平臺的優點和不足

Android平臺手機 5大優點:html

開放性:Android平臺首先就是其開放性,開發的平臺容許任何移動終端廠商加入到Android聯盟中來。顯著的開放性能夠使其擁有更多的開發者;java

掙脫運營商的束縛:在過去很長的一段時間,手機應用每每受到運營商制約,使用什麼功能接入什麼網絡,幾乎都受到運營商的控制,而Android用戶能夠更加方便地鏈接網絡,運營商的制約減小;android

豐富的硬件選擇:因爲Android的開放性,衆多的廠商會推出千奇百怪,功能特點各具的多種產品。功能上的差別和特點,卻不會影響到數據同步、甚至軟件的兼容;git

開發商不受任何限制:Android平臺提供給第三方開發商一個十分寬泛、自由的環境,不會受到各類條條框框的阻擾;github

無縫結合的Google應用: Android平臺手機將無縫結合這些優秀的Google服務如地圖、郵件、搜索等;web

Android平臺手機幾大不足:算法

安全和隱私:因爲手機與互聯網的緊密聯繫,我的隱私很可貴到保守。除了上網過程當中經意或不經意留下的我的足跡,Google這個巨人也時時站在你的身後,洞穿一切;數據庫

過度依賴開發商缺乏標準配置:在Android平臺中,因爲其開放性,軟件更多依賴第三方廠商,好比Android系統的SDK中就沒有內置音樂播放器,所有依賴第三方開發,缺乏了產品的統一性;數組

同類機型用戶不多:在很多手機論壇都會有針對某一型號的子論壇,對一款手機的使用心得交流,並分享軟件資源。而對於Android平臺手機,因爲廠商豐富,產品類型多樣,這樣使用同一款機型的用戶愈來愈少,缺乏統一機型的程序強化。瀏覽器

簡述apk打包過程

1:打包資源文件,生成R.java文件 輸入:res文件,Assets文件,AndroidManifest.xml文件,Android基礎類庫(Android.jar文件) 輸出:R.java,resources.arsc 工具:aapt 工具位置:SDK\build-tools\29.0.0\aapt.exe

2:處理aidl文件,生成相應java文件 輸入:源碼文件,aidl文件,framework.aidl文件 輸出:對應的.java文件 工具:aidl工具 工具位置:SDK\build-tools\29.0.0\aidl.exe

3:編譯工程源代碼,生成相應class文件 輸入:源碼文件(包括R.java和AIDL生成的.java文件),庫文件(jar文件) 輸出:.class文件 工具:javac工具 工具位置:Java\jdk1.8.0_201\bin\javac.exe

4:轉換全部class文件,生成classes.dex文件 輸入:.class文件(包括Aidl生成.class文件,R生成的.class文件,源文件生成的.class文件),庫文件(.jar文件) 輸出:.dex文件 工具:javac工具 工具位置:Java\jdk1.8.0_201\bin\javac.exe

5:打包生成apk文件 輸入:打包後的資源文件,打包後類文件(.dex文件),libs文件(包括.so文件) 輸出:未簽名的.apk文件 工具:apkbuilder.bat工具已廢棄,改成sdklib.jar工具 工具位置:E:\SDK\tools\lib\sdklib.jar

6:對apk文件進行簽名 輸入:未簽名的.apk文件 輸出:簽名的.apk文件 工具: jarsigner工具 apksigner工具 工具位置: Java\jdk1.8.0_201\bin\jarsigner.exe SDK\build-tools\29.0.0\lib\apksigner.jar

7:對簽名後的apk文件進行對齊處理 輸入:簽名後的.apk文件 輸出:對齊後的.apk文件 工具:zipalign工具 工具位置:SDK\build-tools\29.0.0\zipalign.exe

注:工具位置基於win平臺. 參考鏈接: developer.android.com/studio/buil… blog.csdn.net/jiangwei091…

LruCache 算法源碼解析

咱們先看下LruCache算法的構造方法。

public LruCache(int maxSize) {
        if (maxSize <= 0) {
            throw new IllegalArgumentException("maxSize <= 0");
        }
        this.maxSize = maxSize;
        this.map = new LinkedHashMap<K, V>(0, 0.75f, true);
    }
複製代碼

從構造方法的源碼咱們能夠看到,在這段代碼中咱們主要作了兩件事。第一是判斷下傳遞來的最大分配內存大小是否小於零,若是小於零則拋出異常,由於咱們若是傳入一個小於零的內存大小就沒有意義了。以後在構造方法內存就new了一個LinkHashMap集合,從而得知LruCache內部實現原理果真是基於LinkHashMap來實現的。

以後咱們再來看下存儲緩存的put()方法。

public final V put(K key, V value) {
        if (key == null || value == null) {
            throw new NullPointerException("key == null || value == null");
        }
 
        V previous;
        synchronized (this) {
            putCount++;
            size += safeSizeOf(key, value);
            previous = map.put(key, value);
            if (previous != null) {
                size -= safeSizeOf(key, previous);
            }
        }
 
        if (previous != null) {
            entryRemoved(false, key, previous, value);
        }
 
        trimToSize(maxSize);
        return previous;
    }
複製代碼

從代碼中咱們能夠看到,這個put方法內部其實沒有作什麼很特別的操做,就是對數據進行了一次插入操做。可是咱們注意到最後的倒數第三行有一個trimToSize()方法,那麼這個方法是作什麼用的吶?咱們點進去看下。

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);
        }
    }
複製代碼

咱們能夠看到,這個方法原來就是對內存作了一次判斷,若是發現內存已經滿了,那麼就調用map.eldest()方法獲取到最後的數據,以後調用map.remove(key)方法,將這個最近最少使用的數據給剔除掉,從而達到咱們內存不炸掉的目的。

咱們再來看看get()方法。

public final V get(K key) {
        //key爲空拋出異常
        if (key == null) {
            throw new NullPointerException("key == null");
        }
 
        V mapValue;
        synchronized (this) {
            //獲取對應的緩存對象
            //get()方法會實現將訪問的元素更新到隊列頭部的功能
            mapValue = map.get(key);
            if (mapValue != null) {
                hitCount++;
                return mapValue;
            }
            missCount++;
        }
複製代碼

get方法看起來就是很常規的操做了,就是經過key來查找value的操做,咱們再來看看LinkHashMap的中get方法。

public V get(Object key) {
        LinkedHashMapEntry<K,V> e = (LinkedHashMapEntry<K,V>)getEntry(key);
        if (e == null)
            return null;
        //實現排序的關鍵方法
        e.recordAccess(this);
        return e.value;
    }
複製代碼

調用recordAccess()方法以下:

void recordAccess(HashMap<K,V> m) {
            LinkedHashMap<K,V> lm = (LinkedHashMap<K,V>)m;
            if (lm.accessOrder) {
                lm.modCount++;
                remove();
                addBefore(lm.header);
            }
        }
複製代碼

因而可知LruCache中維護了一個集合LinkedHashMap,該LinkedHashMap是以訪問順序排序的。當調用put()方法時,就會在結合中添加元素,並調用trimToSize()判斷緩存是否已滿,若是滿了就用LinkedHashMap的迭代器刪除隊尾元素,即最近最少訪問的元素。當調用get()方法訪問緩存對象時,就會調用LinkedHashMap的get()方法得到對應集合元素,同時會更新該元素到隊頭。

談談熱修復的原理

咱們知道Java虛擬機 —— JVM 是加載類的class文件的,而Android虛擬機——Dalvik/ART VM 是加載類的dex文件, 而他們加載類的時候都須要ClassLoader,ClassLoader有一個子類BaseDexClassLoader,而BaseDexClassLoader下有一個數組——DexPathList,是用來存放dex文件,當BaseDexClassLoader經過調用findClass方法時,實際上就是遍歷數組,找到相應的dex文件,找到,則直接將它return。而熱修復的解決方法就是將新的dex添加到該集合中,而且是在舊的dex的前面,因此就會優先被取出來而且return返回。

簡述app啓動過程

App啓動通常分爲兩種:

  1. 冷啓動:當應用啓動的時候,後臺沒有當前應用的進程,這時系統會建立一個新的進程分配給應用。
  2. 熱啓動:當前應用已經打開,可是被按下返回鍵或者Home鍵退出到桌面或者去到其餘App,當再次回到應用時就是熱啓動。

這裏主要介紹冷啓動,大體分爲5個步驟:

  1. 當點擊桌面圖標,就會利用Launcher經過Binder進程間通訊機制通知ActivityManagerService(AMS),它要啓動一個Activity;
  2. AMS獲得Launcher的通知,就會新建一個Task去準備啓動Activity,並經過Binder機制通知Launcher進入Paused狀態;
  3. Launcher獲得消息,就會直接掛起,並經過Binder告訴AMS我已經Paused了;AMS知道了Launcher已經掛起以後,就能夠放心的爲新的Activity準備啓動工做了,首先,APP確定須要一個新的進程去進行運行,因此須要建立一個新進程,這個過程是須要Zygote參與的,AMS經過Socket去和Zygote協商,而後利用Zygote.fork()建立一個新的進程,在這個進程裏啓動ActivityThread類,這就是每個應用程序都有一個ActivityThread與之對應的緣由;
  4. 進程建立好了,經過調用上述的ActivityThread的main方法,這是應用程序的入口,在這裏開啓Looper消息循環隊列,這也是主線程默認綁定Looper的緣由;(另外,ActivityThread經過Binder將一個ApplicationThread類型的Binder對象傳遞給AMS,以便之後AMS可以經過這個Binder對象和它進行通訊);
  5. 這時候,App尚未啓動完,要永遠記住,四大組件的啓動都須要AMS去啓動,將上述的應用進程信息註冊到AMS中,因此AMS在經過BinderActivityThread,如今一切準備就緒,它能夠真正執行Activity的啓動操做了。

補充知識: Zygote zygote名字翻譯叫受精卵,zygote進程的建立是由Linux系統中init進程建立的,Android中全部的進程都是直接或者間接的由init進程fork出來的,Zygote進程負責其餘進程的建立和啓動,好比建立SystemServer進程。當須要啓動一個新的android應用程序的時候,ActivityManagerService就會經過Socket通知Zygote進程爲這個應用建立一個新的進程。

Launcher 咱們要知道手機的桌面也是一個App咱們叫它launcher,每個手機應用都是在Launcher上顯示,而Launcher的加載是在手機啓動的時候加載Zygote,而後Zygote啓動SystenServer,SystenServer會啓動各類ManageService, 包括ActivityManagerService,並將這些ManageService註冊到ServiceManage 容器中,而後ActivityManagerService就會啓動Home應用程序Launcher.

ActivityManagerService ActivityManagerService咱們簡稱AMS,四大組件都歸它管,四大組件的跨進程通訊都要和它合做。

Binder Binder是Android跨進程通訊(IPC)的一種方式,也是Android系統中最重要的特性之一,android 四大組件以及不一樣的App都運行在不一樣的進程,它則是各個進程的橋樑將不一樣的進程粘合在一塊兒。

ActivityThread 首先ActivityThread並非一個Thread,其做用就是在main方法內作消息循環。那咱們常說的主線程是什麼?主線程就是承載ActivityThread的Zygote fork而建立的進程。 ActivityThread的調用是在ActivityManageService.startProcessLocked()方法裏調用並建立,這個類主要作了這幾個事:

  1. 建立Looper,開啓Looper循環
  2. 建立內部類 H,H繼承於Handler 用於跨進程通訊切換線程
  3. 建立ApplicationThread跨進程Binder對象mAppThread。 這裏要說一點,ActivityThread經過ApplicationThread與AMS進行通訊,ApplicationThread經過H與ActivityThread進行通訊(handler機制),處理Activity的事務。

From GavinCui12

一、點擊桌面應用圖標,Launcher進程將啓動Activity(MainActivity)的請求以Binder的方式發送給了AMS。

二、AMS接收到啓動請求後,交付ActivityStarter處理Intent和Flag等信息,而後再交給ActivityStackSupervisior/ActivityStack 處理Activity進棧相關流程。同時以Socket方式請求Zygote進程fork新進程。

三、Zygote接收到新進程建立請求後fork出新進程。

四、在新進程裏建立ActivityThread對象,新建立的進程就是應用的主線程,在主線程裏開啓Looper消息循環,開始處理建立Activity。

五、ActivityThread利用ClassLoader去加載Activity、建立Activity實例,並回調Activity的onCreate()方法,這樣便完成了Activity的啓動。

Android虛擬機

ART和DVM(Dalvik)區別

什麼是Dalvik: Dalvik是Google公司本身設計用於Android平臺的Java虛擬機。 它能夠支持已轉換爲.dex(即Dalvik Executable)格式的Java應用程序的運行。 .dex格式是專爲Dalvik應用設計的一種壓縮格式,適合內存和處理器速度有限的系統。 Dalvik通過優化,容許在有限的內存中同時運行多個虛擬機的實例,而且每個Dalvik應用做爲獨立的Linux進程執行。 獨立的進程能夠防止在虛擬機崩潰的時候全部程序都被關閉。

什麼是ART: 與Dalvik不一樣,ART使用預編譯(AOT,Ahead-Of-Time)。 也就是在APK運行以前,就對其包含的Dex字節碼進行翻譯,獲得對應的本地機器指令,因而就能夠在運行時直接執行了。 ART應用安裝的時候把dex中的字節碼將被編譯成本地機器碼,以後每次打開應用,執行的都是本地機器碼。 去除了運行時的解釋執行,效率更高,啓動更快。

區別:

  1. Dalvik每次都要編譯再運行,Art只會首次啓動編譯
  2. Art佔用空間比Dalvik大(原生代碼佔用的存儲空間更大),就是用「空間換時間」
  3. Art減小編譯,減小了CPU使用頻率,使用明顯改善電池續航
  4. Art應用啓動更快、運行更快、體驗更流暢、觸感反饋更及時

參考官方文檔: source.android.com/devices/tec…

Android基礎

github-sample

Serializable和Parcelable的區別

Serializable(Java自帶): Serializable是序列化的意思,表示將一個對象轉換成可存儲或可傳輸的狀態。序列化後的對象能夠在網絡上進行傳輸,也能夠存儲到本地。

Parcelable(android 專用): 除了Serializable以外,使用Parcelable也能夠實現相同的效果, 不過不一樣於將對象進行序列化,Parcelable方式的實現原理是將一個完整的對象進行分解, 而分解後的每一部分都是Intent所支持的數據類型,這樣也就實現傳遞對象的功能了。

區別:

  1. 在使用內存的時候,Parcelable 類比Serializable性能高,因此推薦使用Parcelable類。
  2. Serializable在序列化的時候會產生大量的臨時變量,從而引發頻繁的 GC。
  3. Parcelable 不能使用在要將數據存儲在磁盤上的狀況。儘管 Serializable 效率低點,但在這種狀況下,仍是建議你用Serializable 。

實現:

  1. Serializable 的實現,只須要繼承Serializable 便可。這只是給對象打了一個標記,系統會自動將其序列化。
  2. Parcelabel 的實現,須要在類中添加一個靜態成員變量 CREATOR,這個變量須要繼承Parcelable.Creator 接口,(通常利用編譯器能夠自動生成)。

參考自簡書:www.jianshu.com/p/a60b609ec…

From Noble_JIE

從出生來來講,Serializable 是java的方法,Parcelable 是android獨有的序列化反序列化方法 從用法上來講,Serializable 比 Parcelable 簡單,全部類實現Serializable便可,Parcelable須要對對全部屬性及成員變量進行Creator 。 從性能上來講,Parcelable 性能要高於Serializable。

怎樣避免和解決ANR

Application Not Responding,即應用無響應。避免ANR最核心的一點就是在主線程減小耗時操做。一般須要從那個如下幾個方案下手:

a)使用子線程處理耗時IO操做

b)下降子線程優先級,使用Thread或者HandlerThread時,調用Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND)設置優先級,不然仍然會下降程序響應,由於默認Thread的優先級和主線程相同

c)使用Handler處理子線程結果,而不是使用Thread.wait()或者Thread.sleep()來阻塞主線程

d)Activity的onCreate和onResume回調中儘可能避免耗時的代碼

e)BroadcastReceiver中onReceiver代碼也要儘可能減小耗時操做,建議使用intentService處理。intentService是一個異步的,會自動中止的服務,很好解決了傳統的Service中處理完耗時操做忘記中止並銷燬Service的問題

Broadcast 註冊方式與區別

Broadcast廣播,註冊方式主要有兩種.

第一種是靜態註冊,也可成爲常駐型廣播,這種廣播須要在Androidmanifest.xml中進行註冊,這中方式註冊的廣播,不受頁面生命週期的影響,即便退出了頁面,也能夠收到廣播這種廣播通常用於想開機自啓動啊等等,因爲這種註冊的方式的廣播是常駐型廣播,因此會佔用CPU的資源。

第二種是動態註冊,而動態註冊的話,是在代碼中註冊的,這種註冊方式也叫很是駐型廣播,收到生命週期的影響,退出頁面後,就不會收到廣播,咱們一般運用在更新UI方面。這種註冊方式優先級較高。最後須要解綁,否會會內存泄露

廣播是分爲有序廣播和無序廣播。

From chenwentong

1、Brodcast註冊方式:

一、靜態註冊:

二、動態註冊:
複製代碼

2、靜態註冊:在清單文件manifest中註冊,當程序退出以後還能夠收到該廣播。不能控制具體某個時間點接收和不接收廣播。

3、動態註冊:經過代碼的方式註冊context.registerReceiver(broadcastReceiver),註冊的同時注意在不須要接受的時候進行反註冊context.unregisterReceiver(broadcastReceiver);避免內存泄漏, 動態註冊能夠很好的控制廣播接受。

4、從Android 8.0(API 26)開始,對於大部分隱式廣播(廣播的對象不是針對你開發的APP),不能在manifest中聲明receiver,若是須要使用隱式廣播,須要使用context.registerReceiver 的方法。

談談Android的安全機制

  1. Android 是基於Linux內核的,所以 Linux 對文件權限的控制一樣適用於 Android。在 Android 中每一個應用都有本身的/data/data/包名 文件夾,該文件夾只能該應用訪問,而其餘應用則無權訪問。
  2. Android 的權限機制保護了用戶的合法權益。若是咱們的代碼想撥打電話、發送短信、訪問通訊錄、定位、訪問、sdcard 等全部可能侵犯用於權益的行爲都是必需要在 AndroidManifest.xml 中進行聲明的,這樣就給了用戶一個知情權。
  3. Android 的代碼混淆保護了開發者的勞動成果。

ListView如何提升效率

一、使用分頁加載,不要一次性加載全部數據。

二、複用convertView。在getItemView中,判斷converView是否爲空,若是不爲空,可複用。

三、異步加載圖片。Item中若是包含有webimage,那麼最好異步加載。

四、快速滑動時,不顯示圖片。當快速滑動列表(SCROLL_STATE_FLING),item中的圖片或獲取須要消耗資源的view,能夠不顯示出來;而處於其餘兩種狀態(SCROLL_STATE_IDLE和SCROLL_STATE_TOUCH_SCROLL),則將那些view顯示出來

ANR異常的產生條件及解決方案

ANR是什麼? ANR全稱:Application Not Responding,也就是應用程序無響應. 簡單來講,就是應用跑着跑着,忽然duang,界面卡住了,沒法響應用戶的操做如觸摸事件等.

緣由 Android系統中,ActivityManagerService(簡稱AMS)和WindowManagerService(簡稱WMS)會檢測App的響應時間. 若是App在特定時間沒法相應屏幕觸摸或鍵盤輸入時間,或者特定事件沒有處理完畢,就會出現ANR.

ANR的產生須要同時知足三個條件

  1. 主線程:只有應用程序進程的主線程(UI線程)響應超時纔會產生ANR
  2. 超時時間:產生ANR的上下文不一樣,超時時間也不一樣,但只要超過這個時間上限沒有響應就會產生ANR
  3. 輸入事件/特定操做:輸入事件是指按鍵、觸屏等設備輸入事件,特定操做是指BroadcastReceiver和Service的生命週期中的各個函數調用

解決方案: 總結爲一句話,即不要在主線程(UI線程)裏面作繁重的操做

談談 RecyclerView 的性能優化

  1. 數據處理和視圖加載分離

    從遠端拉取數據確定是要放在異步的,在咱們拉取下來數據以後可能就匆匆把數據丟給了 VH 處理,其實,數據的處理邏輯咱們也應該放在異步處理,這樣 Adapter 在 notify change 後,ViewHolder 就能夠簡單無壓力地作數據與視圖的綁定邏輯,好比:

    mTextView.setText(Html.fromHtml(data).toString());
    複製代碼

    這裏的 Html.fromHtml(data) 方法可能就是比較耗時的,存在多個 TextView 的話耗時會更爲嚴重,這樣便會引起掉幀、卡頓,而若是把這一步與網絡異步線程放在一塊兒,站在用戶角度,最多就是網絡刷新時間稍長一點。

  2. 數據優化

    分頁拉取遠端數據,對拉取下來的遠端數據進行緩存,提高二次加載速度;對於新增或者刪除數據經過 DiffUtil 來進行局部刷新數據,而不是一味地全局刷新數據。

  3. 佈局優化

    1. 減小過渡繪製

      減小布局層級,能夠考慮使用自定義 View 來減小層級,或者更合理地設置佈局來減小層級,不推薦在 RecyclerView 中使用 ConstraintLayout,有不少開發者已經反映了使用它效果更差,相關連接有:Is ConstraintLayout that slow?、constraintlayout 1.1.1 not work well in listview。

    2. 減小 xml 文件 inflate 時間

      這裏的 xml 文件不只包括 layout 的 xml,還包括 drawable 的 xml,xml 文件 inflate 出 ItemView 是經過耗時的 IO 操做,尤爲當 Item 的複用概率很低的狀況下,隨着 Type 的增多,這種 inflate 帶來的損耗是至關大的,此時咱們能夠用代碼去生成佈局,即 new View() 的方式,只要搞清楚 xml 中每一個節點的屬性對應的 API 便可。

    3. 減小 View 對象的建立

      一個稍微複雜的 Item 會包含大量的 View,而大量的 View 的建立也會消耗大量時間,因此要儘量簡化 ItemView;設計 ItemType 時,對多 ViewType 可以共用的部分儘可能設計成自定義 View,減小 View 的構造和嵌套。

  4. 其餘

    • 升級 RecycleView 版本到 25.1.0 及以上使用 Prefetch 功能,可參考 RecyclerView 數據預取。

    • 若是 Item 高度是固定的話,能夠使用 RecyclerView.setHasFixedSize(true); 來避免 requestLayout 浪費資源;

    • 設置 RecyclerView.addOnScrollListener(listener); 來對滑動過程當中中止加載的操做。

    • 若是不要求動畫,能夠經過 ((SimpleItemAnimator) rv.getItemAnimator()).setSupportsChangeAnimations(false); 把默認動畫關閉來提神效率。

    • TextView 使用 String.toUpperCase 來替代 android:textAllCaps="true"

    • TextView 使用 StaticLayout 或者 DynamicLayout 的自定義 View 來代替它。

    • 經過重寫 RecyclerView.onViewRecycled(holder) 來回收資源。

    • 經過 RecycleView.setItemViewCacheSize(size); 來加大 RecyclerView 的緩存,用空間換時間來提升滾動的流暢性。

    • 若是多個 RecycledViewAdapter 是同樣的,好比嵌套的 RecyclerView 中存在同樣的 Adapter,能夠經過設置 RecyclerView.setRecycledViewPool(pool); 來共用一個 RecycledViewPool

    • ItemView 設置監聽器,不要對每一個 Item 都調用 addXxListener,應該你們公用一個 XxListener,根據 ID 來進行不一樣的操做,優化了對象的頻繁建立帶來的資源消耗。

    • 經過 getExtraLayoutSpace 來增長 RecyclerView 預留的額外空間(顯示範圍以外,應該額外緩存的空間),以下所示:

      new LinearLayoutManager(this) {
          @Override
          protected int getExtraLayoutSpace(RecyclerView.State state) {
              return size;
          }
      };
      複製代碼

如何減少apk安裝包體積

1.代碼混淆 minifyEnabled true

2.資源壓縮 1)shrinkResources true 2)微信的AndResGuard

3.圖片壓縮 1)tinypng 2)svg 3)webp

4.so庫配置 只保留兩個abi平臺,即armeabi和armeabi-v7a

5.dex優化 Facebook的redex

四種LaunchMode及其使用場景

standard 模式 這是默認模式,每次激活Activity時都會建立Activity實例,並放入任務棧中。使用場景:大多數Activity。

singleTop 模式 若是在任務的棧頂正好存在該Activity的實例,就重用該實例( 會調用實例的 onNewIntent() ),不然就會建立新的實例並放入棧頂,即便棧中已經存在該Activity的實例,只要不在棧頂,都會建立新的實例。使用場景如新聞類或者閱讀類App的內容頁面。

singleTask 模式 若是在棧中已經有該Activity的實例,就重用該實例(會調用實例的 onNewIntent() )。重用時,會讓該實例回到棧頂,所以在它上面的實例將會被移出棧。若是棧中不存在該實例,將會建立新的實例放入棧中。使用場景如瀏覽器的主界面。無論從多少個應用啓動瀏覽器,只會啓動主界面一次,其他狀況都會走onNewIntent,而且會清空主界面上面的其餘頁面。

singleInstance 模式 在一個新棧中建立該Activity的實例,並讓多個應用共享該棧中的該Activity實例。一旦該模式的Activity實例已經存在於某個棧中,任何應用再激活該Activity時都會重用該棧中的實例( 會調用實例的 onNewIntent() )。其效果至關於多個應用共享一個應用,無論誰激活該 Activity 都會進入同一個應用中。使用場景如鬧鈴提醒,將鬧鈴提醒與鬧鈴設置分離。singleInstance不要用於中間頁面,若是用於中間頁面,跳轉會有問題,好比:A -> B (singleInstance) -> C,徹底退出後,在此啓動,首先打開的是B。

Android的數據存儲方式

Android提供了5中存儲數據的方式,分別是如下幾種:

一、使用Shared Preferences存儲數據,用來存儲key-value,pairs格式的數據,它是一個輕量級的鍵值存儲機制,只能夠存儲基本數據類型。

二、使用文件存儲數據,經過FileInputStream和FileOutputStream對文件進行操做。在Android中,文件是一個應用程序私有的,一個應用程序沒法讀寫其餘應用程序的文件。

三、使用SQLite數據庫存儲數據,Android提供的一個標準數據庫,支持SQL語句。

四、使用Content Provider存儲數據,是全部應用程序之間數據存儲和檢索的一個橋樑,它的做用就是使得各個應用程序之間實現數據共享。它是一個特殊的存儲數據的類型,它提供了一套標準的接口用來獲取數據,操做數據。系統也提供了音頻、視頻、圖像和我的信息等幾個經常使用的Content Provider。若是你想公開本身的私有數據,能夠建立本身的Content Provider類,或者當你對這些數據擁有控制寫入的權限時,將這些數據添加到Content Provider中實現共享。外部訪問經過Content Resolver去訪問並操做這些被暴露的數據。

五、使用網絡存儲數據

Android進程間通訊的幾種姿式

進程間通訊即IPC,英文全稱Inter-Process Communication,是指進程間數據交互的過程. Android底層是基於Linux,而Linux基於安全考慮,是不容許兩個進程間直接操做對方的數據,這就是進程隔離. 六種經常使用姿式:

  1. Bundle
  2. 文件共享
  3. AIDL
  4. Messenger
  5. ContentProvider
  6. Socket

參考:Android開發藝術探索 第2章 2.4節

From lydlovexyz

ipc

理解Activity,View,Window三者關係

Activity像一個工匠(控制單元),Window像窗戶(承載模型),View像窗花(顯示視圖)LayoutInflater像剪刀,Xml配置像窗花圖紙。 1:Activity構造的時候會初始化一個Window,準確的說是PhoneWindow。 2:這個PhoneWindow有一個「ViewRoot」,這個「ViewRoot」是一個View或者說ViewGroup,是最初始的根視圖。 3:「ViewRoot」經過addView方法來一個個的添加View。好比TextView,Button等 4:這些View的事件監聽,是由WindowManagerService來接受消息,而且回調Activity函數。好比onClickListener,onKeyDown等

Android SharedPreference頻繁操做有什麼後果?能存多少數據

Android中 SP 的底層是由Xml來實現的,操做SP的過程就是Xml的序列化和解析的過程。Xml是存儲在磁盤上的,所以當咱們頻繁進行SP操做時,就是頻繁進行序列化與解析,這就頻繁進行I/O的操做,因此確定會致使性能消耗。同時序列化Xml是就是將內存中的數據寫到Xml文件中,因爲DVM的內存是頗有限的,所以單個SP文件不建議太大,具體多大是沒有一個具體的要求的,可是咱們知道DVM 堆內存也就是16M,所以數據大小確定不能超過這個數字的。其實 SP 設置的目的就是爲了保存用戶的偏好和配置信息的,所以不要保存太多的數據。

請介紹下Android中經常使用的五種佈局

最經常使用的佈局方式爲Absolute Layout、Relative Layout、Linear Layout、FrameLayout、TableLayout。其中Linear Layout和Relative Layout是最經常使用的方式,他們能夠經過在xml配置文件或者代碼中進行佈局。

一、Frame Layout是最簡單的佈局方式,放置的控件都只能羅列到左上角,控件會有重疊,不能進行復雜的佈局。

二、Linear Layout能夠經過orientation屬性設置線性排列的方向是垂直仍是縱向的,每行或每列只有一個元素,能夠進行復雜的佈局。

三、Absolute Layout可讓子元素指定準確的x、y座標值,並顯示在屏幕上。Absolute Layout沒有頁邊框,容許元素之間相互重疊。它是絕對座標,因此在實際中不提倡使用。

四、Relative Layout容許子元素制定他們相對於其餘元素或父元素的位置(經過ID制定)。所以,你能夠以右對齊,或上下,或置於屏幕中央的形式來排列兩個元素。元素按順序排列,所以若是第一個元素在屏幕的中央,那麼相對於這個元素的其餘元素將以屏幕中央的相對位置來排列。這個是相對於Absolute Layout的,採用相對座標,因此在實際中比較經常使用。

五、Table Layout將以子元素的位置分配到行或列。一個Table Layout由許多的Table Row組成,每一個Table Row都會定義一個row。Table Layout容器不會顯示row、column或者cell的邊線框。每一個row擁有0個或多個的cell; 和html中的table差很少。在實際中也常用。

一、線性佈局 (LinearLayout):是一種很是經常使用的佈局,次佈局會將它包含的控件在線性方向上依次排列。經過android:orientation屬性來肯定排列的方向是vertical(垂直)仍是horizontal(水平)。 二、相對佈局(RelativeLayout):也是一種很是經常使用的佈局,經過相對定位的方式讓控件出如今佈局的任何位置。 三、幀佈局(FrameLayout):因爲定位方式的欠缺,全部的控件都會默認擺放在佈局的左上角,應用場景比較少。 四、絕對佈局(AbsoluteLayout):用x、y座標來肯定控件的位置。 五、表格佈局(TableLayout):每個TableLayout裏面有表格行TableRow,TableRow裏面能夠具體定義每個控件。

Android線程間通訊有幾種方法?

  1. Handler機制
  2. runOnUiThread(Runnable action)
  3. View.post(Runnable action)
  4. AsyncTask
  5. 廣播
  6. 使用EventBus、RxJava等框架

From Taonce

  1. 經過 Handler 來通訊

    val handler = @SuppressLint("HandlerLeak")
    object : Handler(){
        override fun handleMessage(msg: Message?) {
            Log.d("taonce","msg arg1: ${msg?.arg1}")
        }
    }
    thread {
        val msg: Message = handler.obtainMessage()
        msg.arg1 = 1
        handler.sendMessage(msg)
    }
    複製代碼
  2. 經過 runOnUiThread()

    thread {
        val text = "runOnUiThread"
        runOnUiThread {
            tv.text = text
        }
    }
    複製代碼
  3. 經過 View.post()

    thread {
        val text = "post"
        tv.post {
            tv.text = text
        }
    }
    複製代碼
  4. 經過 AsyncTask

    class MyAsyncTask(val name: String) : AsyncTask<String, Int, Any>() {
    
        // 執行任務以前的準備工做,好比將進度條設置爲Visible,工做在主線程
        override fun onPreExecute() {
            Log.d("async", "onPreExecute")
        }
    
        // 在onPreExecute()執行完以後當即在後臺線程中調用
        override fun doInBackground(vararg params: String?): Any? {
            Log.d("async", "$name execute")
            Thread.sleep(1000)
            publishProgress(1)
            return null
        }
    
        // 調用了publishProgress()以後,會在主線程中被調用,用於更新總體進度
        override fun onProgressUpdate(vararg values: Int?) {
            Log.d("async", "progress is: $values")
        }
    
        // 後臺線程執行結束後,會把結果回調到這個方法中,並在主線程中被調用
        override fun onPostExecute(result: Any?) {
            Log.d("async", "onPostExecute")
        }
    }
    複製代碼

談一談對Android中Context理解

Context是一個抽象基類。在翻譯爲上下文,也能夠理解爲環境,是提供一些程序的運行環境基礎信息。Context下有兩個子類,ContextWrapper是上下文功能的封裝類,而ContextImpl則是上下文功能的實現類。而ContextWrapper又有三個直接的子類, ContextThemeWrapper、Service和Application。其中,ContextThemeWrapper是一個帶主題的封裝類,而它有一個直接子類就是Activity,因此Activity和Service以及Application的Context是不同的,只有Activity須要主題,Service不須要主題。Context一共有三種類型,分別是Application、Activity和Service。這三個類雖然分別各類承擔着不一樣的做用,但它們都屬於Context的一種,而它們具體Context的功能則是由ContextImpl類去實現的,所以在絕大多數場景下,Activity、Service和Application這三種類型的Context都是能夠通用的。不過有幾種場景比較特殊,好比啓動Activity,還有彈出Dialog。出於安全緣由的考慮,Android是不容許Activity或Dialog憑空出現的,一個Activity的啓動必需要創建在另外一個Activity的基礎之上,也就是以此造成的返回棧。而Dialog則必須在一個Activity上面彈出(除非是System Alert類型的Dialog),所以在這種場景下,咱們只能使用Activity類型的Context,不然將會出錯。 getApplicationContext()和getApplication()方法獲得的對象都是同一個application對象,只是對象的類型不同。 Context數量 = Activity數量 + Service數量 + 1 (1爲Application)

Android 的四大組件都須要在清單文件中註冊嗎?並簡述四大組件

Activity、Service、ContentProvider 如 果 要 使 用 則 必 須 在AndroidManifest.xml 中 進 行 注 冊 , 而BroadcastReceiver則有兩種註冊方式,靜態註冊和動態註冊。其中靜態註冊就是指在AndroidManifest.xml中進行註冊,而動態註冊時經過代碼註冊。

Activity:一般展示爲一個用戶操做的可視化界面。它爲用戶提供了一個完成操做指令的窗口。 (mp.weixin.qq.com/s/CgfeMT9Yt…) (Activity的來由)

Service:Android系統的服務(不是一個線程,是主程序的一部分),與Activity不一樣,它是不能與用戶交互的,不能本身啓動的,需要調用Context.startService()來啓動,執行後臺,假設咱們退出應用時,Service進程並無結束,它仍然在後臺行。

BroadcastReceiver:廣播接收器是一個專一於接收廣播通知信息,並作出相應處理的組件。

ContentProvider:(內容提供者)主要用於對外共享數據,也就是經過ContentProvider把應用中的數據共享給其它應用訪問,其它應用可以經過ContentProvider對指定應用中的數據進行操做。

Android 實現異步的幾種方式,原理與各自特色

這邊介紹三種:AsyncTask,HandlerThread和IntentService

AsyncTask原理:內部是Handler和兩個線程池實現的,Handler用於將線程切換到主線程,兩個線程池一個用於任務的排隊,一個用於執行任務,當AsyncTask執行execute方法時會封裝出一個FutureTask對象,將這個對象加入隊列中,若是此時沒有正在執行的任務,就執行它,執行完成以後繼續執行隊列中下一個任務,執行完成經過Handler將事件發送到主線程。AsyncTask必須在主線程初始化,由於內部的Handler是一個靜態對象,在AsyncTask類加載的時候他就已經被初始化了。在Android3.0開始,execute方法串行執行任務的,一個一個來,3.0以前是並行執行的。若是要在3.0上執行並行任務,能夠調用executeOnExecutor方法

HandlerThread原理:繼承自Thread,start開啓線程後,會在其run方法中會經過Looper建立消息隊列並開啓消息循環,這個消息隊列運行在子線程中,因此能夠將HandlerThread中的Looper實例傳遞給一個Handler,從而保證這個Handler的handleMessage方法運行在子線程中,Android中使用HandlerThread的一個場景就是IntentService

IntentService原理:繼承自Service,它的內部封裝了HandlerThread和Handler,能夠執行耗時任務,同時由於它是一個服務,優先級比普通線程高不少,因此更適合執行一些高優先級的後臺任務,HandlerThread底層經過Looper消息隊列實現的,因此它是順序的執行每個任務。能夠經過Intent的方式開啓IntentService,IntentService經過handler將每個intent加入HandlerThread子線程中的消息隊列,經過looper按順序一個個的取出並執行,執行完成後自動結束本身,不須要開發者手動關閉

既然RecyclerView在不少方面能取代ListView,Google爲何沒把ListView劃上一條過期的橫線?

ListView採用的是RecyclerBin的回收機制,在一些輕量級的List顯示時效率更高.

  1. ListView採用的是RecyclerBin的回收機制在一些輕量級的List時效率更高。
  • 在處理少許數據使用 ListView
  • 在處理大量數據的時候使用 RecyclerView

ScrollView嵌套ListView的解決方案及其原理

自定義ListView 解決:重寫其中的onMeasure()方法

緣由: ScrollView默認把Childview設置爲UNSPEFEIED模式,而該模式下的ListView給本身的測量的高度就是第一個item的高度.原理: int expandSpec = MeasureSpec.makeMeasureSpec(Integer.MAX_VALUE >> 2, MeasureSpec.AT_MOST); 這個方法的做用是根據大小和模式來生成一個int值,這個int值封裝了模式和大小信息. 首先MeasureSpec類是View的一個靜態內部類,MeasureSpec類封裝了從父佈局到子佈局傳遞的佈局需求. 每一個MeasureSpec對象表明了寬度和高度的要求. MeasureSpec用int類型表示,前2位表明模式,後30位表明大小. 第一個參數Integer.MAX_VALUE >> 2:Integer.MAX_VALUE獲取到int的最大值,可是表示大小的值size是int數值的底30位,因此把這個值右移兩位,留出高兩位表示佈局模式. 此時這個值仍舊是一個30位所能表示的最大數,用該數做爲控件的size,應該足夠知足控件大小的需求. 第二個參數MeasureSpec.AT_MOST:表示這個控件適配父控件的最大空間.

(如下三種僅供參考,不推薦使用) 2.手動設置ListView高度 3.使用單個ListView取代ScrollView中全部內容 4.使用LinearLayout取代ListView

參考資料:
juejin.im/entry/5979a…
juejin.im/post/5a322c…

爲何在子線程建立Handler會拋異常?如何正確使用

Handler的工做是依賴於Looper的,而Looper(與消息隊列)又是屬於某一個線程(ThreadLocal是線程內部的數據存儲類,經過它能夠在指定線程中存儲數據,其餘線程則沒法獲取到),其餘線程不能訪問。所以Handler就是間接跟線程是綁定在一塊兒了。所以要使用Handler必需要保證Handler所建立的線程中有Looper對象而且啓動循環。由於子線程中默認是沒有Looper的,因此會報錯。正確的使用方法是:

private final class WorkThread extends Thread {

        private Handler mHandler;

        public Handler getHandler() {
            return mHandler;
        }
          public void quit() {
            mHandler.getLooper().quit();
        }
        @Override
        public void run() {
            super.run();
            //建立該線程對應的Looper,
            // 內部實現
            // 1。new Looper()
            // 2。將1步中的lopper 放在ThreadLocal裏,ThreadLocal是保存數據的,主要應用場景是:線程間數據互不影響的狀況
            // 3。在1步中的Looper的構造函數中new MessageQueue();
            //其實就是建立了該線程對用的Looper,Looper裏建立MessageQueue來實現消息機制
            //對消息機制不懂得同窗能夠查閱資料,網上不少也講的很不錯。
            Looper.prepare();
            mHandler = new Handler() {
                @Override
                public void handleMessage(Message msg) {
                    super.handleMessage(msg);
                    Log.d("WorkThread", (Looper.getMainLooper() == Looper.myLooper()) + "," + msg.what);
                }
            };
            //開啓消息的死循環處理即:dispatchMessage
            Looper.loop();
            //注意這3個的順序不能顛倒
            Log.d("WorkThread", "end");
        }
    }
複製代碼

自定義控件優化方案

  1. 爲了加速你的view,對於頻繁調用的方法,須要儘可能減小沒必要要的代碼。先從onDraw開始,須要特別注意不該該在這裏作內存分配的事情,由於它會致使GC,從而致使卡頓。在初始化或者動畫間隙期間作分配內存的動做。不要在動畫正在執行的時候作內存分配的事情。
  2. 你還須要儘量的減小onDraw被調用的次數,大多數時候致使onDraw都是由於調用了invalidate().所以請儘可能減小調用invaildate()的次數。若是可能的話,儘可能調用含有4個參數的invalidate()方法而不是沒有參數的invalidate()。沒有參數的invalidate會強制重繪整個view。
  3. 另一個很是耗時的操做是請求layout。任什麼時候候執行requestLayout(),會使得Android UI系統去遍歷整個View的層級來計算出每個view的大小。若是找到有衝突的值,它會須要從新計算好幾回。另外須要儘可能保持View的層級是扁平化的,這樣對提升效率頗有幫助。 若是你有一個複雜的UI,你應該考慮寫一個自定義的ViewGroup來執行他的layout操做。與內置的view不一樣,自定義的view能夠使得程序僅僅測量這一部分,這避免了遍歷整個view的層級結構來計算大小。這個PieChart 例子展現瞭如何繼承ViewGroup做爲自定義view的一部分。PieChart 有子views,可是它歷來不測量它們。而是根據他自身的layout法則,直接設置它們的大小。

談談Android的事件分發機制

事件的傳遞流程: Activity(PhoneWindow)->DecorView->ViewGroup->View。 事件分發過程當中三個重要的方法: dispatchTouchEvent()、onInterceptTouchEvent()、onTouchEvent(); 事件傳遞規則 通常一次點擊會有一系列的MotionEvent,能夠簡單分爲:down->move->….->move->up,當一次event分發到ViewGroup時,ViewGroup收到事件後調用dispatchTouchEvent,在dispatchTouchEvent中先檢查是否要攔截,若攔截則ViewGroup處理事件,不然交給有處理能力的子容器處理。

Android動畫有幾種,對其理解

  1. 視圖動畫。視圖移動、view真真的位置並未移動。
  2. 幀動畫。就和放電影同樣,一幀一幀的播
  3. 屬性動畫。視圖移動、其位置也會隨着移動。
  4. 觸摸返回動畫。發生觸摸事件時有反饋效果。好比波紋效果
  5. 揭露動畫。從某一個點向四周展開或者從四周向某一點聚合起來。
  6. 轉場動畫 & 共享元素。好比切換activity。共享元素通常咱們使用在轉換的先後兩個頁面有共同元素時。
  7. 視圖狀態動畫。就是 View 在狀態改變時執行的動畫效果
  8. 矢量圖動畫。在圖片的基礎上作動畫。
  9. 約束佈局實現的關鍵幀動畫。就是給須要動畫效果的屬性,準備一組與時間相關的值。關鍵的幾個值。

Android 內存泄漏的緣由以及解決方案

  1. 內存泄漏指對象再也不使用,本該被回收,卻由於有其餘正在使用的對象持有該對象的引用,而沒法被JVM回收

  2. 內存泄漏的影響:

    1. 應用可用內存減小,增長堆內存壓力
    2. 頻繁觸發GC,會下降了應用的性能
    3. 到必定程序會致使內存溢出錯誤
  3. Android開發中常見內存泄漏及解決辦法

    1. 靜態變量生命週期與應用的生命週期同樣,若是靜態變量持有某個Activity的上下文,則對應Activity沒法釋放,致使內存泄漏(單例模式) 解決辦法:使用Application的上下文
    2. 匿名內部類與非靜態內部類由於都會持有外部類引用,當執行異步操做易致使內存泄漏 解決辦法:將非靜態內部類轉爲靜態內部類+WeakReferenct的方式
    3. Handler消息隊列存在延時消息致使內存泄漏 在onDestroy方法中調用Handler相應的方法移除回調和刪除消息
    4. 各類註冊的監聽器忘記移除致使內存泄漏 解決辦法:在onDestroy方法中取消註冊
    5. 資源對象未關閉致使內存泄漏,如(IO,數據庫,Bitmap等) 解決辦法:及時關閉資源
    6. 屬性動畫未取消致使內存泄漏(如無限輪播圖效果) 解決辦法:onDestroy方法中取消動畫
    7. 其餘解決辦法:使用AAC框架
  4. 內存泄漏排查工具: AS Monitor,MAT,LeakCanary

  5. 擴展: Java內存管理,GC

  6. Handler引發的內存泄漏 緣由:該線程持有Handler的引用,而Handler也持有Activity的引用,這就致使了Activity再也不使用時,GC回收不了Activity 解決:Handler持有的引用最好使用弱引用,在Activity被釋放的時候要記得清空Message,取消Handler對象的Runnable

  7. 單例模式引發的內存泄漏 緣由:構建該單例的一個實例時須要傳入一個Context,若是此時傳入的是Activity,因爲Context會被建立的實例一直持有,當Activity進入後臺或者開啓設置裏面的不保留活動時,Activity會被銷燬,可是單例持有它的Context引用,Activity無法銷燬 解決:對於生命週期比Activity長的對象,要避免直接引用Activity的context,能夠考慮使用ApplicationContext

  8. 非靜態內部類建立靜態實例引發的內存泄漏 緣由:非靜態的內部類會自動持有外部類的引用,建立的靜態實例就會一直持有的引用 解決:能夠考慮把內部類聲明爲靜態的

  9. 非靜態匿名內部類引發的內存泄漏 緣由:若是匿名內部類被異步線程使用,可能會引發內存泄漏 解決:能夠考慮把內部類聲明爲靜態的

  10. 資源對象沒有關閉引發的內存泄漏 緣由:資源性對象好比Cursor、File、Bitmap、視頻等,系統都用了一些緩衝技術,在使用這些資源以後沒有關閉 解決:處理完資源對象的邏輯記得關閉,最好是造成習慣現寫一開一關

  11. 集合對象沒有及時清理引發的內存泄漏 緣由:若是集合是static、不斷的往裏面添加東西、又忘記去清理,確定會引發內存泄漏 解決:集合裏面的東西、有加入就應該對應有相應的刪除

不能經過 GC 來解決內存泄漏問題

相關文章
相關標籤/搜索