Android動態加載jar/dex

Android動態加載jar/dex


前言 html

在目前的軟硬件環境下,Native App與Web App在用戶體驗上有着明顯的優點,但在實際項目中有些會由於業務的頻繁變動而頻繁的升級客戶端,形成較差的用戶體驗,而這也偏偏是Web App的優點。本文對網上Android動態加載jar的資料進行梳理和實踐在這裏與你們一塊兒分享,試圖改善頻繁升級這一弊病。 java

 

聲明 android

  歡迎轉載,但請保留文章原始出處:)  windows

    博客園:http://www.cnblogs.com 安全

    農民伯伯: http://over140.cnblogs.com  

    Android中文翻譯組:http://androidbox.sinaapp.com/ 網絡

 

正文 app

  1、 基本概念和注意點 ide

    1.1  首先須要瞭解一點:在Android中能夠動態加載,但沒法像Java中那樣方便動態加載jar 工具

     緣由:Android的虛擬機(Dalvik VM)是不認識Java打出jar的byte code,須要經過dx工具來優化轉換成Dalvik byte code才行。這一點在我們Android項目打包的apk中能夠看出:引入其餘Jar的內容都被打包進了classes.dex。 優化

      因此這條路不通,請你們注意。

 

    1.2  當前哪些API可用於動態加載

      1.2.1  DexClassLoader

        這個能夠加載jar/apk/dex,也能夠從SD卡中加載,也是本文的重點。

      1.2.3  PathClassLoader  

        只能加載已經安裝到Android系統中的apk文件。

 

  2、 準備

    本文主要參考"4、參考文章"中第一篇文章,補充細節和實踐過程。

    2.1  下載開源項目

      http://code.google.com/p/goodev-demo

      將項目導入工程,工程報錯的話應該是少了gen文件夾,手動添加便可。注意這個例子是從網上下載優化好的jar(已經優化成dex而後再打包成的jar)到本地文件系統,而後再從本地文件系統加載並調用的。本文則直接改爲從SD卡加載。

 

  3、實踐

    3.1  編寫接口和實現

      3.1.1  接口IDynamic

package com.dynamic;

public  interface IDynamic {
     public String helloWorld();
}

       3.1.2  實現類DynamicTest

複製代碼
package com.dynamic;

public  class DynamicTest  implements IDynamic {

    @Override
     public String helloWorld() {
         return "Hello World!";
    }
}
複製代碼

 

    3.2  打包並轉成dex

      3.2.1  選中工程,常規流程導出便可,如圖:

      注意:在實踐中發現,本身新建一個Java工程而後導出jar是沒法使用的,這一點你們能夠根據文章一來了解相關緣由,也是本文的重點之一。這裏打包導出爲dynamic.jar

      (後期修復:打包請不要把接口文件打進來,參見文章末尾後續維護!)

      3.2.2  將打包好的jar拷貝到SDK安裝目錄android-sdk-windows\platform-tools下,DOS進入這個目錄,執行命名:

dx --dex --output=test.jar dynamic.jar

 

    3.3  修改調用例子

      修改MainActivity,以下:

複製代碼
    @Override
     public  void onCreate(Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
        mToastButton = (Button) findViewById(R.id.toast_button);
        
         //  Before the secondary dex file can be processed by the DexClassLoader,
        
//  it has to be first copied from asset resource to a storage location.
//         final File dexInternalStoragePath = new File(getDir("dex", Context.MODE_PRIVATE),SECONDARY_DEX_NAME);
//         if (!dexInternalStoragePath.exists()) {
//             mProgressDialog = ProgressDialog.show(this,
//                     getResources().getString(R.string.diag_title), 
//                     getResources().getString(R.string.diag_message), true, false);
//              //  Perform the file copying in an AsyncTask.
//              //  從網絡下載須要的dex文件
//             (new PrepareDexTask()).execute(dexInternalStoragePath);
//         } else {
//             mToastButton.setEnabled(true);
//         }
        
        mToastButton.setOnClickListener( new View.OnClickListener() {
             public  void onClick(View view) {
                 //  Internal storage where the DexClassLoader writes the optimized dex file to.
                
// final File optimizedDexOutputPath = getDir("outdex", Context.MODE_PRIVATE);
                 final File optimizedDexOutputPath =  new File(Environment.getExternalStorageDirectory().toString()
                    + File.separator + "test.jar");
                 //  Initialize the class loader with the secondary dex file.
//                 DexClassLoader cl = new DexClassLoader(dexInternalStoragePath.getAbsolutePath(),
//                         optimizedDexOutputPath.getAbsolutePath(),
//                         null,
//                         getClassLoader());
                DexClassLoader cl =  new DexClassLoader(optimizedDexOutputPath.getAbsolutePath(),
                    Environment.getExternalStorageDirectory().toString(),  null, getClassLoader());
                Class libProviderClazz =  null;
                
                 try {
                     //  Load the library class from the class loader.
                    
//  載入從網絡上下載的類
//                     libProviderClazz = cl.loadClass("com.example.dex.lib.LibraryProvider");
                    libProviderClazz = cl.loadClass("com.dynamic.DynamicTest");
                    
                     //  Cast the return object to the library interface so that the
                    
//  caller can directly invoke methods in the interface.
                    
//  Alternatively, the caller can invoke methods through reflection,
                    
//  which is more verbose and slow.
                    
// LibraryInterface lib = (LibraryInterface) libProviderClazz.newInstance();
                    IDynamic lib = (IDynamic)libProviderClazz.newInstance();
                    
                     //  Display the toast!
                    
// lib.showAwesomeToast(view.getContext(), "hello 世界!");
                    Toast.makeText(MainActivity. this, lib.helloWorld(), Toast.LENGTH_SHORT).show();
                }  catch (Exception exception) {
                     //  Handle exception gracefully here.
                    exception.printStackTrace();
                }
            }
        });
    }
複製代碼

 

    3.4  執行結果

     

 

  4、參考文章

    [推薦]在Android中動態載入自定義類

    Android app中加載jar插件

    關於Android的ClassLoader探索

    Android App 如何動態加載類

 

  5、補充

    你們能夠看看DexClassLoader的API文檔,裏面不提倡從SD卡加載,不安全。此外,我也正在組織翻譯組儘快把這個命名空間下的幾個類都翻譯出來,以供你們參考。

    工程下載:這裏,Dex文件下載:這裏。你們能夠直接把Dex文件拷貝到SD卡,而後運行例子。

 

  6、後期維護

    6.1  2011-12-1  修復本文錯誤

      感謝網友ppp250和liuzhaocn的反饋,基本按照評論2來修改:

      6.1.1  不須要在本工程裏面導出jar,本身新建一個Java工程而後導出來也行。

      6.1.2  導出jar時不能帶接口文件,不然會報如下錯:

         java.lang.IllegalAccessError: Class ref in pre-verified class resolved to unexpected implementation

      6.1.3  將jar優化時應該從新成jar(jar->dex->jar),若是以下命令:

      dx --dex --output=test.jar dynamic.jar

 

    6.2  2012-3-29  本文升級版:

      Android應用開發提升系列(4)——Android動態加載(上)——加載未安裝APK中的類

      請你們參照最新的文章來作動態加載!

 

結束

  除了翻譯組的工做和本身本職的工做之外,很難抽時間出來分享一些開發心得,但正所謂擠擠老是有的,歡迎交流!
相關文章
相關標籤/搜索