【Android 插件化】Hook 插件化框架 ( Hook Activity 启动过程 | 静态代理 )(三)

简介: 【Android 插件化】Hook 插件化框架 ( Hook Activity 启动过程 | 静态代理 )(三)

5、代理类开发


被代理的目标对象是 Activity 中的 Instrumentation mInstrumentation 成员变量 ;


代理类中需要持有上述成员变量 , 通过反射获取该成员 , 并设置给代理者 ;


在代理类中 , 继承 Instrumentation 类 , 这是因为还需要通过反射 , 将代理类设置给 Activity 的 Instrumentation mInstrumentation 成员 , 以达到偷梁换柱的目的 , 档 Activity 调用 Instrumentation mInstrumentation 成员时 , 其实调用的是开发者开发的代理类 ;


在 Android 界面跳转时 , 会自动回调 Activity 中的 Instrumentation mInstrumentation 成员的 execStartActivity 方法 ;


实际上调用的是代理类的 execStartActivity 方法 ;


在代理类 execStartActivity 方法中 , 首先调用持有的 Activity 中原本的 Instrumentation mInstrumentation 成员的 execStartActivity 方法 , 然后在该方法的前面 , 后面 , 可以添加自己的业务逻辑 , 该方法的执行参数也可以进行修改 ;


这样就成功将自己的业务逻辑注入到了 Activity 启动过程中 ;



代码示例 :


package com.example.plugin_hook;
import android.app.Activity;
import android.app.Instrumentation;
import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
import android.os.IBinder;
import android.util.Log;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
public class InstrumentationProxy extends Instrumentation {
    private static final String TAG = "InstrumentationProxy";
    /**
     * Activity 中原本的 Instrumentation mInstrumentation 成员
     * 从构造函数中进行初始化
     */
    final Instrumentation orginalInstrumentation;
    public InstrumentationProxy(Instrumentation orginalInstrumentation) {
        this.orginalInstrumentation = orginalInstrumentation;
    }
    public ActivityResult execStartActivity(
            Context who, IBinder contextThread, IBinder token, Activity target,
            Intent intent, int requestCode, Bundle options) {
        Log.i(TAG, "注入的 Hook 前执行的业务逻辑");
        // 1. 反射执行 Instrumentation orginalInstrumentation 成员的 execStartActivity 方法
        Method execStartActivity_Method = null;
        try {
            execStartActivity_Method = Instrumentation.class.getDeclaredMethod(
                    "execStartActivity",
                    Context.class,
                    IBinder.class,
                    IBinder.class,
                    Activity.class,
                    Intent.class,
                    int.class,
                    Bundle.class);
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        }
        // 2. 设置方法可访问性
        execStartActivity_Method.setAccessible(true);
        // 3. 执行 Instrumentation orginalInstrumentation 的 execStartActivity 方法
        //    使用 Object 类型对象接收反射方法执行结果
        ActivityResult activityResult = null;
        try {
            activityResult = (ActivityResult) execStartActivity_Method.invoke(orginalInstrumentation,
                    who,
                    contextThread,
                    token,
                    target,
                    intent,
                    requestCode,
                    options);
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        }
        Log.i(TAG, "注入的 Hook 后执行的业务逻辑");
        return activityResult;
    }
}






三、完整代码示例



1、主界面代码示例


主界面代码示例 :


