android动态加载已安装和未安装的apk资源

           在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资源就实现了。


源码下载









郑重声明:本站内容如果来自互联网及其他传播媒体,其版权均属原媒体及文章作者所有。转载目的在于传递更多信息及用于网络分享,并不代表本站赞同其观点和对其真实性负责,也不构成任何其他建议。