在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")) { <span style="white-space:pre"> </span>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();<span style="white-space:pre"> </span>//利用反射调用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();}
好了,至此动态加载已安装和未安装的apk资源就实现了。
源码下载