package com.example.plugin_hook;
import androidx.appcompat.app.AppCompatActivity;
import android.app.Activity;
import android.app.Instrumentation;
import android.content.Intent;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
public class MainActivity extends AppCompatActivity {
    private static final String TAG = "MainActivity";
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        // 获取按钮 , 并未按钮组件设置点击事件
        Button button = findViewById(R.id.button);
        button.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Log.i(TAG, "Button OnClickListener onClick");
                startActivity(new Intent(MainActivity.this, MainActivity2.class));
            }
        });
        hookOnClick(button);
        hookStartActivity();
    }
    @Override
    public void startActivity(Intent intent) {
        super.startActivity(intent);
    }
    /**
     * hook Button 组件的 getListenerInfo 方法
     * @param view
     */
    private void hookOnClick(View view){
        // 获取 View 的 getListenerInfo 方法
        Method getListenerInfo = null;
        try {
            getListenerInfo = View.class.getDeclaredMethod("getListenerInfo");
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        }
        // 执行所有的反射方法 , 设置成员变量 之前 , 都要设置可见性
        getListenerInfo.setAccessible(true);
        // 执行 View view 对象的 getListenerInfo 方法
        Object mListenerInfo = null;
        try {
            mListenerInfo = getListenerInfo.invoke(view);
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        }
        // 反射获取 OnClickListener 成员
        // ① 先根据全类名获取 ListenerInfo 字节码
        Class<?> clazz = null;
        try {
            clazz = Class.forName("android.view.View$ListenerInfo");
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
        // ② 获取 android.view.View.ListenerInfo 中的 mOnClickListener 成员
        Field field = null;
        try {
            field = clazz.getField("mOnClickListener");
        } catch (NoSuchFieldException e) {
            e.printStackTrace();
        }
        // ③ 设置该字段访问性, 执行所有的反射方法 , 设置成员变量 之前 , 都要设置可见性
        field.setAccessible(true);
        // ④ 获取 mOnClickListener 成员变量
        View.OnClickListener mOnClickListener = null;
        try {
            mOnClickListener = (View.OnClickListener) field.get(mListenerInfo);
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        }
        // ⑤ 修改 View 的 ListenerInfo 成员的 mOnClickListener 成员
        // 其中 ListenerInfo 成员 是
        try {
            View.OnClickListener finalMOnClickListener = mOnClickListener;
            field.set(mListenerInfo, new View.OnClickListener(){
                @Override
                public void onClick(View v) {
                    Log.i(TAG, "Hook Before");
                    finalMOnClickListener.onClick(view);
                    Log.i(TAG, "Hook After");
                }
            });
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        }
    }
    /**
     * Hook Activity 界面启动过程
     * 钩住 Instrumentation 的 execStartActivity 方法
     * 向该方法中注入自定义的业务逻辑
     */
    private void hookStartActivity(){
        // 1. 获取 Activity 字节码文件
        //    字节码文件是所有反射操作的入口
        Class<?> clazz = Activity.class;
        // 2. 获取 Activity 的 Instrumentation mInstrumentation 成员 Field 字段
        Field mInstrumentation_Field = null;
        try {
            mInstrumentation_Field = clazz.getDeclaredField("mInstrumentation");
        } catch (NoSuchFieldException e) {
            e.printStackTrace();
        }
        // 3. 设置 Field mInstrumentation 字段的可访问性
        mInstrumentation_Field.setAccessible(true);
        // 4. 获取 Activity 的 Instrumentation mInstrumentation 成员对象值
        //      获取该成员的意义是 , 创建 Instrumentation 代理时, 需要将原始的 Instrumentation 传入代理对象中
        Instrumentation mInstrumentation = null;
        try {
            mInstrumentation = (Instrumentation) mInstrumentation_Field.get(this);
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        }
        // 5. 将 Activity 的 Instrumentation mInstrumentation 成员变量
        //      设置为自己定义的 Instrumentation 代理对象
        try {
            mInstrumentation_Field.set(this, new InstrumentationProxy(mInstrumentation));
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        }
    }
}



2、代理类代码示例


代理类代码示例 :


package com.example.plugin_hook;
import android.app.Activity;
import android.app.Instrumentation;
import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
import android.os.IBinder;
import android.util.Log;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
public class InstrumentationProxy extends Instrumentation {
    private static final String TAG = "InstrumentationProxy";
    /**
     * Activity 中原本的 Instrumentation mInstrumentation 成员
     * 从构造函数中进行初始化
     */
    final Instrumentation orginalInstrumentation;
    public InstrumentationProxy(Instrumentation orginalInstrumentation) {
        this.orginalInstrumentation = orginalInstrumentation;
    }
    public ActivityResult execStartActivity(
            Context who, IBinder contextThread, IBinder token, Activity target,
            Intent intent, int requestCode, Bundle options) {
        Log.i(TAG, "注入的 Hook 前执行的业务逻辑");
        // 1. 反射执行 Instrumentation orginalInstrumentation 成员的 execStartActivity 方法
        Method execStartActivity_Method = null;
        try {
            execStartActivity_Method = Instrumentation.class.getDeclaredMethod(
                    "execStartActivity",
                    Context.class,
                    IBinder.class,
                    IBinder.class,
                    Activity.class,
                    Intent.class,
                    int.class,
                    Bundle.class);
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        }
        // 2. 设置方法可访问性
        execStartActivity_Method.setAccessible(true);
        // 3. 执行 Instrumentation orginalInstrumentation 的 execStartActivity 方法
        //    使用 Object 类型对象接收反射方法执行结果
        ActivityResult activityResult = null;
        try {
            activityResult = (ActivityResult) execStartActivity_Method.invoke(orginalInstrumentation,
                    who,
                    contextThread,
                    token,
                    target,
                    intent,
                    requestCode,
                    options);
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        }
        Log.i(TAG, "注入的 Hook 后执行的业务逻辑");
        return activityResult;
    }
}


