1. 程式人生 > >android中熱修復與外掛化(一)

android中熱修復與外掛化(一)

簡介

目前android技術最前沿莫屬熱修復與外掛化的技術點,當下用得最多的就是阿里的Andfix,和微信的Tinker框架,針對原始碼的實現,再次做個記錄。

熱修復給我們解決的問題
  1. 剛上線的APP應用,由於測試的疏忽,發現了一個嚴重的bug。
  2. 針對一些小功能,不想再次經歷釋出,打包傳送給使用者。
外掛化解決的問題
  1. 解決應用越來越大所帶來的問題(方法超過65535)
  2. 解決應用越來越大帶來的合作開發的問題

我們從三方面來對於熱修復和外掛化進行分析。

目錄

  • 知識詳解模組
  • 熱修復應用模組
  • 外掛化應用模組
知識詳解模組
  1. dex/class深入講解
  2. jvm/dvm/art深入講解

class檔案結構深入解析

什麼是class檔案

class檔案全名稱為Java class檔案,能被java虛擬機器所識別的二進位制檔案。能夠被jvm載入 並執行的檔案格式。

如何生成一個class檔案

  1. 通過IDE(程式碼編輯器)自動幫我們生成class檔案
  2. 通過javac去生成class檔案

class檔案的作用

記錄一個類檔案的所以資訊

class檔案格式詳解

1.一種8位位元組的二進位制流檔案
2.各個資料按順序緊密的排列,無間隙(例如:cafebabe00000032....)
3.每個類和介面都對應一個class檔案    

class檔案格式組成
這裡寫圖片描述

注:其中u1,u2,u4,u8分別代表1位元組,2位元組,4位元組和8位元組的無符號型別整數。

我們編寫一個java檔案:

public class Hello{
      private int test;
      public int test(){
            return test;
        }
}

接下來用010Edit開啟檔案格式

ca fe ba be 00 00 00 32 00 12 0a 00 04 00 0e 09
00 03 00 0f 07 00 10 07 00 11 01 00 04 74 65 73
74 01 00 01 49 01 00 06 3c 69 6e 69 74
3e 01 00 03 28 29 56 01 00 04 43 6f 64 65 01 00 0f 4c 69 6e 65 4e 75 6d 62 65 72 54 61 62 6c 65 01 00 03 28 29 49 01 00 0a 53 6f 75 72 63 65 46 69 6c 65 01 00 0a 48 65 6c 6c 6f 2e 6a 61 76 61 0c 00 07 00 08 0c 00 05 00 06 01 00 05 48 65 6c 6c 6f 01 00 10 6a 61 76 61 2f 6c 61 6e 67 2f 4f 62 6a 65 63 74 00 21 00 03 00 04 00 00 00 01 00 02 00 05 00 06 00 00 00 02 00 01 00 07 00 08 00 01 00 09 00 00 00 1d 00 01 00 01 00 00 00 05 2a b7 00 01 b1 00 00 00 01 00 0a 00 00 00 06 00 01 00 00 00 01 00 01 00 05 00 0b 00 01 00 09 00 00 00 1d 00 01 00 01 00 00 00 05 2a b4 00 02 ac 00 00 00 01 00 0a 00 00 00 06 00 01 00 00 00 03 00 01 00 0c 00 00 00 02 00 0d

(ca fe ba be)
magic魔數,魔數的唯一作用是確定這個檔案是否是class檔案。魔數值固定為 0xCAFEBABE,不會改變。

(00 00 00 32)
minor_version、major_version副版本號和主版本號,當前副版本號是0,主版本號是50。

(00 12)
constant_pool_count常量池計數器,該常量數為17

constant_pool常量池,它包含 Class 檔案結構及其子結構中引用的所有字串常量、類或介面名、欄位名和其它常量。constant_pool 就下來就是分析這17個常量了。

常量池如下:
          00 12  常量池的數目 18-1=17
          0a 00 04 00 0e  方法:java.lang.Ojbect void <init>()
          09 00 03 00 0f   方法 :Hello int test() 
          07 00 10  字串:Hello
          07 00 11 字串:java.lang.Ojbect
          01 00 04 74 65 73 74 字串:test
          01 00 01 49  字串:I
          01 00 06 3c 69 6e 69 74 3e 字串:<init>
          01 00 03 28 29 56 字串:()V
          01 00 04 43 6f 64 65 字串:Code 
          01 00 0f 4c 69 6e 65 4e 75 6d 62 65 72 54 61 62 6c 65 字串:LineNumberTable 
          01 00 03 28 29 49 字串:()I
          01 00 0a 53 6f 75 72 63 65 46 69 6c 65 字串:SourceFile
          01 00 0a 48 65 6c 6c 6f 2e 6a 61 76 61 字串:Hello.java
          0c 00 07 00 08 NameAndType:<init> ()V
          0c 00 05 00 06 NameAndType:test I
          01 00 05 48 65 6c 6c 6f 字串:Hello
          01 00 10 6a 61 76 61 2f 6c 61 6e 67 2f 4f 62 6a 65 63 74 字串: java/lang/Object

