通过加载未安装应用的方式实现app插件化
学习应用插件化技术,通过加载未安装apk,实现插件化功能,此处做个笔记,方便查阅
获取未安装应用的信息
/**
* @param apkDir 未安装应用的路径,包含应用带apk后缀的名称
* @return 获得未安装应用的信息
* */
private String[] getUninstallApkInfo(Context context,String apkDir)throws Exception{
String[] apkInfo = new String[2];
PackageManager pm = context.getPackageManager();
PackageInfo packageInfo = pm.getPackageArchiveInfo(apkDir, PackageManager.GET_ACTIVITIES);
if(packageInfo!=null){
ApplicationInfo appInfo = packageInfo.applicationInfo;
String versionName = packageInfo.versionName;
String packageName = appInfo.packageName;
Drawable icon = pm.getApplicationIcon(appInfo);
String appName = pm.getApplicationLabel(appInfo).toString();//此处获取到的appName与应用包名一样
apkInfo[0] = "plug.apk"; //插件apk名称
apkInfo[1] = packageName; //插件apk包名
}
return apkInfo;
}
获取未安装应用的Resources对象
/**
* 反射获取插件apk的Resources对象
* @para apkDir 插件应用路径,带有.apk的名称后辍
* @return 得到对应插件应用的Resource对象
* */
private Resources getPluginResources(Context context,String apkDir) {
try {
AssetManager assetManager = AssetManager.class.newInstance();
Method addAssetPath = assetManager.getClass().getMethod("addAssetPath", String.class);
//将未安装的apk添加进AssetManager中,第二个参数是插件apk的路径带apk名
addAssetPath.invoke(assetManager, apkDir);
Resources superRes = context.getResources();
Resources resources = new Resources(assetManager, superRes.getDisplayMetrics(), superRes.getConfiguration());
return resources;
}catch (Exception e){
e.printStackTrace();
return null;
}
}
获取插件应用中的图片
/**
* 获取资源文件
* */
public Drawable dynamicLoadApk() throws Exception{
String apkName = "plugin.apk"; //名称随意,记住就行
String mApkDir = Environment.getExternalStorageDirectory().getPath()+File.separator+"Pictures"+File.separator+apkName;
String apkPackageName = mApkInfo[1];
//在应用安装目录下创建一个名为app_dex文件夹目录,如果已经存在则不创建
File optimizedDirFile = mContext.getDir("dex", Context.MODE_PRIVATE);
// 参数一:包含dex的apk或jar路径
// 参数二:apk,jar解压生成dex文件的存储路径
// 参数三:本地library库目录,一般为null
// 参数四:父classLoader
DexClassLoader dexClassLoader = new DexClassLoader(mApkDir, optimizedDirFile.getPath(), null, ClassLoader.getSystemClassLoader());
//使用apk自己的类加载器,加载图片资源类,其它资源文件类似,修改.R$mipmap中的mipmap为string可获取字符串class对象,,mApkInfo[1]是插件apk的包名
Class<?> clazz = dexClassLoader.loadClass(Class<?> clazz = dexClassLoader.loadClass(apkPackageName + ".R$mipmap"); + ".R$mipmap");
//获取名称为one的图片资源***********************
Field field = clazz.getDeclaredField("one");
int resId = field.getInt(R.mipmap.class);
//得到插件apk的resources
Resources resources = getPluginResources(mContext, mApkDir);
if(resources!=null){
Drawable drawable = resources.getDrawable(resId);
return drawable;
}
return null;
}
简单使用
public class MainActivity extends Activity {
private static final String appName = "plug.apk";
private Drawable drawable;
Handler mHandler = new Handler(){
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
switch (msg.what){
case 1:
bg.setImageDrawable(drawable);
break;
}
}
};
private ImageView bg;
private LoadUtils loadUtils;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
init();
}
private void init() {
bg = ((ImageView) findViewById(R.id.bg));
try {
loadUtils = new LoadUtils(this, appName);
drawable = loadUtils.dynamicLoadApk();
} catch (Exception e) {
e.printStackTrace();
}
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.menu,menu);
return true;
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()){
case R.id.update:
mHandler.sendEmptyMessage(1);
break;
}
return true;
}
}