【Android 插件化】Hook 插件化框架 ( 加载插件包资源 )

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

Android 插件化系列文章目录

【Android 插件化】插件化简介 ( 组件化与插件化 )
【Android 插件化】插件化原理 ( JVM 内存数据 | 类加载流程 )
【Android 插件化】插件化原理 ( 类加载器 )

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

【Android 插件化】Hook 插件化框架 ( Hook 技术 | 代理模式 | 静态代理 | 动态代理 )
【Android 插件化】Hook 插件化框架 ( Hook 实现思路 | Hook 按钮点击事件 )
【Android 插件化】Hook 插件化框架 ( Hook Activity 启动过程 | 静态代理 )

【Android 插件化】Hook 插件化框架 ( 从 Hook 应用角度分析 Activity 启动流程 一 | Activity 进程相关源码 )
【Android 插件化】Hook 插件化框架 ( 从 Hook 应用角度分析 Activity 启动流程 二 | AMS 进程相关源码 | 主进程相关源码 )

【Android 插件化】Hook 插件化框架 ( hook 插件化原理 | 插件包管理 )
【Android 插件化】Hook 插件化框架 ( 通过反射获取 “插件包“ 中的 Element[] dexElements )
【Android 插件化】Hook 插件化框架 ( 通过反射获取 “宿主“ 应用中的 Element[] dexElements )
【Android 插件化】Hook 插件化框架 ( 合并 “插件包“ 与 “宿主“ 中的 Element[] dexElements | 设置合并后的 Element[] 数组 )
【Android 插件化】Hook 插件化框架 ( 创建插件应用 | 拷贝插件 APK | 初始化插件包 | 测试插件 DEX 字节码 )

【Android 插件化】Hook 插件化框架 ( Hook Activity 启动流程 | Hook 点分析 )
【Android 插件化】Hook 插件化框架 ( Hook Activity 启动流程 | 反射获取 IActivityManager 对象 )
【Android 插件化】Hook 插件化框架 ( Hook Activity 启动流程 | AMS 启动前使用动态代理替换掉插件 Activity 类 )
【Android 插件化】Hook 插件化框架 ( Hook Activity 启动流程 | 主线程创建 Activity 实例之前使用插件 Activity 类替换占位的组件 )

【Android 插件化】Hook 插件化框架 ( 反射工具类 | 反射常用操作整理 )

【Android 插件化】Hook 插件化框架 ( 插件包资源加载 )


前言

【Android 插件化】Hook 插件化框架 ( Hook Activity 启动流程 | AMS 启动前使用动态代理替换掉插件 Activity 类 )
【Android 插件化】Hook 插件化框架 ( Hook Activity 启动流程 | 主线程创建 Activity 实例之前使用插件 Activity 类替换占位的组件 )

博客中 , 基本已经完成了插件 Activity 的实例化操作 , 已经可以启动插件 Activity , 只剩下资源文件没有获取 ;

本博客开始加载插件包中的资源文件 ;





一、加载插件包资源文件



在插件包中的 Activity , 如果加载 R.layout.activity_main , 拿到的是 “宿主” 应用中的资源 , 无法拿到插件包中的资源 ;


1、反射 AssetManager 类并创建实例对象


反射 AssetManager 类 , 并创建 AssetManager 实例对象 ;

        // 使用反射工具类进行链式调用 , 创建 AssetManager 对象
        AssetManager assetManager = Reflector.on(AssetManager.class).newInstance();

  
 
  • 1
  • 2

涉及到的反射工具类内容 : 上述代码使用了上一篇博客 【Android 插件化】Hook 插件化框架 ( 反射工具类 | 反射常用操作整理 ) 中的 反射工具类 , 涉及到的反射代码如下 :

/**
 * 封装反射相关逻辑的工具类
 * 该封装类会维持链式调用
 */
public class Reflector {
    /**
     * 反射某个类的入口方法
     *
     * @param type 要反射的类
     * @return
     */
    public static Reflector on(Class<?> type) {
        Reflector reflector = new Reflector();
        reflector.mClass = type;
        return reflector;
    }

    /**
     * 创建 mClass 类型的实例对象
     * @param <T>
     * @return
     * @throws Exception
     */
    public <T> T newInstance() {
        try {
            return (T) mClass.newInstance();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
            return null;
        } catch (InstantiationException e) {
            e.printStackTrace();
            return null;
        }
    }
}

  
 
  • 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

2、反射 AssetManager 的 addAssetPath 方法并调用


通过反射获取 AssetManager 的 addAssetPath 方法 , 方法的参数是 String 类型 ;

执行该方法 , 传入当前插件包 APK 的路径 ;

        // 获取插件包 APK 文件路径 , 加载该 APK 下的资源
        // /data/user/0/com.example.plugin_hook/files/plugin.apk
        String pluginPath = mBase.getFilesDir() + "/plugin.apk";

        // 使用反射调用 AssetManager 中的 addAssetPath 方法 , 传入 APK 插件包的路径
        // addAssetPath 方法的参数为 /data/user/0/com.example.plugin_hook/files/plugin.apk
        Reflector.on(assetManager).method("addAssetPath", String.class).call(pluginPath);

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

涉及到的反射工具类内容 : 上述代码使用了上一篇博客 【Android 插件化】Hook 插件化框架 ( 反射工具类 | 反射常用操作整理 ) 中的 反射工具类 , 涉及到的反射代码如下 :

/**
 * 封装反射相关逻辑的工具类
 * 该封装类会维持链式调用
 */
public class Reflector {
    /**
     * 反射某个类的入口方法
     *
     * @param type 要反射的类
     * @return
     */
    public static Reflector on(Class<?> type) {
        Reflector reflector = new Reflector();
        reflector.mClass = type;
        return reflector;
    }