(00 21)
access_flags 描述的是當前類(或者介面)的訪問修飾符, 如public, private等。
這裡寫圖片描述

(00 03)
this_class類索引,this_class存的是當前類的名稱在常量池裡的索引,這裡指向第三個常量,即是“Hello”。

(00 04)
super_class存的是父類的名稱在常量池裡的索引,這裡指向第四個常量,即是“java/lang/Object”。

(00 00)
nterfaces包含interfaces_count和interfaces[]兩個欄位。這裡interfaces_count為0,所以後面的內容也對應為空

fields_count欄位計數器,fields_count 的值表示當前 Class 檔案 fields陣列的成員個數。

fields陣列中的每個成員都必須是一個 fields_info 的資料項,用於表示當前類或介面中某個欄位的完整描述。

methods_count 的值表示當前 Class 檔案 methods[]陣列的成員個數。

methods[]陣列中的每個成員都必須是一個 method_info 的資料項,用於表示當前類或介面中某個方法的完整描述。

attributes_count 的值表示當前 Class 檔案 attributes 表的成員個數。

attributes 表的每個項的值必須是 attribute_info 。

class檔案弊端

 1.記憶體佔用大,不適合移動端
 2.堆疊的載入模式,載入速度慢。
 3.檔案io操作多,類查詢慢。
接下來我們分析dex檔案
什麼是dex檔案

能夠被dvm識別,載入並執行的檔案格式

如何生成一個dex檔案
通過ide自動幫我們build生成
手動通過dx命令去生成dex檔案
手動執行dex檔案執行在手機上
執行dex檔案
1.找到android sdk->build-tools->選擇版本->dx,配置到環境變數中
2.在cmd中  dx --dex --output Hello.dex Hello.class
3.將dex檔案push到手機儲存卡中,然後執行adb shell進入手機控制檯
4.使用dalvikvm -cp /sdcard/Hello.dex 類名 執行dex檔案
dex檔案的作用

記錄整個工程中所有類檔案的資訊,記住是整個工程。

dex檔案格式詳解
1.一種8位位元組的二進位制流檔案
2.各個資料按順序緊密排列,無間隙。
3.整個應用中所有java原始檔都放在一個dex中
dex檔案結構

這裡寫圖片描述

header(基本資訊)

header是DEX檔案頭,包含magic欄位、alder32校驗值、SHA-1雜湊值、string_ids的個數以及偏移地址等。DEX檔案的頭結構很固定,佔用0x70個位元組,具體定義程式碼如下所示(摘自DexFile.h):

struct DexHeader {
    u1  magic[8];           /* includes version number */  
    u4  checksum;           /* adler32 checksum */  
    u1  signature[kSHA1DigestLen]; /* SHA-1 hash */
    u4  fileSize;           /* length of entire file */
    u4  headerSize;         /* offset to start of next section */
    u4  endianTag;
    u4  linkSize;
    u4  linkOff;
    u4  mapOff;
    u4  stringIdsSize;
    u4  stringIdsOff;
    u4  typeIdsSize;
    u4  typeIdsOff;
    u4  protoIdsSize;
    u4  protoIdsOff;
    u4  fieldIdsSize;
    u4  fieldIdsOff;
    u4  methodIdsSize;
    u4  methodIdsOff;
    u4  classDefsSize;
    u4  classDefsOff;
    u4  dataSize;
    u4  dataOff;
};

magic[8]:共8個位元組。目前為固定值dex\n035。

checksum:檔案校驗碼,使用alder32演算法校驗檔案除去magic、checksum外餘下的所有檔案區域,用於檢查檔案錯誤。

signature:使用 SHA-1演算法hash除去magic,checksum和signature外餘下的所有檔案區域 ,用於唯一識別本檔案 。

fileSize:DEX檔案的長度。

headerSize:header大小,一般固定為0x70位元組。

endianTag:指定了DEX執行環境的cpu位元組序,預設值ENDIAN_CONSTANT等於0x12345678,表示預設採用Little-Endian位元組序。

linkSize和linkOff:指定連結段的大小與檔案偏移,大多數情況下它們的值都為0。link_size:LinkSection大小,如果為0則表示該DEX檔案不是靜態連結。link_off用來表示LinkSection距離DEX頭的偏移地址,如果LinkSize為0,此值也會為0。

mapOff:DexMapList結構的檔案偏移。

stringIdsSize和stringIdsOff:DexStringId結構的資料段大小與檔案偏移。

typeIdsSize和typeIdsOff:DexTypeId結構的資料段大小與檔案偏移。

protoIdsSize和protoIdsSize:DexProtoId結構的資料段大小與檔案偏移。

fieldIdsSize和fieldIdsSize:DexFieldId結構的資料段大小與檔案偏移。

methodIdsSize和methodIdsSize:DexMethodId結構的資料段大小與檔案偏移。

classDefsSize和classDefsOff:DexClassDef結構的資料段大小與檔案偏移。

dataSize和dataOff:資料段的大小與檔案偏移。

class檔案與dex檔案對比

1.本質上他們都是一樣的,dex是從class檔案演變而來的
2.class檔案存在許多冗餘資訊,dex會去除冗餘資訊