Android 插件化基础

加载apk中的代码

Android中加载apk我们可以使用DexClassLoader

A class loader that loads classes from .jar and .apk files containing a classes.dex entry. This can be used to execute code not installed as part of an application.

当我们的apk或者jar没有被加载的时候,我们可以使用DexClassLoader去加载对应的类。

  • 插件apk
    插件apk中我们首先写一个类,这个类中定义一个方法,然后我们在宿主中调用这个方法
package com.example.apkbeloaded;

import android.util.Log;

public class ClassToBeImported {
    public static ClassLoader method() {
        Log.v("ClassToBeImported", "called method of class " + ClassToBeImported.class.getName());
        return ClassToBeImported.class.getClassLoader();
    }
}
  • 宿主apk
    宿主apk中,首先加载apk中的dex文件,然后加载对应的类,为了方便我这里将插件apk文件放置到asserts中,然后通过代码把这个apk文件拷贝到apk应用自己的缓存目录下

    代码:
    public void loadDexClassses() {
        String des = "/data/data/com.example.hostdemolearn/files" + "/" + "apkbeloaded-release-unsigned.apk";
        File desFile = new File(des);
        if (!desFile.exists()) {
            copyAssetsApkToFile(this, "plugins/apkbeloaded-release-unsigned.apk", des);
        }
        /**
         * dexPath:apk路径,不能为空。
         *
         * optimizedDirectory:解压后的.dex文件的存储路径,不能为空。这个路径强烈建议使用应用程序的私有路径,不要放到sdcard上,否则代码容易被注入攻击。
         *
         * libraryPath:os库的存放路径,可以为空,若有os库,必须填写。
         *
         * parent:父亲加载器,一般为context.getClassLoader(),使用当前上下文的类加载器。
         */
        /**
         * DexClassLoader教程
         * https://developer.android.com/reference/dalvik/system/DexClassLoader.html?spm=ata.21736010.0.0.3d346c6bDsq2oP&hl=zh-cn
         */
        final DexClassLoader classloader = new DexClassLoader(des, "/data/data/com.example.hostdemolearn/cache", "/data/data/com.example.hostdemolearn/cache", ClassLoader.getSystemClassLoader());
        try {
            Class<?> classToLoad = (Class<?>) classloader.loadClass("com.example.apkbeloaded.ClassToBeImported");
            Method m = classToLoad.getMethod("method");
            m.invoke(null);
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
            Log.i(TAG, "loadDexClassses: ");
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
            Log.i(TAG, "loadDexClassses: NoSuchMethodException");
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        }
    }

    public void copyAssetsApkToFile(Context context, String src, String des) {
        try {
            InputStream is = context.getAssets().open(src);
            FileOutputStream fos = new FileOutputStream(new File(des));
            byte[] buffer = new byte[1024];
            while (true) {
                int len = is.read(buffer);
                if (len == -1) {
                    break;
                }
                fos.write(buffer, 0, len);
            }
            is.close();
            fos.close();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

其中/data/data/com.example.hostdemolearn/是应用自己的缓存目录,通过dexClassLoader加载了apk中的dex文件以后,然后加载对应的类,最后通过反射调用对应的方法。

上面的代码,主要其实也是DexClassLoader创建,然后使用。

加载apk中的资源

  • 使用反射创建
    通过调用addAssetPath方法,传入apk的路径,创建一个新的AsserManager对象,通过这个asserManager我们创建一个新的Resource对象
    public Resources getApkResource(Context context, String apkPath) {
        AssetManager assetManager = createAssetManager(apkPath);
        return new Resources(assetManager,
                context.getResources().getDisplayMetrics(),
                context.getResources().getConfiguration());
    }
    private AssetManager createAssetManager(String apkPath) {
        try {
            AssetManager assetManager = AssetManager.class.newInstance();
            AssetManager.class.getDeclaredMethod("addAssetPath", String.class).invoke(
                    assetManager, apkPath);
            return assetManager;
        } catch (Throwable th) {
            th.printStackTrace();
        }
        return null;
    }
  • 使用PackageManager创建
    下面的这个也是Replugin使用的创建Resource的方式
    public Resources getResourcesByPackageManger(Context context, String path) {
        PackageManager packageManager = context.getPackageManager();
        // PackageInfo
        PackageInfo packageInfo = packageManager.getPackageArchiveInfo(path,
                PackageManager.GET_ACTIVITIES | PackageManager.GET_SERVICES | PackageManager.GET_PROVIDERS | PackageManager.GET_RECEIVERS | PackageManager.GET_META_DATA);


        packageInfo.applicationInfo.sourceDir = path;
        packageInfo.applicationInfo.publicSourceDir = path;

        if (TextUtils.isEmpty(packageInfo.applicationInfo.processName)) {
            packageInfo.applicationInfo.processName = packageInfo.applicationInfo.packageName;
        }
        Resources resources = null;
        try {
            resources = packageManager.getResourcesForApplication(packageInfo.applicationInfo);
        } catch (PackageManager.NameNotFoundException e) {
            e.printStackTrace();
        }
        return resources;
    }
  • 通过Resource对象加载资源
                String des = "/data/data/com.example.hostdemolearn/files/apkbeloaded-release-unsigned.apk";
                // 通过反射创建
                Resources resources = getApkResource(MainActivity.this, des);
                // 通过PackageManager创建
                // Resources resources = getResourcesByPackageManger(MainActivity.this, des);
                Drawable drawable = resources.getDrawable(resources.getIdentifier("plugin_image", "drawable", "com.example.apkbeloaded"));
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值