DiyCode技術沙龍

目錄
一、內存管理優化
二、動態更新
三、hotfix
四、App安全
五、一些感受

最近事情比較多,很遺憾這是一篇遲來的分享。

DiyCode技術沙龍 是由廣州社區的成員合力舉辦的一場Android技術分享會,因為獲得了一張免費的門票,加上分享的主題都比較感興趣,所以去參加學習一下,總的來說干貨滿滿,見識了很多的技術大牛。

一、內存管理優化

Low Memory Killder(LMK)

常見的進程優先級:

  • 前臺進程(Foreground Process)
  • 可見進程(Visbile Process)
  • 服務進程(Service Process)
  • 后臺進程(Background Process)
  • 空進程(Empty Process)
進程優先級

oom_adj的值越高,優先級越低,越容易被系統回收

查看oom_adj

在adb shell 中,輸入如下兩個命令即可:

  • ps | grep 包名
  • cat /proc/進程pid/oom_adj

示例:

    E:\asWorkSpace>adb shell
    shell@hnSCL-Q:/ $ ps | grep com.android.browser
    u0_a7     26894 353   1716624 138140 ffffffff 00000000 S com.android.browser
    u0_a7     26963 353   1538696 49280 ffffffff 00000000 S com.android.browser:service
    shell@hnSCL-Q:/ $ cat /proc/26963/oom_adj
    1
    shell@hnSCL-Q:/ $

其中的26894和26963分別是瀏覽器兩個進程的pid

StrictMode(嚴苛模式)

參考資料:Android性能調優利器StrictMode

什么是StrictMode ?

StrictMode(嚴苛模式)是用來檢測程序中違例情況的一個開發者工具,最常用的場景是檢測主線程中本地磁盤和網絡讀寫等耗時操作。

能檢測哪些?

嚴苛模式主要用來檢測兩個問題,一個是線程策略,即ThreadPolicy,另一個是VM策略,即VmPolicy。

ThreadPolicy

  • 自定義的耗時調用,使用detectCustomSlowCalls()開啟
  • 磁盤讀取操作,使用detectDiskReads()開啟
  • 磁盤寫入操作,使用detectDiskWrites()開啟
  • 網絡操作,使用detectNetwork()開啟

VmPolicy

  • Activity泄漏,使用detectActivityLeaks()開啟
  • 未關閉的Closable對象泄漏,使用detectLeakedClosableObjects()開啟
  • 泄漏的Sqlite對象,使用detectLeakedSqliteObjects()開啟
  • 檢測實例數量,使用setClassInstanceLimit()開啟

在哪里開啟?

嚴苛模式的的開啟可以放在Application或者Activity以及其他組件的onCreate方法。為了更好地分析應用中的問題,建議放在Application的onCreate方法。

簡單的開啟方式?

第一種,通過代碼的方式:


if (IS_DEBUG && Build.VERSION.SDK_INT >= 9) {
    StrictMode.setThreadPolicy(new StrictMode.ThreadPolicy.Builder().detectAll().penaltyLog().build());
  StrictMode.setVmPolicy(new VmPolicy.Builder().detectAll().penaltyLog().build());
}

嚴苛模式需要在Debug模式開啟,不要在Release版本中啟用。同時,嚴苛模式自API19開始引入,某些方法也從API11開始引入,使用時需注意API級別。

第二種,通過在手機開發者選項中開啟嚴苛模式,開啟之后,如果主線程中有執行時間長的操作,屏幕會閃爍。

