Android动态加载
Android动态加载
一、动态加载Java Class
运行中使用类加载器加载sd卡中的jar包中的类,并调用该类的方法流程:
1.创建主工程
主工程很简单,就一个界面,界面中一个按钮,点击执行下面的代码
2.主工程实现插件(jar包)加载逻辑
//SD卡根目录
File sdcardPath = Environment.getExternalStorageDirectory().getAbsoluteFile();
String pluginName = "myplugin_dex.jar";
//插件(.jar 或 .dex)路径
String pluginPath = sdcardPath.getAbsolutePath() + File.separator + pluginName;
// PathClassLoader classLoader = new PathClassLoader(pluginPath, getClass().getClassLoader());
// ClassLoader classLoader = new BaseDexClassLoader(pluginPath, sdcardPath, null, getClass().getClassLoader());
//构造dex类加载器
DexClassLoader classLoader = new DexClassLoader(pluginPath, sdcardPath.getAbsolutePath(), null, getClass().getClassLoader());
try {
//加载SD卡jar包中的class
Class mainClazz = classLoader.loadClass("com.example.myplugin.PluginMain");
Object pluginO = mainClazz.getConstructor(null).newInstance();
// IPlugin plugin = (IPlugin) pluginO;
// String version = plugin.getVersion();
//这里可以在主工程定义与插件中的类实现的接口相同的接口,通过强转直接调用方法,向上面这样
Method method = mainClazz.getMethod("getVersion");
String version = (String) method.invoke(pluginO, null);
Log.v(TAG, "getVersion=" + version);
mTv.setText("verson=" + version);
} catch (Exception ) {
e.printStackTrace();
}
3.创建插件工程,插件工程打包生成dex,jar
插件工程作为一个android library .gradle文件中 apply plugin: 'com.android.library'
插件工程就是提供一个类,一些方法,生成jar包,给主工程运行时加载调用
双击执行gradle的assemble任务
最终输出的aar文件(因为module是android library),但是我们需要中间生成的jar文件,路径为
./myplugin/build/intermediates/aar_main_jar/debug/classes.jar
dx工具在android sdk的build-tools文件夹中,先切换到dx工具所在目录,后执行以下命令对origin.jar做编译优化,输出的target.jar才可以被android的classloader加载
dx --dex --output=target.jar origin.jar
另外,myplugin模块的buildTypes中debug中的minifyEnabled 要为false
buildTypes { release { minifyEnabled false proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' } debug{ minifyEnabled false } }
4.dex/jar放入sdcard中
adb push myplugin_dex.jar adb push myplugin_dex.jar /sdcard/
查看是否成功push到sd卡中,终端依次输入
adb shell
cd sdcard/
ls | grep .jar
5.运行加载插件
执行加载的代码
二、加载apk中的资源
mypluginapk工程是一个普通的android application(apk)工程
目标:可以获取APK中的资源
流程:
1.获取资源id
这里我们传入apk的路径,获取apk的应用名称的id
public int getAppNameResIdpublic int getAppNameResId(String apkPath){
PackageManager packageManager = this.getPackageManager();
PackageInfo info = packageManager.getPackageArchiveInfo(apkPath, 0);
return info.applicationInfo.labelRes;
}
2.从APK创建Resource对象
/**
* 获去apk的res
*/
public static Resources getResourcesObject(Context context, String apkPath) throws Exception {
Resources res = context.getResources();
Class> assertClass = Class.forName("android.content.res.AssetManager");
Object assetMag = assertClass.getConstructor(null).newInstance(null);
Method method = assertClass.getMethod("addAssetPath", String.class);
method.invoke(assetMag, apkPath);
Class> resClass = Class.forName("android.content.res.Resources");
res = (Resources) resClass.getConstructor(assertClass, res.getDisplayMetrics().getClass(), res.getConfiguration().getClass())
.newInstance(assetMag, res.getDisplayMetrics(), res.getConfiguration());
return res;
}
通过反射调用AssetManager中的addAssetPath方法,可以将一个apk中的资源加载到Resources中,由于addAssetPath是隐藏api我们无法直接调用,所以只能通过反射,下面是它的声明,通过注释我们可以看出,传入的路径可以是zip文件也可以是一个资源目录,而apk就是一个zip,所以直接将apk的路径传给它,资源就加载到AssetManager中了,然后再通过AssetManager来创建一个新的Resources对象,这个对象就是我们可以使用的apk中的资源了
/**
* Add an additional set of assets to the asset manager. This can be
* either a directory or ZIP file. Not for use by applications. Returns
* the cookie of the added asset, or 0 on failure.
* {@hide}
*/
public final int addAssetPath(String path) {
int res = addAssetPathNative(path);
return res;
}
3.从Resource对象获取资源,文本、图片等等
File sdcardPath = Environment.getExternalStorageDirectory().getAbsoluteFile();
String pluginApk = "mypluginapk-debug.apk";
String pluginPath = sdcardPath.getAbsolutePath() + File.separator + pluginApk;
int resId = getAppNameResId(pluginPath);
Resources resources = getResourcesObject(MainActivity.this, pluginPath);
//获取资源
String apkName = resources.getText(resId).toString();
Log.v(TAG, "apkName=" + apkName);
mTv.setText("apkName=" + apkName);
三、动态加载apk中的代码
原理也是通过类加载器,比如构造DexClassLoader对象,传入apk路径,然后通过反射调用apk中的类的方法。
本博客所有文章除特别声明外,均采用 CC BY-SA 4.0 协议 ,转载请注明出处!