    /**
     * 反射某个类的入口方法
     *
     * @param className 要反射的类名
     * @return
     */
    public static Reflector on(String className) {
        try {
            return on(Class.forName(className));
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
            return null;
        }
    }

    /**
     * 反射某个类的入口方法
     *
     * @param object 反射类对应的实例对象
     * @return
     */
    public static Reflector on(Object object) {
        return on(object.getClass()).with(object);
    }

    /**
     * 反射类中的某个方法
     *
     * @param name
     * @param args
     * @return
     */
    public Reflector method(String name, Class<?>... args) {
        mMethod = findMethod(name, args);
        mMethod.setAccessible(true);
        return this;
    }

    /**
     * 根据方法名 和 参数名称 , 查找 Method 方法
     *      首先在本类中查找
     *          如果找到直接返回字段
     *          如果在本类中没有找到 , 就去遍历它的父类 , 尝试在父类中查找该字段
     *              如果有父类 , 则在父类中查找
     *                  如果在父类中找到 , 返回该字段
     *                  如果在父类中没有找到 , 则返回空
     *              如果没有父类 , 返回空
     *
     * 尽量传具体的正确的类 , 不要传子类
     * @param name
     * @param args
     * @return
     */
    private Method findMethod(String name, Class<?>... args) {
        try {
            // 首先在本类中查找 , 如果找到直接返回方法
            return mClass.getDeclaredMethod(name, args);
        } catch (NoSuchMethodException e) {
            // 如果在本类中没有找到 , 就去遍历它的父类 , 尝试在父类中查找该方法
            for (Class<?> cls = mClass; cls != null; cls = cls.getSuperclass()) {
                try {
                    // 如果在父类中找到 , 返回该字段
                    return cls.getDeclaredMethod(name);
                } catch (NoSuchMethodException ex) {
                    // 如果在父类中没有找到 , 则返回空
                    return null;
                }
            }
            // 如果没有父类, 则返回空
            return null;
        }
    }

    /**
     * 调用 mCaller 的 mMethod 方法
     *
     * @param args
     * @param <T>
     * @return
     */
    public <T> T call(Object... args) {
        try {
            return (T) mMethod.invoke(mCaller, args);
        } catch (IllegalAccessException e) {
            e.printStackTrace();
            return null;
        } catch (InvocationTargetException e) {
            e.printStackTrace();
            return null;
        }
    }
}

  
 
  • 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

3、创建 Resources


创建 Resources , 传入 AssetManager 对象作为主要参数 , 其它参数都可以从应用的 Resources 中获取 ;

        // 创建 Resources 并返回
        return mResources = new Resources(
                assetManager,
                mBase.getResources().getDisplayMetrics(),
                mBase.getResources().getConfiguration()

  
 
  • 1
  • 2
  • 3
  • 4
  • 5




二、自定义 Application 中设置插件资源



加载到插件资源后 , 重写 Application 的 public Resources getResources() 方法 , 返回插件包方法 ;

    @Override
    public Resources getResources() {
        if (pluginResources != null)
            return pluginResources;
        return super.getResources();
    }

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

完整代码示例 :

package com.example.plugin_hook;

import android.app.Application;
import android.content.res.Resources;
import android.util.Log;

import java.io.File;

import kim.hsl.plugin.PluginManager;

public class MyApplication extends Application {

    private static final String TAG = "plugin_MyApplication";

    /**
     * 插件资源
     */
    private Resources pluginResources;

    @Override
    public void onCreate() {
        super.onCreate();

        // 如果已经存在文件, 先删除 , 防止拷贝过程中出错
        File pluginFile = new File(getFilesDir() + "/plugin.apk");
        if (pluginFile.exists()){
            pluginFile.delete();
        }

        // 先将 assets 中的插件包拷贝到 内置存储中
        CommandUtils.copyAssets2File(
                this,
                "plugin.apk",
                getFilesDir() + "/plugin.apk");

        // 将文件从 assets/plugin.apk 拷贝到 /data/user/0/com.example.plugin_hook/files/plugin.apk
        Log.i(TAG, "将文件从 assets/plugin.apk 拷贝到 " + getFilesDir() + "/plugin.apk");


        // 初始化插件包
        PluginManager.getInstance(this).init();

        Log.i(TAG, "插件化 初始化完毕");

        // 设置插件包中的资源文件
        pluginResources = PluginManager.getInstance(this).getmResources();

    }

    @Override
    public Resources getResources() {
        if (pluginResources != null)
            return pluginResources;
        return super.getResources();
    }
}


  
 
  • 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




三、插件 Activity 组件中设置插件资源



在 Activity 中重写 public Resources getResources() 方法 , 核心是返回插件资源 ;

    @Override
    public Resources getResources() {
        if (getApplication() != null && getApplication().getResources() != null) {
            return getApplication().getResources();
        }
        return super.getResources();
    }

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

完整代码示例 :

package com.example.plugin;

import androidx.appcompat.app.AppCompatActivity;

import android.app.Activity;
import android.content.res.Resources;
import android.os.Bundle;
import android.util.Log;

public class MainActivity extends Activity {
    private static final String TAG = "plugin_MainActivity";

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        Log.i("plugin", "启动了插件 Activity");
    }

    public void log(){
        Log.i(TAG, "Plugin MainActivity");
    }

    @Override
    public Resources getResources() {
        if (getApplication() != null && getApplication().getResources() != null) {
            return getApplication().getResources();
        }
        return super.getResources();
    }
}

  
 
  • 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




四、博客资源



博客资源 :

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

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

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

评论(0

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

全部回复

上滑加载中

设置昵称

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

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

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