【Android 插件化】“ 插桩式 “ 插件化框架 ( 类加载器创建 | 资源加载 )

举报
韩曙亮 发表于 2022/01/10 23:33:04 2022/01/10
【摘要】 Android 插件化系列文章目录 【Android 插件化】插件化简介 ( 组件化与插件化 ) 【Android 插件化】插件化原理 ( JVM 内存数据 | 类加载流程 ) 【Android 插件化...

Android 插件化系列文章目录

【Android 插件化】插件化简介 ( 组件化与插件化 )
【Android 插件化】插件化原理 ( JVM 内存数据 | 类加载流程 )
【Android 插件化】插件化原理 ( 类加载器 )
【Android 插件化】“ 插桩式 “ 插件化框架 ( 原理与实现思路 )
【Android 插件化】“ 插桩式 “ 插件化框架 ( 类加载器创建 | 资源加载 )
【Android 插件化】“ 插桩式 “ 插件化框架 ( 注入上下文的使用 )
【Android 插件化】“ 插桩式 “ 插件化框架 ( 获取插件入口 Activity 组件 | 加载插件 Resources 资源 )
【Android 插件化】“ 插桩式 “ 插件化框架 ( 运行应用 | 代码整理 )




参考 【Android 插件化】“ 插桩式 “ 插件化框架 ( 原理与实现思路 ) 中给出的实现思路 , 逐步实现 “ 插桩式 “ 插件化框架 ;




一、创建核心依赖库



创建 " Android Library " 依赖库 , 作为 " 插件化 " 框架 核心依赖库 ;

在这里插入图片描述

" 宿主 " 模块 应用 , 依赖该 “ 插桩式 “ 插件化框架 核心库 , 依靠该框架核心库 , 管理 " 插件 " 模块 编译打包成的 apk 文件 ;





二、创建类加载器



创建 DexClassLoader , 使用其构造函数创建 , 需要传入四个参数到构造函数中 ;

package dalvik.system;

import java.io.File;

public class DexClassLoader extends BaseDexClassLoader {
    public DexClassLoader(String dexPath, String optimizedDirectory, String librarySearchPath, ClassLoader parent) {
        super((String)null, (File)null, (String)null, (ClassLoader)null);
        throw new RuntimeException("Stub!");
    }
}

  
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

DexClassLoader 构造函数 参数说明 :

String dexPath : 插件包加载路径 ;

String optimizedDirectory : 开发者指定的 apk 插件包解压后的缓存路径 ;

String librarySearchPath : 函数库的搜索路径 , 可设置为空 , 忽略 ;

ClassLoader parent : DexClassLoader 加载器的父类加载器 ;


创建插件包解压后的缓存路径 : 注意 String optimizedDirectory 参数对应的路径必须是私有的 ;

// DexClassLoader 的 optimizedDirectory 操作目录必须是私有的
// ( 模式必须是 Context.MODE_PRIVATE )
File optimizedDirectory = context.getDir("plugin", Context.MODE_PRIVATE);

  
 
  • 1
  • 2
  • 3

创建类加载器 : 传入上述 4 4 4 个参数 , 创建类加载器 ;

// 创建 DexClassLoader
mDexClassLoader = new DexClassLoader(
        loadPath, // 加载路径
        optimizedDirectory.getAbsolutePath(), // apk 解压缓存目录
        null,
        context.getClassLoader() // DexClassLoader 加载器的父类加载器
);

  
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

注意 : 类加载时 , 只会加载一次 , 如果有重复的类 , 不会重复加载 ;

BootClassLoader 主要作用是加载 JDK 中的字节码类对象 ;

DexClassLoaderPathClassLoader 主要作用是加载 Android 和 引入的第三方库 中的字节码类对象 ;





三、加载资源



加载资源时需要使用到 AssetManager , 但是其构造函数是 隐藏 的 , 被 @Hide 注解 , 开发者无法直接调用 , 需要使用反射进行调用 ;

通过反射创建 AssetManager 对象 : 注意异常捕获 ;

// 加载资源
try {
    // 通过反射创建 AssetManager
    AssetManager assetManager = AssetManager.class.newInstance();
} catch (IllegalAccessException e) {
    e.printStackTrace();
} catch (InstantiationException e) {
    e.printStackTrace();
}

  
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

创建 AssetManager 对象后 , 调用 addAssetPath 方法 , 添加资源的路径 , 用于加载插件包路径下的资源文件 ;

addAssetPath 也是隐藏方法 , 也是需要使用反射调用该方法 ;

// 通过反射获取 AssetManager 中的 addAssetPath 隐藏方法
Method addAssetPathMethod = assetManager.
        getClass().
        getDeclaredMethod("addAssetPath");
// 调用反射方法
addAssetPathMethod.invoke(assetManager, loadPath);

  
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

AssetManager 示例代码 :

public final class AssetManager implements AutoCloseable {
    /**
     * @hide
     */
    @UnsupportedAppUsage
    public AssetManager() {
    }
    
    /**
     * @deprecated Use {@link #setApkAssets(ApkAssets[], boolean)}
     * @hide
     */
    @Deprecated
    @UnsupportedAppUsage
    public int addAssetPath(String path) {
        return addAssetPathInternal(path, false /*overlay*/, false /*appAsLib*/);
    }
}

  
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18

