[轉]Android app中加載jar插件

Android app中加載jar插件

 

插件的引入

1、UI方面有些自定義的通用控件代碼,並不侷限於一個項目中使用,當需要共享使用時雖然可以採用源碼方式合入新項目,但是這種原始方式增加代碼佈局上的複雜程度的同時,也增大了通用控件的不安全性。

2、有些功能性模塊,或是第三方開發,或是分階段開發的,爲了方便程序的功能擴展,我們同樣考慮將功能代碼封裝爲插件包的形式。

那麼在Android中是否支持插件呢?也就是是否支持類似Windows和Symbian平臺的lib和dll等庫方式呢?答案是肯定的,在Android中支持的插件庫,可以是由C/C++開發的JNI形式,也可以是由java代碼開發的jar形式(也可以是android封包完成的apk文件)。由於JNI需要涉及Android NDK,這裏不做介紹,本文只針對jar插件。

加載jar插件的方式

雖然在加載jar插件方式之前,我們應該考慮jar插件的創建,但是由於jar加載方式的不同,決定了jar插件創建形式的不同。目前加載jar插件的方式有兩類:一類是跟加載SDK api的Android.jar一樣的靜態加載方式;另一類是運行時動態加載方式。前者是編譯過程中就必須要jar文件存在,後者是運行時通過反射機制來動態調用。下面我們分別闡述。

靜態加載jar插件

靜態jar插件的創建

靜態jar插件的創建,可以只是由一個簡單的java類文件編譯生成,也可以從一個完整的工程中導出生成。

假設有一個沒有用到Android API的 test.java文件放置在c盤根目錄下面,那麼在安裝jdk的PC上我們可以通過cmd命令行來生成一個jar文件

//編譯test.java,生成test.class

C:\>javac test.java

//壓縮test.class生成jar文件

C:\>jar cvf test.jar test.class

標明清單(manifest)

增加:test.class(讀入= 267) (寫出= 213)(壓縮了 20%)

//查看生成的jar文件中具體有哪些內容

C:\>jar tvf test.jar

     0 Fri Sep 23 11:21:34 CST 2011 META-INF/

    75 Fri Sep 23 11:21:34 CST 2011 META-INF/MANIFEST.MF

   267 Thu Sep 22 17:56:42 CST 2011 test.class

通過如上步驟一個最簡單jar插件就生成了。下面在看如何從一個完整工程中生成一個jar文件,當然也可以通過cmd命令行的方式來實現,但是對於工程來說,涉及代碼文件多,在Eclipse中又提供了簡易操作的情況下,我們自然優先選擇Eclipse來圖形化實現jar文件的生成。本想自己按步驟小結下,但是發現網上很多博客整理的很詳細了,我在這裏就引用http://hi.baidu.com/lovewjlove/blog/item/95d74560ebacdbd68db10d90.html 博文中的內容吧。

1. 首先在Eclipse中打開項目, 右鍵點擊項目,選擇“Export”;

2. 選擇Java/JAR file,Next;

3. Select the resources to export中可以選擇你想要包含的項目文件夾,一些不必要的文件夾就無需放進去了,免得增大空間;

這裏有幾個選項:

    * Export generated class files and resources 表示只導出生成的.class文件和其他資源文件

    * Export all output folders for checked projects 表示導出選中項目的所有文件夾

    * Export java source file and resouces 表示導出的jar包中將包含你的源代碼*.java,如果你不想泄漏源代碼,那麼就不要選這項了

    * Export refactorings for checked projects 把一些重構的信息文件也包含進去

在Select the export destination中選擇導出的jar的路徑,Next

4. 下一頁可以選擇是否導出那些含有警告warning或者錯誤errors的*.class文件。一般不用理他,Next

5. 下一個頁面裏可以對項目做一些配置

    * Generate the manifest file是系統幫我們自動生成MANIFEST.MF文件,如果你的項目沒有引用其他class-path,那可以選擇這一項;

    * Use existing mainfest from workspace。這是可以選擇我們自定義的.MF文件,格式如上所寫;

    * Seal content。要封裝整個jar或者指定的包packet;

    * Main class。這裏可以選擇你的程序入口,將來打包出來的jar就是你這個入口類的執行結果;

配置完點擊Finish,工程的jar文件就生成了。

博文中只是針對java工程,對於android工程我們自然也是可以通過上述方法完成靜態jar文件的生成。

靜態jar插件的使用

通過上面靜態jar插件的創建步驟,我們生成了一個drawcolor.jar的jar插件,下面我們來演示下如何將這個文件加載到Android app中進行使用。

首先,拷貝jar插件至工程目錄。在android工程根目錄創建一個lib文件夾,將該drawcolor.jar拷貝至lib文件夾下;

其次,工程中添加jar插件。右擊工程,通過屬性菜單,打開Java Build Path對話框,選中Libraries,添加JARS,在彈出的對話框中選擇lib/drawcolor.jar文件(也可以通過項目右鍵點擊Build Path,Build Path–> Libraries–> Add JARs,選擇第三方的jar包,),結果如下

第三,調用jar中的操作。通過上述步驟之後,在Eclipse的Project Explorer下面就可以看到jar包中的類和成員了,與使用Android.jar內的API一樣使用,具體就不給出示例了。

第四,將添加的jar打包到apk中。由於SDK的API在運行環境中(模擬器和手機上)已經有插件存在了,所以我們不用打包到apk中,而我們自己添加的jar,就必須要打包到apk中,否則運行時會報沒有找到包的錯誤。打包到apk需要做如下操作

