首先介紹一下要實現的效果:靜默更新所有客戶端應用程序中的jar包。
產生這個需求的原因:當做好的jar包發佈出去,開發者已經在使用的情況下,有一天我們發現jar包中有個bug,這是我們就要去更新jar包,但是如果開發者需要更新jar包的話,那代價無疑是巨大的。出於減少開發者更新jar包的代價的目的。我們想到了使用這種方式來解決。整個項目的結構如下圖:
插件管理框架工作流程:
客戶端app啓動時,插件管理框架讀取本地所有插件信息,例如插件版本,提供者等等,並將該信息發送到插件管理服務端,在服務端監測是否有新的插件需要更新。如果有新的插件插件管理框架負責下載等操作。
具體的實現的話,爲了統一,我們在插件管理框架中定義好接口,所有插件都要實現該接口,實現該接口的插件,我們就認爲它是一個合法的插件。這樣在application中我們就可以通過插件管理框架中的方法來調用插件中的方法。
插件管理框架主要模塊:
1.插件管理:讀取插件信息和服務端交互,根據返回判斷是否下載新的插件。
2.調用插件中的方法。
關於第一點就不多少了,就是一些網絡訪問和下載的東東。我們主要說一下調用插件中的方法。在使用jar包的使用我們知道如果是放在lib目錄下,通過build path 加載到項目中,我們可以直接通過import導入使用jar包中的類和方法。但是插件的類和方法該怎麼使用呢?這是我們就要使用到android的類加載器。android中有兩種類加載器 DexClassLoader和PathClassLoader,兩者的區別就是PathClassLoader只能加載已經安裝到android系統中的apk文件,也就是/data/app/下的apk文件,加載其他位置的文件會報ClassNotFoundException ,DexClassLoader可以加載apk、jar和dex文件,而且可以加載sdcard下的文件。
先介紹一下我們的插件項目,該項目就是簡單的做個加法運算。
package com.apkplug;
public class plugClass {
public plugClass() {
}
public int add(int a, int b){
return a+b;
}
}
在下面的介紹中我們使用DexClassLoader.首先我們來打包jar包並轉換成dex文件,首先將寫好的jar包導出,Export->java jarfile ->Next 然後選擇jar的存放目錄。然後我們將jar包轉成dex格式,爲了方便我們將jar包放到SDK的安裝目錄plarform-tools下,DOS進入這個目錄,執行命令: dx --dex -ouput=classes.jar Myplug.jar 這樣我們就將Myplugin.jar轉成了dex格式,我們將classes.jar 放到SDcard目錄。下面在程序中我們使用DexClassLoader來加載classes.jar中的類
package com.apkplugtest;
import android.os.Bundle;
import android.app.Activity;
import android.view.Menu;
import android.widget.TextView;
import java.lang.reflect.Method;
import dalvik.system.DexClassLoader;
import dalvik.system.PathClassLoader;
public class MainActivity extends Activity {
TextView tv = null;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
tv = (TextView) findViewById(R.id.tv);
useDexClassLoader();
}
private void useDexClassLoader() {
String dexPath = "/mnt/sdcard/classes.jar";
String dexOutputDir = "/data/data/com.apkplugtest";
DexClassLoader pathClassLoader = new DexClassLoader(dexPath, dexOutputDir, null, this
.getClass().getClassLoader());
// dexPath 目標jar或apk的路徑 ; dexOutputDirdex 文件路徑 ;null 目標類中使用的c/c++庫存放的路徑
// ; 第四個參數 父裝載類
try {
Class<?> class1 = pathClassLoader.loadClass("com.apkplug.plugClass");
Object object = class1.newInstance();
Class[] params = new Class[2];
params[0] = Integer.TYPE;
params[1] = Integer.TYPE;
Method action = class1.getMethod("add", params);
Integer ret = (Integer) action.invoke(object, 12, 13);
tv.setText("method : " + action.getName() + ", return :" + ret);
} catch (Exception e) {
// TODO: handle exception
}
}
}
程序運行結果如下圖:
同時我們看到在data/data/com.apkplugtest下生成了.dex 文件如下圖:
整個思路就是這樣了。