加载apk中的代码
Android中加载apk我们可以使用DexClassLoader
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.
当我们的apk或者jar没有被加载的时候,我们可以使用DexClassLoader去加载对应的类。
- 插件apk
插件apk中我们首先写一个类,这个类中定义一个方法,然后我们在宿主中调用这个方法
package com.example.apkbeloaded;
import android.util.Log;
public class ClassToBeImported {
public static ClassLoader method() {
Log.v("ClassToBeImported", "called method of class " + ClassToBeImported.class.getName());
return ClassToBeImported.class.getClassLoader();
}
}
- 宿主apk
宿主apk中,首先加载apk中的dex文件,然后加载对应的类,为了方便我这里将插件apk文件放置到asserts中,然后通过代码把这个apk文件拷贝到apk应用自己的缓存目录下
代码:
public void loadDexClassses() {
String des = "/data/data/com.example.hostdemolearn/files" + "/" + "apkbeloaded-release-unsigned.apk";
File desFile = new File(des);
if (!desFile.exists()) {
copyAssetsApkToFile(this, "plugins/apkbeloaded-release-unsigned.apk", des);
}
/**
* dexPath:apk路径,不能为空。
*
* optimizedDirectory:解压后的.dex文件的存储路径,不能为空。这个路径强烈建议使用应用程序的私有路径,不要放到sdcard上,否则代码容易被注入攻击。
*
* libraryPath:os库的存放路径,可以为空,若有os库,必须填写。
*
* parent:父亲加载器,一般为context.getClassLoader(),使用当前上下文的类加载器。
*/
/**
* DexClassLoader教程
* https://developer.android.com/reference/dalvik/system/DexClassLoader.html?spm=ata.21736010.0.0.3d346c6bDsq2oP&hl=zh-cn
*/
final DexClassLoader classloader = new DexClassLoader(des, "/data/data/com.example.hostdemolearn/cache", "/data/data/com.example.hostdemolearn/cache", ClassLoader.getSystemClassLoader());
try {
Class<?> classToLoad = (Class<?>) classloader.loadClass("com.example.apkbeloaded.ClassToBeImported");
Method m = classToLoad.getMethod("method");
m.invoke(null);
} catch (ClassNotFoundException e) {
e.printStackTrace();
Log.i(TAG, "loadDexClassses: ");
} catch (NoSuchMethodException e) {
e.printStackTrace();
Log.i(TAG, "loadDexClassses: NoSuchMethodException");
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
}
public void copyAssetsApkToFile(Context context, String src, String des) {
try {
InputStream is = context.getAssets().open(src);
FileOutputStream fos = new FileOutputStream(new File(des));
byte[] buffer = new byte[1024];
while (true) {
int len = is.read(buffer);
if (len == -1) {
break;
}
fos.write(buffer, 0, len);
}
is.close();
fos.close();
} catch (Exception e) {
e.printStackTrace();
}
}
其中/data/data/com.example.hostdemolearn/是应用自己的缓存目录,通过dexClassLoader加载了apk中的dex文件以后,然后加载对应的类,最后通过反射调用对应的方法。
上面的代码,主要其实也是DexClassLoader创建,然后使用。
加载apk中的资源
- 使用反射创建
通过调用addAssetPath方法,传入apk的路径,创建一个新的AsserManager对象,通过这个asserManager我们创建一个新的Resource对象
public Resources getApkResource(Context context, String apkPath) {
AssetManager assetManager = createAssetManager(apkPath);
return new Resources(assetManager,
context.getResources().getDisplayMetrics(),
context.getResources().getConfiguration());
}
private AssetManager createAssetManager(String apkPath) {
try {
AssetManager assetManager = AssetManager.class.newInstance();
AssetManager.class.getDeclaredMethod("addAssetPath", String.class).invoke(
assetManager, apkPath);
return assetManager;
} catch (Throwable th) {
th.printStackTrace();
}
return null;
}
- 使用PackageManager创建
下面的这个也是Replugin使用的创建Resource的方式
public Resources getResourcesByPackageManger(Context context, String path) {
PackageManager packageManager = context.getPackageManager();
// PackageInfo
PackageInfo packageInfo = packageManager.getPackageArchiveInfo(path,
PackageManager.GET_ACTIVITIES | PackageManager.GET_SERVICES | PackageManager.GET_PROVIDERS | PackageManager.GET_RECEIVERS | PackageManager.GET_META_DATA);
packageInfo.applicationInfo.sourceDir = path;
packageInfo.applicationInfo.publicSourceDir = path;
if (TextUtils.isEmpty(packageInfo.applicationInfo.processName)) {
packageInfo.applicationInfo.processName = packageInfo.applicationInfo.packageName;
}
Resources resources = null;
try {
resources = packageManager.getResourcesForApplication(packageInfo.applicationInfo);
} catch (PackageManager.NameNotFoundException e) {
e.printStackTrace();
}
return resources;
}
- 通过Resource对象加载资源
String des = "/data/data/com.example.hostdemolearn/files/apkbeloaded-release-unsigned.apk";
// 通过反射创建
Resources resources = getApkResource(MainActivity.this, des);
// 通过PackageManager创建
// Resources resources = getResourcesByPackageManger(MainActivity.this, des);
Drawable drawable = resources.getDrawable(resources.getIdentifier("plugin_image", "drawable", "com.example.apkbeloaded"));