获取 Resources 资源对象 : 通过上述加载插件资源后的 AssetManager 对象来创建 Resources 资源对象 ;

// 获取资源
mResources = new Resources(
        assetManager,
        context.getResources().getDisplayMetrics(),
        context.getResources().getConfiguration()
);

  
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

传入的 DisplayMetrics metrics 和 Configuration config 参数从调用插件包的上下文中获取 ;


加载资源部分代码示例 :

首先 , 通过反射创建 AssetManager 对象 ;
然后 , 通过反射调用并执行 AssetManager 对象的 addAssetPath 方法 , 加载插件包资源 ;
最后 , 调用 Resources 构造函数 , 创建资源 , 传入 AssetManager 对象 和 上下文相关参数 ;

// 加载资源
try {
    // 通过反射创建 AssetManager
    AssetManager assetManager = AssetManager.class.newInstance();
    
    // 通过反射获取 AssetManager 中的 addAssetPath 隐藏方法
    Method addAssetPathMethod = assetManager.
            getClass().
            getDeclaredMethod("addAssetPath");
            
    // 调用反射方法
    addAssetPathMethod.invoke(assetManager, loadPath);
    
    // 获取资源
    mResources = new Resources(
            assetManager,
            context.getResources().getDisplayMetrics(),
            context.getResources().getConfiguration()
    );
    
} catch (IllegalAccessException e) {
    // 调用 AssetManager.class.newInstance() 反射构造方法异常
    e.printStackTrace();
} catch (InstantiationException e) {
    // 调用 AssetManager.class.newInstance() 反射构造方法异常
    e.printStackTrace();
} catch (NoSuchMethodException e) {
    // getDeclaredMethod 反射方法异常
    e.printStackTrace();
} catch (InvocationTargetException e) {
    // invoke 执行反射方法异常
    e.printStackTrace();
}

  
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33




四、插件管理器完整代码



插件管理器完整代码 :

package com.example.plugin_core;

import android.content.Context;
import android.content.pm.PackageInfo;
import android.content.res.AssetManager;
import android.content.res.Resources;

import java.io.File;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

import dalvik.system.DexClassLoader;

/**
 * 插件化框架核心类
 */
public class PluginManager {

    /**
     * 类加载器
     * 用于加载插件包 apk 中的 classes.dex 文件中的字节码对象
     */
    private DexClassLoader mDexClassLoader;

    /**
     * 从插件包 apk 中加载的资源
     */
    private Resources mResources;

    /**
     * 插件包信息类
     */
    private PackageInfo mPackageInfo;

    /**
     * 加载插件的上下文对象
     */
    private Context mContext;

    /**
     * PluginManager 单例
     */
    private static PluginManager instance;

    private PluginManager(){

    }

    /**
     * 获取单例类
     * @return
     */
    public static PluginManager getInstance(){
        if (instance == null) {
            instance = new PluginManager();
        }
        return instance;
    }

    /**
     * 加载插件
     * @param context 加载插件的应用的上下文
     * @param loadPath 加载的插件包地址
     */
    public void loadPlugin(Context context, String loadPath) {
        this.mContext = context;

        // DexClassLoader 的 optimizedDirectory 操作目录必须是私有的
        // ( 模式必须是 Context.MODE_PRIVATE )
        File optimizedDirectory = context.getDir("plugin", Context.MODE_PRIVATE);

        // 创建 DexClassLoader
        mDexClassLoader = new DexClassLoader(
                loadPath, // 加载路径
                optimizedDirectory.getAbsolutePath(), // apk 解压缓存目录
                null,
                context.getClassLoader() // DexClassLoader 加载器的父类加载器
        );

        // 加载资源
        try {
            // 通过反射创建 AssetManager
            AssetManager assetManager = AssetManager.class.newInstance();

            // 通过反射获取 AssetManager 中的 addAssetPath 隐藏方法
            Method addAssetPathMethod = assetManager.
                    getClass().
                    getDeclaredMethod("addAssetPath");

            // 调用反射方法
            addAssetPathMethod.invoke(assetManager, loadPath);

            // 获取资源
            mResources = new Resources(
                    assetManager,
                    context.getResources().getDisplayMetrics(),
                    context.getResources().getConfiguration()
            );

        } catch (IllegalAccessException e) {
            // 调用 AssetManager.class.newInstance() 反射构造方法异常
            e.printStackTrace();
        } catch (InstantiationException e) {
            // 调用 AssetManager.class.newInstance() 反射构造方法异常
            e.printStackTrace();
        } catch (NoSuchMethodException e) {
            // getDeclaredMethod 反射方法异常
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            // invoke 执行反射方法异常
            e.printStackTrace();
        }
    }
}

  
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114




五、博客资源



博客资源 :

文章来源: hanshuliang.blog.csdn.net,作者:韩曙亮,版权归原作者所有,如需转载,请联系作者。

原文链接:hanshuliang.blog.csdn.net/article/details/117453759

【版权声明】本文为华为云社区用户转载文章,如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。