Android動(dòng)態(tài)加載dex入門

前言

Android構(gòu)建過(guò)程是將Java源代碼轉(zhuǎn)換成.dex(Dalvik EXexcutable)文件,這些文件是Android OS在Dalvik虛擬機(jī)("DVM")中運(yùn)行的文件质和。所以我們不能直接加載使用基于class的jar稳摄,而是需要將class轉(zhuǎn)化成dex字節(jié)碼。優(yōu)化后的字節(jié)碼可以存放在一個(gè).jar中饲宿,只要其內(nèi)部存放的是.dex即可使用秩命。

如何轉(zhuǎn)換呢?

在Android的SDK中為我們提供了一個(gè)dx命令(在\android-sdk\build-tools\version[23.0.1] 或 \android-sdk\platform-tools下能找到);命令使用方式為:dx --dex --output=out.jar in.jar褒傅,該命令將包含class的in.jar轉(zhuǎn)化為包含dex的out.jar文件。

Android支持的動(dòng)態(tài)加載

Android支持動(dòng)態(tài)加載的兩種方式是:DexClassLoader和PathClassLoader袄友。它倆的區(qū)別:

  • DexClassLoader可以加載jar/apk/dex殿托,可以從SD卡中加載未安裝的apk
  • PathClassLoader只能加載系統(tǒng)中已經(jīng)安裝過(guò)的apk
    點(diǎn)擊查看源碼分析

實(shí)驗(yàn)開始

新建一個(gè)Android工程
1.新建一個(gè)DexRes類

public class DexRes {
  public String getString() {
    return "我是來(lái)自dex中的資源";
  }
}

2.編譯一下,在對(duì)應(yīng)的工程目錄下會(huì)生成對(duì)應(yīng)的class文件(build/intermediates/classes/debug/com/maqiang/dexdemo/DexRes.class),我們需要編寫gradle腳本將這個(gè)class文件先轉(zhuǎn)換成jar,腳本代碼如下:

android{
   .....

   //刪除jar包
   task deleOldJar(type: Delete){
     delete 'build/libs/in.jar'
   }

   //生成jar包
   task makeJar(type: org.gradle.api.tasks.bundling.Jar){
     baseName 'in'

     from('build/intermediates/classes/debug/com/maqiang/dexdemo/DexRes.class')

     into('com/maqiang/dexdemo')

   }
}

注意:from表示需要轉(zhuǎn)換的class文件的地址剧蚣,into表示轉(zhuǎn)換后對(duì)應(yīng)的文件目錄(一定要和class文件中的package對(duì)應(yīng)起來(lái))

然后在Android studio中的右側(cè)面板中的Gradle中執(zhí)行我們的makeJar,執(zhí)行完畢后在工程的build/libs下就會(huì)有一個(gè)in.jar

生成jar包的方法
執(zhí)行完畢后在這個(gè)目錄下會(huì)生成對(duì)應(yīng)的jar

3.將jar轉(zhuǎn)換成含dex的jar
我們將這個(gè)jar包拷貝到dx命令(\android-sdk\build-tools\version或 \android-sdk\platform-tools)所在的目錄下支竹,我是拷貝到了platform-tools下面,然后執(zhí)行命令dx --dex --output=out.jar in.jar鸠按,將in.jar轉(zhuǎn)換成含dex的out.jar.

執(zhí)行結(jié)果

4.使用adb命令adb push out.jar sdcard/out.jar將out.jar放到SD卡下

上傳過(guò)程

5.編寫客戶端調(diào)用代碼
核心思想就是使用DexClassLoader去加載dex礼搁,然后通過(guò)反射調(diào)用我們之前定義的方法獲取相關(guān)資源.

public class MainActivity extends AppCompatActivity {

  private static final String TAG = "MainActivity";

  @Override protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
  }

  /**
   * 點(diǎn)擊事件
   * @param view
   */
  public void loadDex(View view) {
    File dexOutputDir = getDir("dex1", 0);
    String dexPath = Environment.getExternalStorageDirectory() + File.separator + "out.jar";
    DexClassLoader loader =
      new DexClassLoader(dexPath, dexOutputDir.getAbsolutePath(), null, getClassLoader());
    try {
      Class clz = loader.loadClass("com.maqiang.dexdemo.DexRes");
      Method dexRes = clz.getDeclaredMethod("getString");
      Toast.makeText(this, (CharSequence) dexRes.invoke(clz.newInstance()), Toast.LENGTH_LONG)
        .show();
    } catch (Exception e) {
      e.printStackTrace();
    }
  }
}