3、跳转的界面


跳转的界面 :


package com.example.plugin_hook;
import androidx.appcompat.app.AppCompatActivity;
import android.os.Bundle;
public class MainActivity2 extends AppCompatActivity {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main2);
    }
}


4、执行结果


按钮点击事件 , 在调用按钮事件的前后 , 注入的业务逻辑 , 分别打印 Hook Before 和 Hook After ;


Activity 启动过程 , Hook 住了 Activity 的 Instrumentation mInstrumentation 成员的 execStartActivity 方法 , 在调用该方法的 前后 , 各注入的业务逻辑中打印 注入的 Hook 前执行的业务逻辑 和 注入的 Hook 后执行的业务逻辑 ;


I/MainActivity: Hook Before
I/MainActivity: Button OnClickListener onClick
I/InstrumentationProxy: 注入的 Hook 前执行的业务逻辑
I/InstrumentationProxy: 注入的 Hook 后执行的业务逻辑
I/MainActivity: Hook After




目录
相关文章
|
1月前
|
算法 JavaScript Android开发
|
1月前
|
开发框架 Dart Android开发
安卓与iOS的跨平台开发:Flutter框架深度解析
在移动应用开发的海洋中,Flutter作为一艘灵活的帆船,正引领着开发者们驶向跨平台开发的新纪元。本文将揭开Flutter神秘的面纱,从其架构到核心特性,再到实际应用案例,我们将一同探索这个由谷歌打造的开源UI工具包如何让安卓与iOS应用开发变得更加高效而统一。你将看到,借助Flutter,打造精美、高性能的应用不再是难题,而是变成了一场创造性的旅程。
|
2月前
|
Java 程序员 API
Android|集成 slf4j + logback 作为日志框架
做个简单改造,统一 Android APP 和 Java 后端项目打印日志的体验。
138 1
|
3月前
|
前端开发 Java 数据库
💡Android开发者必看!掌握这5大框架,轻松打造爆款应用不是梦!🏆
在Android开发领域,框架犹如指路明灯,助力开发者加速应用开发并提升品质。本文将介绍五大必备框架:Retrofit简化网络请求,Room优化数据库访问,MVVM架构提高代码可维护性,Dagger 2管理依赖注入,Jetpack Compose革新UI开发。掌握这些框架,助你在竞争激烈的市场中脱颖而出,打造爆款应用。
427 3
|
3月前
|
编译器 Android开发 开发者
带你了解Android Jetpack库中的依赖注入框架:Hilt
本文介绍了Hilt,这是Google为Android开发的依赖注入框架,基于Dagger构建,旨在简化依赖注入过程。Hilt通过自动化的组件和注解减少了DI的样板代码,提高了应用的可测试性和可维护性。文章详细讲解了Hilt的主要概念、基本用法及原理,帮助开发者更好地理解和应用Hilt。
96 8
|
Java Android开发 容器
|
19天前
|
搜索推荐 前端开发 API
探索安卓开发中的自定义视图:打造个性化用户界面
在安卓应用开发的广阔天地中,自定义视图是一块神奇的画布,让开发者能够突破标准控件的限制,绘制出独一无二的用户界面。本文将带你走进自定义视图的世界,从基础概念到实战技巧,逐步揭示如何在安卓平台上创建和运用自定义视图来提升用户体验。无论你是初学者还是有一定经验的开发者,这篇文章都将为你打开新的视野,让你的应用在众多同质化产品中脱颖而出。
40 19
|
19天前
|
JSON Java API
探索安卓开发:打造你的首个天气应用
在这篇技术指南中,我们将一起潜入安卓开发的海洋,学习如何从零开始构建一个简单的天气应用。通过这个实践项目,你将掌握安卓开发的核心概念、界面设计、网络编程以及数据解析等技能。无论你是初学者还是有一定基础的开发者,这篇文章都将为你提供一个清晰的路线图和实用的代码示例,帮助你在安卓开发的道路上迈出坚实的一步。让我们一起开始这段旅程,打造属于你自己的第一个安卓应用吧!
44 14