【Android 插件化】Hook 插件化框架 ( Hook 技术 | 代理模式 | 静态代理 | 动态代理 )

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

Android 插件化系列文章目录

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

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

【Android 插件化】Hook 插件化框架 ( Hook 技术 | 代理模式 | 静态代理 | 动态代理 )



前言

在之前的系列博客中 , 介绍了 " 插桩式 " 插件化框架 , 该框架存在一些问题 :

开发需要定制 : " 插件 " 模块中的 Activity 必须集成 BaseActivity , 其中很多操作都需要针对该方式进行 定制化开发 , 与开发普通的应用完全不同 ;

没有真正的上下文环境 : " 插件 " 模块内部 , 调用 Activity 组件的 getApplicationContext 方法会出现问题 , 因为 插件内部没有真正的供应用运行的上下文环境 ;

( 之前的 " 插桩式 " 插件化框架 , 只是简单示例 , 远远达不到能在项目中使用的复杂程度 )


插件化框架 的最终目的是让 " 插件 " 模块的开发和使用 , 与正常的应用开发和使用达到完全一致的效果 , " 宿主 " 模块 与 " 插件 " 模块 之间可以 无障碍通信 ;






一、Hook 技术简介



Hook 技术 又称为 钩子技术 , 同样 Hook 函数 也称为 钩子函数 ; 钩子技术 在 系统入侵 中 , 广泛使用 ;

Hook 技术 没有硬性规定技术标准 , 只是一种 技术概念 ; 在某一段代码的运行流程中 , 挂入自定义的钩子 , 在钩子的 前面 , 后面 , 可以 插入任意自定义的操作代码 , 达到 业务注入 的目的 ;


Hook 技术可以理解为 面向切面编程思想 , 想办法在不修改源码的前提下 , 在某个方法调用之前 , 插入自己的代码 , 业务逻辑 ,


Android 中的 Hook 技术 : 通过分析 Android 系统源码执行 , 通过 动态注入技术 , 在代码运行的某个阶段 , 注入开发者自定义的代码 ;


常用的动态注入技术 :

① 编译时修改字节码数据 : 代码编译时修改 Class 字节码数据 , 如 Dagger ;

② 运行时修改字节码数据 : 运行时可以修改字节码文件数据 , 达到代码入侵的效果 ;


Android 中的 Hook 机制 , 主要涉及到下面两种技术 :

① 反射机制 : Java 反射机制 ;

② 代理机制 : 动态代理 , 静态代理 ;





二、代理机制



代理机制 :

存在一个 目标对象 Subject代理者 Proxy ;

目标对象 Subject 执行一些业务逻辑 , 代理者 Proxy 持有 目标对象 Subject , 当 目标对象 Subject 要执行某个方法时 , 通过 代理者 Proxy 调用 目标对象 Subject 中的方法执行 ;

代理者 Proxy 调用 目标对象 Subject 方法 之前 , 之后 , 可以插入自己的业务逻辑 ;


下面简要介绍 静态代理 与 动态代理 ;


1、静态代理示例


定义代理方法接口 : 代理者 和 目标对象 都要实现该接口 , 代理者 和 目标对象 可以进行相互替换 ;

/**
 * 代理者 和 目标对象 都要实现该接口
 * 代理者 可以替换 目标对象
 */
public interface AInterface {
    void request();
}

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

目标对象 : 被代理的目标对象 , 实现了

/**
 * 被代理的目标对象
 * 目标对象 Subject 执行一些业务逻辑
 * 代理者 Proxy 持有 目标对象 Subject
 * 当目标对象 Subject 要执行某个方法时
 * 通过 代理者 Proxy 调用 目标对象 Subject 中的方法执行
 */
public class Subject implements AInterface {

    /**
     * 目标对象的业务逻辑
     */
    @Override
    public void request() {
        System.out.println("Subject request");
    }
}

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

代理者 :

/**
 * 代理者
 * 目标对象 Subject 执行一些业务逻辑
 * 代理者 Proxy 持有 目标对象 Subject
 * 当目标对象 Subject 要执行某个方法时
 * 通过 代理者 Proxy 调用 目标对象 Subject 中的方法执行
 */
public class Proxy implements AInterface {
    /**
     * 代理者 持有的 目标对象
     */
    private Subject subject;