常見問題及優化方案

  • 使用合適的Context,一般注冊第三方框架或者SDK時采用Application的Context,除非特地要求傳Activity的Context

  • 多進程應用需要避免Appication onCreate多次執行引起的重復初始化

  • 圖片資源放對位置(優先考慮主流設備,優先考慮用戶分布多的設備)

  • 圖片加載優化

      1.inSampleSize(降低采樣率)
      2.BitmapRegionDecoder(加載超級大圖)
      3.Matrix(小圖放大)
      4.LruCache/LinkedHaspMap(緩存控制,避免oom)
      5.選擇合適的圖片加載框架(UIL、Fresco、Glide、Picasso)
      6.按需顯示,優先顯示縮略圖,需要時顯示大圖
      7.優化加載圖片的時機
    
  • 主動釋放內存

      關鍵函數: onTrimMemory(int level) ,這是4.0以后提供的一個API,系統提供的回調有
      - Application.onTrimMemory()
      - Activity.onTrimMemory()
      - Fragement.OnTrimMemory()
      - Service.onTrimMemory()
      - ContentProvider.OnTrimMemory()
      
      參數level代表不同的內存狀態:
      - TRIM_MEMORY_COMPLETE:內存不足,并且該進程在后臺進程列表最后一個,馬上就要被清理
      - TRIM_MEMORY_MODERATE:內存不足,并且該進程在后臺進程列表的中部
      - TRIM_MEMORY_BACKGROUND:內存不足,并且該進程是后臺進程
      - TRIM_MEMORY_UI_HIDDEN:內存不足,并且該進程的UI已經不可見了
      以上4個是4.0新增
      - TRIM_MEMORY_RUNNING_CRITICAL:內存不足(后臺進程不足3個),并且該進程優先級比較高,需要清理內存
      - TRIM_MEMORY_RUNNING_LOW:內存不足(后臺進程不足5個),并且該進程優先級比較高,需要清理內存
      - TRIM_MEMORY_RUNNING_MODERATE:內存不足(后臺進程超過5個),并且該進程優先級比較高,需要清理內存
      以上3個是4.1新增
    
  • 選擇合適的時機,對View和資源解綁,在合適的時機再恢復

  • 釋放緩存

  • 小心未關閉的Dialog,在Activity和Fragment銷毀時記得先dismiss

  • 匿名內部類和非靜態內部類泄漏,用靜態外部類和弱引用的方式取而代之

  • 避免創建大量的臨時對象而造成內存抖動,考慮復用

      - 高頻執行函數中避免創建大量臨時對象,如View的onDraw和onTouch等
      - StingBuilder、StringBuffer代替String
    
  • WebView造成內存泄漏

      Android系統和各家的ROM本身存在的問題造成的,最好的處理方式是將承載WebView的界面獨立到其他進程,在合適的時機選擇殺死WebView進程
    
  • 自身內存監控(來自騰訊開發者提供的一種方案

      - Runtime.getRuntime().getMaxMemory()
      Runtime.getRuntime().totalMemory() - Runtime.getRuntime().freeMemory()
    
      - 定期檢查上邊的比例值,達到一定峰值時調用此API觸發內存釋放
      WindowManagerGlobal.getInstance().startTrimMemory(TRIM_MEMORY_COMPLETE);
    
  • 更多

      - 注冊和反注冊(BroadcastReceiver、Observer)
      - 關閉資源(Cursor、IO流)
      - 使用優化過的數據結構SparseXXXX
      - 考慮使用Parcelable取代Serializable
      - 建立緩存池,如ListView復用View的思路
      - 開辟多進程(當然,也不是越多越好)
      - 借助lint來規避一些常見的編碼問題
    

二、動態更新

隨著App日益龐大以及越來越復雜的邏輯,不僅需要團隊多人的協作開發還有方法數越界等問題,因此,插件化以此衍生出而來,它的好處有以下幾個方面:

  • 插件模塊動態升級
  • 改進大型App的架構
  • 實現多團隊協作開發,各自發布

因為對這一塊接觸了解的并不是很多,在此不作更詳細的記錄。

三、hotfix

所謂熱補丁(hotfix),它可以讓應用在無須重新安裝下能夠自動更新。對于熱補丁的應用,可能我們會存在幾個等級的需求:

  1. Bugfix : 簡單的bugfix,解決一些空指針之類的問題
  2. UI : 改變UI,支持資源的變更
  3. features : 功能的發布,甚至一定情況下面代替升級

對于現在市面上的熱修復方案,大致可以分為兩個流派,第一個是native派,它肯定是使用native方式實現,第二個是Java派,如kkfix可以hook系統的方法,robust是AOP的方式。

熱修復框架的分類

那么問題又來了,熱補丁技術哪家強?其實各個方案都有自己的優缺點,世上沒有最好的方案,只有只適合自己的方案

  • AndFix方案

這套方案主要是在Native層替換方法的實現,關鍵挑戰是兼容性問題,底層的代碼每個版本都有改動,可能有些廠商也會去改這塊代碼,所以這套方案的使用場景存在較大的限制

    優點:1、立刻生效               缺點:1、兼容性
         2、性能影響小                   2、Native定位復雜
         3、補丁相對較小                 3、應用場景首先
         4、支持大部分的加固場景

    總結: 適合Bugfix場景
  • QZone方案

主要是利用了android classloader查找類的順序,當出現重復類的時候,優先使用補丁中修改過的類。這套方案有個問題是dalvik在加載修改過的類的時候會出現一個crash,當時的解決方案是用插樁的方式。這套方案的特點是非常簡單,而且兼容性不錯。

    優點:1、兼容性較好               缺點:1、性能損耗
         2、應用場景廣                    2、補丁可能會過大
         3、簡單,成功率高                 3、無法立刻生效
         4、支持大部分的加固場景

    總結: 可實現Features發布
  • Tinker方案

在審視大量方案后,發現gradle的instant run 和 facebook buck exopacakage方案都使用了全量合成的做法,于是想到了推送差異Dex的方式。

    優點:1、兼容性較好               缺點:1、Dalvik Rom體積較大
         2、應用場景廣                    2、單獨的合成過程
         3、補丁較小                      3、無法立刻生效
         4、性能損耗小                    4、不支持加固(通過回退Qzone方案)

    總結: 可實現Features發布
  • Robust方案

關鍵挑戰:super方法調用問題;Proguard內聯

    優點:1、立即生效               缺點:1、應用場景受限
         2、性能影響小                   2、修改源碼
         3、補丁較小                     3、安裝包變大(5%左右)
         4、支持大部分加固場景                

    總結: 適合Bugfix場景

各種方案的對比:

熱修復框架對比

四、App安全

代碼安全

  • 完整性校驗:檢查classex.dex、apk的完整性,最好將哈希值存儲到服務器
  • 防逆向分析:代碼混淆、加殼保護
  • 防進程注入:防止ptrace進行so注入,并hook任意函數

傳輸安全

  • HTTPS=HTTP+SSL/TLS
  • 蘋果已宣布從2017年起所有IOS應用將強制使用HTTPS
  • HTTP/2.0也只支持HTTPS
  • 防止中間人攻擊:SSL Pinning
  • 防止降級攻擊

關于URL簽名的一般處理方式:

  1. 將所有參數按參數名進行升序排序;
  2. 將排序后的參數名和值拼接成字符串stringParams,格式:key1value1key2value2...;
  3. 在上一步的字符串前面拼接上請求URL的Endpoint,字符串后面拼接上AppSecret,即stringUri+StringParams+AppSecret;
  4. 使用AppSecret為密鑰,對上一步的結果字符串使用HMAC算法計算MAC值,這個MAC值就是簽名。

URL示例:

    http://api.domain.com/users/123?appKey=qwer&token=asfe&timestamp=23456789

    AppSecret:nmefj

    參數排序:appKey、timestamp、token

    參數拼接:appKeyqwertimestamp23456789tokenasfe

    URL拼接:users/123appKeyqwertimestamp23456789tokenasfenmefj

    計算MAC值:reRwV

    最終URL:http://api.domain.com/users/123?appKey=qwer&token=asfe&timestamp=23456789&sign=reRwV

存儲安全

常用的存儲方式:

  • SharedPreferences
  • Internal Storage
  • External Storage
  • SQlite Databases
  • keystore/keychain
  • 硬編碼
  • so文件

存儲的安全標準:

  • 敏感數據不能存在外部存儲器上,無論是否加密
  • 私有目錄數據正確設置權限
  • 銘感數據不能以明文存儲在私有目錄

敏感數據安全

密碼:

  • 不要在客戶端本地保存
  • 網絡傳輸加密:MD5、加鹽MD5、HMAC、AES、RSA
  • 數據庫保存:加鹽MD5,鹽值和MD5分開保存

密鑰:

  • 保存到SharedPreferences
  • 硬編碼到代碼中
  • 加密保存到配置文件中
  • NDK開發,放在so文件中,加解密操作都在so文件,并添加簽名認證

TOKEN

  • 用戶登錄成功后返回Token,一般有兩個:accessToken和和refreshToken
  • Token需要設置有效期,accessToken的有效期不能設置太長,最好不超過一周,refreshToken可以長一點
  • accessToken過期后,通過refreshToken更新accessToken
  • refreshToken過期后,則需要用戶重新登錄

五、一些感受

額,做技術的人好像蠻容易禿頂的...

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容