此處需要注意DexClassLoader的四個(gè)參數(shù):

  • 參數(shù)1 dexPath:待加載的dex文件路徑,如果是外存路徑目尖,一定要加上讀外存文件的權(quán)限(<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/> )馒吴,否則會(huì)報(bào)與上面一樣的錯(cuò)誤,這點(diǎn)參考文章2中說(shuō)這個(gè)權(quán)限可有可無(wú)是錯(cuò)誤的瑟曲。(更正下:Android4.4 KitKat及以后的版本需要此權(quán)限饮戳,之前的版本不需要權(quán)限)

  • 參數(shù)2 optimizedDirectory:解壓后的dex存放位置,此位置一定要是可讀寫且僅該應(yīng)用可讀寫(安全性考慮)洞拨,所以只能放在data/data下扯罐。本文getDir("dex1", 0)會(huì)在/data/data/**package/下創(chuàng)建一個(gè)名叫”app_dex1“的文件夾,其內(nèi)存放的文件是自動(dòng)生成output.dex烦衣;如果不滿足條件歹河,Android會(huì)報(bào)的錯(cuò)誤為:

            java.lang.IllegalArgumentException: optimizedDirectory not readable/writable: /storage/sdcard0
            java.lang.IllegalArgumentException: Optimized data directory /storage/sdcard0 is not owned by the current user. Shared storage cannot protect your application from code injection attacks.
    
  • 參數(shù)3 libraryPath:指向包含本地庫(kù)(so)的文件夾路徑掩浙,可以設(shè)為null

  • 參數(shù)4 parent:父級(jí)類加載器,一般可以通過(guò)Context.getClassLoader獲取到秸歧,也可以通過(guò)ClassLoader.getSystemClassLoader()取到厨姚。

如果出現(xiàn)以下錯(cuò)誤,請(qǐng)檢查jar中的文件目錄是否使用正確寥茫,在打包過(guò)程中是否正確將對(duì)應(yīng)的class的打包成功.

java.lang.ClassNotFoundException: Didn't find class "com.example.testdextoast.ShowToastImpl" on path: DexPathList[[zip file "/storage/emulated/0/testtoast.jar"],nativeLibraryDirectories=[/vendor/lib, /system/lib]]
.....
 Suppressed: java.lang.NoClassDefFoundError: Failed resolution of: Lcom/example/testdextoast/IShowToast;
         ... 16 more
 Caused by: java.lang.ClassNotFoundException: Didn't find class "com.example.testdextoast.IShowToast" on path: DexPathList[[zip file "/storage/emulated/0/testtoast.jar"],nativeLibraryDirectories=[/vendor/lib, /system/lib]]
         ... 21 more
         Suppressed: java.lang.ClassNotFoundException: Didn't find class "com.example.testdextoast.IShowToast" on path: DexPathList[[zip file "/data/app/com.example.testshowtoastdex-1/base.apk"],nativeLibraryDirectories=[/vendor/lib, /system/lib]]
                 ... 22 more
                 Suppressed: java.lang.ClassNotFoundException: com.example.testdextoast.IShowToast
                         ... 23 more
                 Caused by: java.lang.NoClassDefFoundError: Class not found using the boot class loader; no stack available
 Suppressed: java.lang.ClassNotFoundException: Didn't find class "com.example.testdextoast.ShowToastImpl" on path: DexPathList[[zip file "/data/app/com.example.testshowtoastdex-1/base.apk"],nativeLibraryDirectories=[/vendor/lib, /system/lib]]
         ... 15 more
         Suppressed: java.lang.ClassNotFoundException: com.example.testdextoast.ShowToastImpl
                 ... 16 more
         Caused by: java.lang.NoClassDefFoundError: Class not found using the boot class loader; no stack available

6.實(shí)驗(yàn)結(jié)束

調(diào)用成功截圖

參考博客:Android動(dòng)態(tài)加載dex技術(shù)初探

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末遣蚀,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子纱耻,更是在濱河造成了極大的恐慌芭梯,老刑警劉巖,帶你破解...
    沈念sama閱讀 206,311評(píng)論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件弄喘,死亡現(xiàn)場(chǎng)離奇詭異玖喘,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)蘑志,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,339評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門累奈,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人急但,你說(shuō)我怎么就攤上這事澎媒。” “怎么了波桩?”我有些...
    開封第一講書人閱讀 152,671評(píng)論 0 342
  • 文/不壞的土叔 我叫張陵戒努,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我镐躲,道長(zhǎng)储玫,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 55,252評(píng)論 1 279
  • 正文 為了忘掉前任萤皂,我火速辦了婚禮撒穷,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘裆熙。我一直安慰自己端礼,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,253評(píng)論 5 371
  • 文/花漫 我一把揭開白布入录。 她就那樣靜靜地躺著齐媒,像睡著了一般。 火紅的嫁衣襯著肌膚如雪纷跛。 梳的紋絲不亂的頭發(fā)上喻括,一...
    開封第一講書人閱讀 49,031評(píng)論 1 285
  • 那天,我揣著相機(jī)與錄音贫奠,去河邊找鬼唬血。 笑死望蜡,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的拷恨。 我是一名探鬼主播脖律,決...
    沈念sama閱讀 38,340評(píng)論 3 399
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼腕侄!你這毒婦竟也來(lái)了小泉?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 36,973評(píng)論 0 259
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤冕杠,失蹤者是張志新(化名)和其女友劉穎微姊,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體分预,經(jīng)...
    沈念sama閱讀 43,466評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡兢交,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,937評(píng)論 2 323
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了笼痹。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片配喳。...
    茶點(diǎn)故事閱讀 38,039評(píng)論 1 333
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖凳干,靈堂內(nèi)的尸體忽然破棺而出晴裹,到底是詐尸還是另有隱情,我是刑警寧澤救赐,帶...
    沈念sama閱讀 33,701評(píng)論 4 323
  • 正文 年R本政府宣布涧团,位于F島的核電站,受9級(jí)特大地震影響净响,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜喳瓣,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,254評(píng)論 3 307
  • 文/蒙蒙 一馋贤、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧畏陕,春花似錦配乓、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,259評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至鞠绰,卻和暖如春腰埂,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背蜈膨。 一陣腳步聲響...
    開封第一講書人閱讀 31,485評(píng)論 1 262
  • 我被黑心中介騙來(lái)泰國(guó)打工屿笼, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留牺荠,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 45,497評(píng)論 2 354
  • 正文 我出身青樓驴一,卻偏偏與公主長(zhǎng)得像休雌,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子肝断,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,786評(píng)論 2 345

推薦閱讀更多精彩內(nèi)容