在工程目錄下找到.classpath文件,因爲我們這裏添加的jar位於lib/drawcolor.jar,所以查看文件中是否有如下一行

       <classpathentry kind="lib" path="lib/drawcolor.jar"/>

假如有就可以了,假如是其他的,就要把他更改過來,比如http://www.yoyong.com/archives/tag/android-%E6%89%93%E5%8C%85%E7%AC%AC%E4%B8%89%E6%96%B9jar博文中提到的一樣。

懷疑這個跟Eclipse的版本有關吧,我的編譯環境下,這個在工程中添加jar插件步驟時已經在工程的.classpath文件修改爲需求的樣子了。

以上就實現了靜態加載jar插件。

動態加載jar插件

動態jar插件的創建

動態jar插件的創建相對比靜態jar多了一個步驟,因爲Android虛擬機是基於dex的,所以我們的class不能簡單的調用jar命令壓縮就可以了,而是需要使用sdk\platform-tools目錄下的dx工具來進行類型轉換。下面演示單一類文件AddFunc.java的生成jar插件使用命令行的過程。

1、  使用javac命令編譯生成AddFunc.class文件

2、  由於AddFunc類的包目錄爲com.demo.jar,所以需要將AddFunc.class文件拷貝至sdk\platform-tools\com\demo\jar文件夾下面

3、  使用dx命令,生成jar插件文件,上述步驟的命令如下

C:\>javac AddFunc.java

 

C:\>dx --dex --output=AddFunc.jar com/demo/jar/AddFunc.class

 

C:\>jar tvf D:\Android\sdk\platform-tools\AddFunc.jar

    72 Fri Sep 23 14:28:48 CST 2011 META-INF/MANIFEST.MF

   964 Fri Sep 23 14:28:50 CST 2011 classes.dex

通過上述步驟在sdk\platform-tools上面就有一個AddFunc.jar文件了。在這裏需要說明的是AddFunc.class必須按照包名放置,否則生成jar會報錯。

其實假如本身就在Android工程下面,那麼可以現在eclipse下面先編譯程序,後在Bin目錄下面,自然就按包名,放置了class文件,再將需要的class文件包含文件目錄拷貝至sdk\platform-tools目錄下就好了。假設我們需要打包兩個class,就可以通過如下命令實現

C:\>dx --dex --output=AddFunc.jar com/demo/jar/AddFunc.class com/demo/jar/GameView.class

經過查詢,明顯比上面這個classes.dex文件大多了,具體如下

C:\>jar tvf D:\Android\sdk\platform-tools\AddFunc.jar

    72 Fri Sep 23 14:38:06 CST 2011 META-INF/MANIFEST.MF

  1752 Fri Sep 23 14:38:06 CST 2011 classes.dex

好了,我們需要的動態jar插件就形成了。

動態jar插件的使用

如上所述,我們生成了需要動態jar插件,如同java中動態加載使用ClassLoader類動態加載一樣,在Android我們需要使用DexClassLoader來通過反射機制動態加載。

相對來說假如動態jar插件創建正確,這一步就沒什麼難題了,就全交給代碼來說明吧。

AddFunc的源代碼爲

package com.demo.jar;

 

import android.util.Log;

 

public class AddFunc

{

    public AddFunc()

    {

           Log.i("AddFunc", "AddFunc class Init");

    }

   

    public int Add(int a, int b)

    {

           int c  = a + b;

           Log.i("AddFunc", "Add result is "+c);

           return c;

    }

}

以上的代碼生成動態jar插件後,在模擬器通過cmd如下命令,將jar插件放置到運行壞境中,本文是將其放置在模擬器的sdcard根目錄,具體如下

C:\>adb push addFunc.jar sdcard/

6 KB/s (1149 bytes in 0.187s)

這樣,就可以在代碼中使用反射機制調用jar中的方法了,具體的Android工程中動態調用jar插件的代碼如下

package com.demo.jar.runloadjarDemo;

 

import java.io.File;

import java.lang.reflect.Method;

import dalvik.system.DexClassLoader;

import android.app.Activity;

import android.os.Bundle;

 

public class RunLoadJarDemoActivity extends Activity

{

    /** Called when the activity is first created. */

       Class myClass  =null;

      

    @Override

    public void onCreate(Bundle savedInstanceState)

    {

        super.onCreate(savedInstanceState);

        setContentView(R.layout.main);

        try

        {

               File file = new File("/sdcard/AddFunc.jar");

//              File file = new File("/sdcard/drawView.apk");

               if(file.exists())

               { 

                     DexClassLoader cl = new DexClassLoader(file.toString(), getFilesDir().getAbsolutePath(), null, ClassLoader.getSystemClassLoader().getParent());  

                      myClass = cl.loadClass("com.demo.jar.AddFunc");

                      Object obj = myClass.newInstance();

                      Class[] params = new Class[2];

                      params[0] = Integer.TYPE;

                      params[1] = Integer.TYPE;

                      Method action = myClass.getMethod("Add", params);

                      int ret = (Integer)action.invoke(obj, 15, 20);                     

               } 

        }

        catch (Exception ex)

        {     

               ex.printStackTrace();

        }

    }

}

其實從原理上來說,如上生成的動態jar插件,跟apk的生成是同一個道理,所以假設我們不想通過繁瑣的dx工具,那麼也可以直接由eclipse生成apk,然後通過動態加載的方法來使用apk中的類和方法,本人測試過也是可行的。

好了,至此將Android app中加載jar插件的問題就介紹到這裏,如果大家覺得如上的代碼調用反射太過繁瑣,那麼可以通過設計接口的方法來將反射變得簡單,至於這個話題就不在本文進行闡述了。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章