編輯:關於android開發
A class loader that loads classes from .jar and .apk files containing a classes.dex entry. This can be used to execute code not installed as part of an application.
也就是說,DexClassLoader可以加載一個含有classes.dex文件的壓縮包,既可以是jar也可以是apk。那麼加載一個離線的apk文件需要注意哪些呢?
1.DexClassLoader的構造方法:
DexClassLoader(String dexPath, String optimizedDirectory, String libraryPath, ClassLoader parent)
2.私有目錄
This class loader requires an application-private, writable directory to cache optimized classes.
了解到上述兩點,我們就可以根據DexClassLoader所需要的參數,動態加載assets中的apk了。
該類主要是負責管理這些DexClassLoader的,首先,我們定義了一個叫做BundleDexClassLoader的類,它繼承自DexClassLoader,用於加載離線的apk文件。每一個apk文件對應一個BundleDexClassLoader,而BundleClassLoaderManager則保存了一個List,在加載的時候,用於查找類。具體代碼如下:
package net.mobctrl.hostapk;
import java.io.File;
import java.io.FilenameFilter;
import java.util.ArrayList;
import java.util.List;
import android.annotation.TargetApi;
import android.content.Context;
import android.os.Build;
/**
* @Author Zheng Haibo
* @PersonalWebsite http://www.mobctrl.net
* @version $Id: BundleClassLoaderManager.java, v 0.1 2015年12月11日 下午7:30:59
* mochuan.zhb Exp $
* @Description
*/
@TargetApi(Build.VERSION_CODES.ICE_CREAM_SANDWICH)
public class BundleClassLoaderManager {
public static List bundleDexClassLoaderList = new ArrayList();
/**
* 加載Assets裡的apk文件
* @param context
*/
public static void install(Context context) {
AssetsManager.copyAllAssetsApk(context);
// 獲取dex文件列表
File dexDir = context.getDir(AssetsManager.APK_DIR,
Context.MODE_PRIVATE);
File[] szFiles = dexDir.listFiles(new FilenameFilter() {
@Override
public boolean accept(File dir, String filename) {
return filename.endsWith(AssetsManager.FILE_FILTER);
}
});
for (File f : szFiles) {
System.out.println("debug:load file:" + f.getName());
BundleDexClassLoader bundleDexClassLoader = new BundleDexClassLoader(
f.getAbsolutePath(), dexDir.getAbsolutePath(), null,
context.getClassLoader());
bundleDexClassLoaderList.add(bundleDexClassLoader);
}
}
/**
* 查找類
*
* @param className
* @return
* @throws ClassNotFoundException
*/
public static Class loadClass(Context context,String className) throws ClassNotFoundException {
try {
Class clazz = context.getClassLoader().loadClass(className);
if (clazz != null) {
System.out.println("debug: class find in main classLoader");
return clazz;
}
} catch (Exception e) {
e.printStackTrace();
}
for (BundleDexClassLoader bundleDexClassLoader : bundleDexClassLoaderList) {
try {
Class clazz = bundleDexClassLoader.loadClass(className);
if (clazz != null) {
System.out.println("debug: class find in bundle classLoader");
return clazz;
}
} catch (Exception e) {
e.printStackTrace();
}
}
throw new ClassCastException(className + " not found exception");
}
}
注意點:
1.install方法
install方法主要是將assets中的apk全部拷貝到私有目錄,然後再遍歷私有目錄,使用BundleDexClassLoader加載apk文件,然後將這些BundleDexClassLoader保存到數組中。
2.loadClass方法
該方法先從當前的ClassLoader中查找需要的類,如果找不到,在從List中遍歷查找。
在MainActivity中,我們可以通過如下方式,調用apk類中的方法:
private void loadApk() {
try {
Class clazz = BundleClassLoaderManager.loadClass(getApplicationContext(),
"net.mobctrl.normal.apk.Utils");
Constructor constructor = clazz.getConstructor();
Object bundleUtils = constructor.newInstance();
Method printSumMethod = clazz.getMethod("printSum", Context.class,
int.class, int.class, String.class);
printSumMethod.setAccessible(true);
Integer sum = (Integer) printSumMethod.invoke(bundleUtils,
getApplicationContext(), 10, 20, "計算結果");
System.out.println("debug:sum = " + sum);
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (SecurityException e) {
e.printStackTrace();
} catch (NoSuchMethodException e) {
e.printStackTrace();
} catch (IllegalArgumentException e) {
e.printStackTrace();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
}
與MultiDex不同時,我們是通過BundleClassLoaderManager來加載類的,而不是當前的ClassLoader。
正如BundleClassLoaderManager中的loadClass方法,其實我們創建一個ClassLoader對象,通過重寫當前ClassLoader的findClass方法即可,然後在Override的findClass方法中,首先從當前ClassLoader中查找類,然後再從BundleDexClassLoader中遍歷查找,這樣既可以在Host項目中調用Bundle中的類,也能夠在Bundle中調用Host中的類。
mClassLoader = new ClassLoader(super.getClassLoader()) {
@Override
protected Class findClass(String className)
throws ClassNotFoundException {
Class clazz = BundleClassLoaderManager.loadClass(context,className);
if (clazz == null) {
throw new ClassNotFoundException(className);
}
return clazz;
}
};
上一篇博客和這一篇博客將的都是類的加載。如果所需要加載的類都是工具類,不需要加載資源等,那麼上面的方案都沒啥問題。但是如果加載的類是Fragment或者Activity等UI,需要引用資源文件,這又改如何處理呢?
Kotlin的擴展函數:擴展Android框架(KAD 08),kotlinandroid作者:Antonio Leiva 時間:Jan 11, 2017 原文鏈接:ht
Android java傳遞int類型數據給C,androidint本文根據《Android jni簡便開發流程》中的開發流程來實現一個java傳遞int類型數據給C 新
TextView的API 中文文檔中說明了它的結構: 結構 java.lang.Object android.view.View andro
初探ListView,初探網ListView可能是Android開發中最常用的一個控件,但要用的純熟還需要不斷的鍛煉。 建立簡單的ListView 1.在布局文件(.