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插件的問題就介紹到這裏,如果大家覺得如上的代碼調用反射太過繁瑣,那麼可以通過設計接口的方法來將反射變得簡單,至於這個話題就不在本文進行闡述了。