Android 插件化Hook 插件化框架总结 ( 插件包管理 | Hook Activity 启动流程 | Hook 插件包资源加载 ) ★★★
Posted 韩曙亮
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Android 插件化Hook 插件化框架总结 ( 插件包管理 | Hook Activity 启动流程 | Hook 插件包资源加载 ) ★★★相关的知识,希望对你有一定的参考价值。
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 点选择 | 资源冲突解决方案 )
【Android 插件化】Hook 插件化框架 ( 使用 Hook 方式替换插件 Activity 的 mResources 成员变量 )
【Android 插件化】Hook 插件化框架总结 ( 插件包管理 | Hook Activity 启动流程 | Hook 插件包资源加载 ) ★★★
文章目录
前言
本系列博客开发了一个简易 Hook 插件化框架 , 仅做学习使用 , 商业化还是使用大厂退出的成熟插件化框架 ;
源码在博客资源中 ;
一、项目结构及运行方法
1、项目结构
这是项目的结构图 ;
host 是宿主应用 Module ;
plugin 是插件应用 Module ;
lib_plugin_core 是插件化框架 , 是插件化依赖库 , 项目类型是 Android Library Module ;
2、项目运行
编译 plugin 插件应用 , 将编译后的 APK 安装包拷贝到宿主应用 host 的 " Plugin_Hook\\host\\src\\main\\assets " 目录下 ;
在 host 应用启动时 , 会将文件从 项目资源文件目录 " assets/plugin.apk " 拷贝到 " /data/user/0/com.example.plugin_hook/files/plugin.apk " Android 内置存储目录中 ;
运行时直接读取该内置文件中的插件包 , 加载 , 并显示插件包 APK 中的 Activity 界面 ;
GitHub 上的应用可以直接运行 , 我已经将 plugin 插件应用编译成 APK , 并拷贝到了 宿主应用的 assets 资源目录下 ;
注意拷贝后将 APK 插件包文件名修改为 plugin.apk ;
二、宿主应用
1、拷贝工具类
该工具类的作用是将 assets 资源文件拷贝到 Android 文件系统中 ;
package com.example.host;
import android.content.Context;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
public class CommandUtils {
/**
* 将 Assets 中的文件拷贝到应用内置存储区域
* @param context 上下文
* @param assetsFilePath Assets 中的文件路径
* @param appFilePath 应用内置存储
* @return
*/
public static boolean copyAssets2File(Context context, String assetsFilePath, String appFilePath) {
// 内置存储文件对象
File file = new File(appFilePath);
// 确保目录存在
File filesDirectory = file.getParentFile();
if (!filesDirectory.exists()){
filesDirectory.mkdirs();
}
// 拷贝文件
boolean ret = false;
InputStream is = null;
FileOutputStream fos = null;
try {
is = context.getAssets().open(assetsFilePath);
fos = new FileOutputStream(file);
byte[] buffer = new byte[2048];
int n;
while ((n = is.read(buffer)) != -1) {
fos.write(buffer, 0, n);
}
fos.flush();
ret = true;
} catch (IOException e) {
e.printStackTrace();
} finally {
if (null != is) {
try {
is.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if (null != fos) {
try {
fos.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
return ret;
}
public static String inputStream2String(InputStream inputStream) {
try {
BufferedReader r = new BufferedReader(new InputStreamReader(inputStream));
String str;
StringBuilder sb = new StringBuilder();
while ((str = r.readLine()) != null) {
sb.append(str);
}
return sb.toString();
} catch (IOException e) {
e.printStackTrace();
}
return null;
}
}
2、自定义 Application
主要用于初始化插件化框架 ;
package com.example.host;
import android.app.Application;
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";
/**
* 插件资源, 这种方式侵入代码 , 造成开发的差异性 , 建议使用 Hook 加载插件资源
*/
//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, "插件化 初始化完毕");
// 设置插件包中的资源文件, 这种方式侵入代码 , 造成开发的差异性 , 建议使用 Hook 加载插件资源
//pluginResources = PluginManager.getInstance(this).getmResources();
}
/*
// 这种方式侵入代码 , 造成开发的差异性 , 建议使用 Hook 加载插件资源
@Override
public Resources getResources() {
if (pluginResources != null)
return pluginResources;
return super.getResources();
}*/
}
3、宿主 Activity 界面
在该 Activity 界面中 , 主要用于跳转到插件 Activity 中 ;
package com.example.host;
import androidx.appcompat.app.AppCompatActivity;
import android.app.Activity;
import android.content.ComponentName;
import android.content.Intent;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
public class MainActivity extends Activity {
private static final String TAG = "MainActivity";
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Log.i(TAG, "宿主应用 MainActivity onCreate");
// 反射插件包中的 com.example.plugin.MainActivity
Class<?> clazz = null;
try {
clazz = Class.forName("com.example.plugin.MainActivity");
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
Method method = null;
try {
method = clazz.getDeclaredMethod("log");
} catch (NoSuchMethodException e) {
e.printStackTrace();
}
try {
// 执行 com.example.plugin.MainActivity 的 log 方法
method.invoke(clazz.newInstance());
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
} catch (InstantiationException e) {
e.printStackTrace();
}
// 设置按钮点击事件
findViewById(R.id.button).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
// 启动插件包中的 Activity
Intent pluginIntent = new Intent();
pluginIntent.setComponent(new ComponentName("com.example.plugin",
"com.example.plugin.MainActivity"));
pluginIntent.putExtra("isPlugin", true);
startActivity(pluginIntent);
}
});
}
}
三、插件化框架
1、反射工具类
使用反射工具类 , 能快速开发反射相关功能 ;
package kim.hsl.plugin;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
/**
* 封装反射相关逻辑的工具类
* 该封装类会维持链式调用
*/
public class Reflector {
/**
* 反射的类型
*/
private Class<?> mClass;
/**
* 反射针对的实例对象
* 如获取 Object 某个字段的值
*/
private Object mCaller;
/**
* 反射的字段
*/
private Field mField;
/**
* 反射的方法
*/
private Method mMethod;
/**
* 反射某个类的入口方法
*
* @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 object
* @return
*/
public Reflector with(Object object) {
mCaller = object;
return this;
}
/**
* 创建 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;
}
}
/**
* 反射类中的某个字段
*
* @param name 要反射的字段名称
* @return
*/
public Reflector field(String name) {
mField = findField(name);
mField.setAccessible(true);
return this;
}
/**
* 查找字段名称
* 首先在本类中查找
* 如果找到直接返回字段
* 如果在本类中没有找到 , 就去遍历它的父类 , 尝试在父类中查找该字段
* 如果有父类 , 则在父类中查找
* 如果在父类中找到 , 返回该字段
* 如果在父类中没有找到 , 则返回空
* 如果没有父类 , 返回空
*
* 尽量传具体的正确的类 , 不要传子类
* @param fieldName
* @return
*/
private Field findField(String fieldName) {
try {
// 首先在本类中查找 , 如果找到直接返回字段
return mClass.getDeclaredField(fieldName);
} catch (NoSuchFieldException e) {
// 如果在本类中没有找到 , 就去遍历它的父类 , 尝试在父类中查找该字段
for (Class<?> clazz = mClass; clazz != null; clazz = clazz.getSuperclass()) {
try {
// 如果在父类中找到 , 返回该字段
return clazz.getDeclaredField(fieldName);
} catch (NoSuchFieldException ex) {
// 如果在父类中没有找到 , 则返回空
return null;
}
}
// 如果没有父类, 则返回空
return null;
}
}
/**
* 获取 mCaller 对象中的 mField 属性值
*
* @return
*/
public Object get() {
try {
return mField.get(mCaller);
} catch (IllegalAccessException e) {
e.printStackTrace();
return null;
}
}
/**
* 设置 mCaller 对象中的 mField 属性值
*
* @param value
* @return 链式调用 , 返回 Reflector
*/
public Reflector set(Object value) {
try {
mField.set(mCaller, value);
} catch (IllegalAccessException e) {
e.printStackTrace();
}
return this;
}
/**
* 反射类中的某个方法
*
* @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);<以上是关于Android 插件化Hook 插件化框架总结 ( 插件包管理 | Hook Activity 启动流程 | Hook 插件包资源加载 ) ★★★的主要内容,如果未能解决你的问题,请参考以下文章
Android 插件化Hook 插件化框架 ( hook 插件化原理 | 插件包管理 )
Android 插件化Hook 插件化框架 ( Hook Activity 启动流程 | Hook 点分析 )
Android 插件化Hook 插件化框架 ( Hook Activity 启动过程 | 静态代理 )
Android 插件化Hook 插件化框架 ( Hook 技术 | 代理模式 | 静态代理 | 动态代理 )
Android 插件化Hook 插件化框架 ( Hook Activity 启动流程 | 反射获取 IActivityManager 对象 )
Android 插件化Hook 插件化框架 ( 使用 Hook 方式替换插件 Activity 的 mResources 成员变量 )