前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >【Android 插件化】Hook 插件化框架 ( 加载插件包资源 )

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

作者头像
韩曙亮
发布2023-03-29 15:24:36
3660
发布2023-03-29 15:24:36
举报
文章被收录于专栏:韩曙亮的移动开发专栏

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 实例对象 ;

代码语言:javascript
复制
        // 使用反射工具类进行链式调用 , 创建 AssetManager 对象
        AssetManager assetManager = Reflector.on(AssetManager.class).newInstance();

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

代码语言:javascript
复制
/**
 * 封装反射相关逻辑的工具类
 * 该封装类会维持链式调用
 */
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;
        }
    }
}

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

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

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

代码语言:javascript
复制
        // 获取插件包 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);

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

代码语言:javascript
复制
/**
 * 封装反射相关逻辑的工具类
 * 该封装类会维持链式调用
 */
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;
        }
    }
}

3、创建 Resources

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

代码语言:javascript
复制
        // 创建 Resources 并返回
        return mResources = new Resources(
                assetManager,
                mBase.getResources().getDisplayMetrics(),
                mBase.getResources().getConfiguration()

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


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

代码语言:javascript
复制
    @Override
    public Resources getResources() {
        if (pluginResources != null)
            return pluginResources;
        return super.getResources();
    }

完整代码示例 :

代码语言:javascript
复制
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();
    }
}

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


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

代码语言:javascript
复制
    @Override
    public Resources getResources() {
        if (getApplication() != null && getApplication().getResources() != null) {
            return getApplication().getResources();
        }
        return super.getResources();
    }

完整代码示例 :

代码语言:javascript
复制
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();
    }
}

四、博客资源

博客资源 :

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2021-08-11,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • Android 插件化系列文章目录
    • 文章目录
    • 前言
    • 一、加载插件包资源文件
      • 1、反射 AssetManager 类并创建实例对象
        • 2、反射 AssetManager 的 addAssetPath 方法并调用
          • 3、创建 Resources
          • 二、自定义 Application 中设置插件资源
          • 三、插件 Activity 组件中设置插件资源
          • 四、博客资源
          领券
          问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档