android插件化,简单调用未安装apk的资源和代码

插件化网上资料挺多的,我现在还是简单的研究一下,主要参考了以下文章

插件化开发—动态加载技术加载已安装和未安装的apk这篇文章主要讲了调用资源这一块

Android动态加载插件APK这篇文章主要讲了代码这一块

整合了一下

 

public class PluginUtil {
    public final static String apkDir = "/storage/emulated/0/360";
    private Context context;
    private DexClassLoader dexClassLoader;
    private String apkName;
    private String apkPackageName;
    public PluginUtil(Context context,String apkPackageName,String apkName){
        this.context = context;
        this.apkName = apkName;
        this.apkPackageName = apkPackageName;

        File optimizedDirectoryFile = context.getDir("dex", Context.MODE_PRIVATE);//在应用安装目录下创建一个名为app_dex文件夹目录,如果已经存在则不创建
        //参数:1、包含dex的apk文件或jar文件的路径,2、apk、jar解压缩生成dex存储的目录,3、本地library库目录,一般为null,4、父ClassLoader
        dexClassLoader = new DexClassLoader(apkDir+File.separator+apkName, optimizedDirectoryFile.getPath(), null, context.getClassLoader());
    }

    //获取apk数据
    private String[] getUninstallApkInfo(Context context, String archiveFilePath) {
        String[] info = new String[2];
        PackageManager pm = context.getPackageManager();
        PackageInfo pkgInfo = pm.getPackageArchiveInfo(archiveFilePath, PackageManager.GET_ACTIVITIES);
        if (pkgInfo != null) {
            ApplicationInfo appInfo = pkgInfo.applicationInfo;
            String versionName = pkgInfo.versionName;//版本号
            Drawable icon = pm.getApplicationIcon(appInfo);//图标
            String appName = pm.getApplicationLabel(appInfo).toString();//app名称
            String pkgName = appInfo.packageName;//包名
            info[0] = appName;
            info[1] = pkgName;
        }
        return info;
    }
    private Resources getPluginResources() {
        try {
            AssetManager assetManager = AssetManager.class.newInstance();
            Method addAssetPath = assetManager.getClass().getMethod("addAssetPath", String.class);//反射调用方法addAssetPath(String path)
            //第二个参数是apk的路径:Environment.getExternalStorageDirectory().getPath()+File.separator+"plugin"+File.separator+"apkplugin.apk"
            addAssetPath.invoke(assetManager, apkDir+ File.separator+apkName);//将未安装的Apk文件的添加进AssetManager中,第二个参数为apk文件的路径带apk名
            Resources superRes = context.getResources();
            Resources mResources = new Resources(assetManager, superRes.getDisplayMetrics(),
                    superRes.getConfiguration());
            return mResources;
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }
    //drawableId = "forest"
    //资源的位置根据自己的情况修改,如果把资源放在mipmap下,就把drawable改成mipmap
    public Bitmap getApkBitmap(String drawableId){
        try {
            Class<?> clazz = dexClassLoader.loadClass(apkPackageName + ".R$drawable");//通过使用apk自己的类加载器,反射出R类中相应的内部类进而获取我们需要的资源id
            Field field = clazz.getDeclaredField(drawableId);
            int resId = field.getInt(R.id.class);//得到图片id
            Resources mResources = getPluginResources();
            return BitmapFactory.decodeResource(mResources,resId);
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        } catch (NoSuchFieldException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        }
        return null;
    }
    public Drawable getApkDrawable(String drawableId){
        try {
            Class<?> clazz = dexClassLoader.loadClass(apkPackageName + ".R$drawable");//通过使用apk自己的类加载器,反射出R类中相应的内部类进而获取我们需要的资源id
            Field field = clazz.getDeclaredField(drawableId);
            int resId = field.getInt(R.id.class);//得到图片id
            Resources mResources = getPluginResources();
            assert mResources != null;
            return mResources.getDrawable(resId);
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        } catch (NoSuchFieldException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        }
        return null;
    }

    public Object getClass(String className){
        try {
            Class<?> clazz = dexClassLoader.loadClass(className);
            return clazz.newInstance();
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        } catch (InstantiationException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        }
        return null;
    }
}

 

apk直接拉到文件夹了,没有他们的复制这一步,如果开发时应该是直接下载下来,毕竟选择这么开发也想给安装包减肥

调用资源

 

PluginUtil pluginUtil = new PluginUtil(this,"com.example.plugina","plugin_a.apk");
ImageView imageView = findViewById(R.id.plugin_image);
imageView.setImageBitmap(pluginUtil.getApkBitmap("forest"));

 

调用代码要说一下DexClassLoader初始化最后一个参数和jar包的调用,我在初始化时把当前app的资源加载器传进来了,如果使用ClassLoader.getSystemClassLoader()会出现错误,详情参考这篇文章Android插件化框架系列之类加载器

调用代码

Object c = pluginUtil.getClass("com.example.plugina.PluginAUtils");
PluginInterface pluginInterface = (PluginInterface) c;
pluginInterface.showToastInfo(this);

jar包的PluginInterface.java

public interface PluginInterface {
    void showToastInfo(Context context);
}

插件apk的PluginAUtils.java

 

public class PluginAUtils implements PluginInterface {

    public void showToastInfo(Context context) {
        Toast.makeText(context, "This is from pluginA", Toast.LENGTH_SHORT).show();
    }
}

 

还要研究activity之类有生命周期的,网上框架也不少,这样就可以像游戏一样不用每次都重新安装就能实现更新了

 

 

 

 

 

 

 

©️2020 CSDN 皮肤主题: 大白 设计师:CSDN官方博客 返回首页