    public Proxy(Subject subject) {
        this.subject = subject;
    }

    /**
     * 当 Subject 需要执行 request 方法时 , 自己不直接执行
     * 而是通过 Proxy 的该方法调用 持有的 目标对象 Subject 来执行
     */
    @Override
    public void request() {
        before();
        subject.request();
        after();
    }

    /**
     * 执行 Subject 目标对象的 request 方法前执行的业务逻辑
     */
    private void before() {
        System.out.println("Proxy before");
    }

    /**
     * 执行 Subject 目标对象的 request 方法后执行的业务逻辑
     */
    private void after() {
        System.out.println("Proxy after");
    }
}

  
 
  • 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

main 函数调用 : 通过代理者调用目标对象中的类 , 并在执行目标对象 Subject 的 request 方法时 , 对该方法进行逻辑增强 ;

① 方式一 :

public class Main {
    public static void main(String[] args) {
        // 1. 创建目标对象
        Subject subject = new Subject();

        // 2. 创建代理类
        Proxy proxy = new Proxy(subject);

        // 3. 通过代理类调用目标对象的方法
        proxy.request();

        /*
            代理类的作用 :
              执行 目标对象 Subject 的 request 方法时 ,
              对该方法进行逻辑增强 ;
         */
    }
}

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

② 方式二 :

public class Main {
    public static void main(String[] args) {
        /*
            下面的这种用法, 不需要关注目标对象
            只需要了解 Proxy 代理者
            调用者不了解目标对象的内部实现细节
            目标对象也不了解调用者
         */
        AInterface aInterface = new Proxy(new Subject());
        aInterface.request();
    }
}

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

执行结果 :

Proxy before
Subject request
Proxy after

  
 
  • 1
  • 2
  • 3

2、动态代理示例


动态代理接口 :

/**
 * 代理者 需要实现的接口
 * 该接口就是动态代理接口
 */
public interface AInterface {
    void request();
}

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

目标对象 : 被代理的目标对象 , 需要实现代理接口 ;

public class Subject implements AInterface {
    @Override
    public void request() {
        System.out.println("Subject request");
    }
}

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

InvocationHandler 实现 : 这是 Hook 钩子 , 用于向被代理的目标对象的目标方法中注入业务逻辑 ;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;

public class AInvocationHandler implements InvocationHandler {

    /**
     * 被代理的对象
     */
    Object target;

    public AInvocationHandler(Object target) {
        this.target = target;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        before();
        Object object = method.invoke(target, args);
        after();
        return object;
    }

    /**
     * 被代理对象方法调用之前执行
     */
    private void before(){
        System.out.println("AInvocationHandler before");
    }

    /**
     * 被代理对象方法调用之后执行
     */
    private void after(){
        System.out.println("AInvocationHandler after");
    }
}

  
 
  • 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

动态代理执行 main 函数 :

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Proxy;

public class Main {

    public static void main(String[] args) {
        // 1. 创建目标对象
        Subject subject = new Subject();

        // 2. 获取目标对象类加载器
        ClassLoader classLoader = subject.getClass().getClassLoader();

        // 3. 获取接口 Class 数组, Subject 只实现了一个 AInterface 接口
        Class<?>[] interfaces = subject.getClass().getInterfaces();

        // 4. 创建 InvocationHandler , 传入被代理的目标对象 , 处理该目标对象中被代理的函数
        InvocationHandler invocationHandler = new AInvocationHandler(subject);

        // 5. 动态代理 :
        //    ① jdk 根据传入的参数信息 , 在内存中动态的创建 与 .class 字节码文件等同的字节码数据
        //    ② 将字节码数据 转为 对应的 字节码类型
        //    ③ 使用反射调用 newInstance 创建动态代理实例
        AInterface proxy = (AInterface) Proxy.newProxyInstance(
                classLoader,
                interfaces,
                invocationHandler);
        // 正式调用被动态代理的类
        proxy.request();
    }
}

  
 
  • 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

执行结果 :

AInvocationHandler before
Subject request
AInvocationHandler after

  
 
  • 1
  • 2
  • 3




三、博客资源



博客资源 :

  • GitHub :

在这里插入图片描述

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

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

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

评论(0

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

全部回复

上滑加载中

设置昵称

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

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

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