在android开发中动态加载已安装和未安装的apk资源,是很有用的,可以用来实现换肤功能等等。今天我们来学习。
首先新建一个工程plugpicinstall,我们需要往该工程的asset目录和drawable目录下拷贝一些呆会需要加载的图片。运行该工程,即安装。
我们先看看如何实现加载已经安装的apk中的资源:
我们需要先写两个方法,用来获取对应的已安装的apk的context对象和resource对应的id,如下:
/**
* 该方法用来获取已经安装的apk对应的context对象
* @return
* @throws NameNotFoundException
*/
private Context getInstalledContext() throws NameNotFoundException {
return createPackageContext("com.example.plugpicinstall",Context.CONTEXT_IGNORE_SECURITY | Context.CONTEXT_INCLUDE_CODE);
}
/**
* 该方法用来获取已经安装的apk中对应的resource对象
* @param resources
* @param resType
* @param resName
* @return
*/
private int getResourceId(Resources resources,String resType,String resName) {
return resources.getIdentifier(resName, resType,"com.example.plugpicinstall");
}
接下来是加载drawable文件加下图片和加载string.xml中的字符串的代码:
Resources installedResource = null;
try {
//得到已经安装的apk的resource对象
installedResource = getInstalledContext().getResources();
} catch (NameNotFoundException e) {
e.printStackTrace();
}
imageViewInstall.setImageDrawable(installedResource.getDrawable(getResourceId(installedResource,"drawable","three")));
String app_name = installedResource.getString(getResourceId(installedResource, "string","app_name"));
String hello_world = installedResource.getString(getResourceId(installedResource, "string","hello_world"));
Toast.makeText(MainActivity.this,"app_name is :"+app_name+"===hello_world is :"+hello_world,Toast.LENGTH_LONG).show();
下面是加载已经安装的apk中的asset中的资源:
AssetManager assetManager = getInstalledContext().getResources().getAssets();
InputStream ins = assetManager.open("six.jpg");
Bitmap bitmap = BitmapFactory.decodeStream(ins);
imageViewInstall.setImageBitmap(bitmap);
这样就实现了加载已经安装好的apk中的资源文件。
接下来看看,加载没有安装的apk中对应的文件,首先将刚才的plugpicinstall工程从手机上卸载,然后将bin目录下生成的apk拷贝到手机sd卡相应的目录下:
同样,首先新建一个方法用来获取没有安装的apk对应的resource对象:
/**
* 该方法用来获取未安装的apk的reosurces对象
* @return
*/
private Resources getUnInstalledResource() {
// 反射出资源管理器
try {
Class> assetManager_clazz = Class
.forName("android.content.res.AssetManager");
//生成assetManager对象
Object assetObj = assetManager_clazz.newInstance();
//因为addAssetPath是隐藏的,所以只能通过反射来获取
Method addAssetMethod = assetManager_clazz.getDeclaredMethod("addAssetPath",String.class);
addAssetMethod.invoke(assetObj,"/storage/sdcard0/183/plugpicinstall.apk");
Resources resources = getResources();
Constructor>resources_constructor = Resources.class.getConstructor(assetManager_clazz,resources.getDisplayMetrics().getClass(),resources.getConfiguration().getClass());
resources = (Resources) resources_constructor.newInstance(assetObj,resources.getDisplayMetrics(),resources.getConfiguration());
//返回/storage/sdcard0/183/plugpicinstall.apk的resources实例
return resources;
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (NoSuchMethodException e) {
e.printStackTrace();
} catch (IllegalArgumentException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
return null;
}
下面的代码实现加载未安装的apk中的资源:
String apkPath = "/storage/sdcard0/183/plugpicinstall.apk";
String dexPath = MainActivity.this.getDir("dex",Context.MODE_PRIVATE).getAbsolutePath();
DexClassLoader classLoader = new DexClassLoader(apkPath, dexPath, null, getClassLoader());
try {
//反射得到R文件的内部类drawable
Class> drawable_clazz = classLoader.loadClass("com.example.plugpicinstall.R$drawable");
//得到drawable类的所有属性
Field[]fields = drawable_clazz.getDeclaredFields();
for (Field field : fields) {
field.setAccessible(true);
if (field.getName().equals("ten")) {
int id = field.getInt(new R.id());
imageViewInstall.setBackground(getUnInstalledResource().getDrawable(id));
}
}
//反射得到R文件的内部类string
Class>string_clazz = classLoader.loadClass("com.example.plugpicinstall.R$string");
StringBuffer sb = new StringBuffer();
//得到string内部类的所有属性,这些属性就是我们在string.xml文件中生命的字符串资源
Field[]fields2 = string_clazz.getDeclaredFields();
int id = 0;
for (Field field : fields2) {
//得到对应的字符串资源的id值
id = field.getInt(new R.id());
sb.append(getUnInstalledResource().getString(id));
}
Toast.makeText(MainActivity.this,sb.toString(),Toast.LENGTH_SHORT).show();
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (IllegalArgumentException e) {
e.printStackTrace();
}
同样的,下面是获取未安装apk的asset资源的方法:
AssetManager assetManager = getUnInstalledResource().getAssets();
InputStream ins;
try {
ins = assetManager.open("five.jpg");
Bitmap bitmap = BitmapFactory.decodeStream(ins);
imageViewInstall.setImageBitmap(bitmap);
} catch (IOException e) {
e.printStackTrace();
}
到现在为止我们的动态加载资源就结束了。
复习一下动态加载,在plugpicinstall工程中新建一个类DynamicClass.java如下:
package com.example.plugpicinstall;
import android.app.Activity;
import android.widget.Toast;
public class DynamicClass {
private Activity mActivity = null;
public void init(Activity activity) {
this.mActivity = activity;
}
public void showHello(String name) {
Toast.makeText(mActivity,"your name is :"+name, Toast.LENGTH_SHORT).show();
}
public void showAddResult(int a,int b) {
Toast.makeText(mActivity,"the result is :"+(a+b),Toast.LENGTH_SHORT).show();
}
}
这个类中定义的方法,就是等会需要加载运行的。
再次将该工程plugpicinstall打包成apk,将该apk放入到sdcard的某一个目录下。
下面是加载该类DynamicClass.java类中方法的代码:
运行showAddResult方法:
String apkPath = "/storage/sdcard0/183/plugpicinstall.apk";
String dexPath = MainActivity.this.getDir("dex",Context.MODE_PRIVATE).getAbsolutePath();
DexClassLoader classLoader = new DexClassLoader(apkPath, dexPath, null, getClassLoader());
try {
Class> clazz = classLoader.loadClass("com.example.plugpicinstall.DynamicClass");
Object obj = clazz.newInstance();
Method initMeghod = clazz.getDeclaredMethod("init",Activity.class);
initMeghod.invoke(obj,MainActivity.this);
//利用反射运行showAddResult方法
Class[]params = new Class[2];
params[0] = Integer.TYPE;
params[1] = Integer.TYPE;
Method showAddMethod = clazz.getDeclaredMethod("showAddResult",params);
showAddMethod.invoke(obj, 1,33);
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (NoSuchMethodException e) {
e.printStackTrace();
} catch (IllegalArgumentException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
运行showHello方法:
//apk文件的存放路径
String apkPath = "/storage/sdcard0/183/plugpicinstall.apk";
//dex文件的路径
String dexPath = MainActivity.this.getDir("dex",Context.MODE_PRIVATE).getAbsolutePath();
DexClassLoader classLoader = new DexClassLoader(apkPath, dexPath, null, getClassLoader());
try {
//加载需要的类
Class> clazz = classLoader.loadClass("com.example.plugpicinstall.DynamicClass");
Object obj = clazz.newInstance();
//利用反射调用init方法,将context对象赋值
Method initMeghod = clazz.getDeclaredMethod("init",Activity.class);
initMeghod.invoke(obj,MainActivity.this);
//利用反射执行showHello方法,传入一个string参数
Method helloMethod = clazz.getDeclaredMethod("showHello",String.class);
helloMethod.invoke(obj,"李磊");
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (NoSuchMethodException e) {
e.printStackTrace();
} catch (IllegalArgumentException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
源码下载