【Android 插件化】Hook 插件化框架总结 ( 插件包管理 | Hook Activity 启动流程 | Hook 插件包资源加载 ) ★★★(五)

简介: 【Android 插件化】Hook 插件化框架总结 ( 插件包管理 | Hook Activity 启动流程 | Hook 插件包资源加载 ) ★★★(五)

5、Hook Handler 代理类


静态代理 ActivityThread 中的 final H mH = new H() 成员 ;


在 AMS 执行完毕后 , 主线程 ActivityThread 中创建 Activity 实例对象之间 , 再将插件包 Activity 替换回去 ;


package kim.hsl.plugin;
import android.content.Intent;
import android.os.Handler;
import android.os.Message;
import android.util.Log;
import java.lang.reflect.Field;
import java.util.List;
/**
 * 静态代理 ActivityThread 中的 final H mH = new H() 成员
 */
public class HandlerProxy implements Handler.Callback {
    public static final int EXECUTE_TRANSACTION = 159;
    @Override
    public boolean handleMessage(Message msg) {
        if (msg.what == EXECUTE_TRANSACTION) {
            // 反射 android.app.servertransaction.ClientTransaction 类
            // 该类中有如下成员变量
            // private List<ClientTransactionItem> mActivityCallbacks;
            // 这个集合中存放的就是 android.app.servertransaction.LaunchActivityItem 类实例
            // 不能直接获取 LaunchActivityItem 实例 , 否则会出错
            Class<?> clientTransactionClass = null;
            try {
                clientTransactionClass =
                        Class.forName("android.app.servertransaction.ClientTransaction");
            } catch (ClassNotFoundException e) {
                e.printStackTrace();
            }
            // 验证当前的 msg.obj 是否是 ClientTransaction 类型 , 如果不是则不进行 Intent 替换
            // 通过阅读源码可知 , 在 ActivityThread 的 mH 中, 处理 EXECUTE_TRANSACTION 信号时
            // 有 final ClientTransaction transaction = (ClientTransaction) msg.obj;
            if (!clientTransactionClass.isInstance(msg.obj)) {
                return true;
            }
            // 反射获取
            // private List<ClientTransactionItem> mActivityCallbacks; 成员字段
            Field mActivityCallbacksField = null;
            try {
                mActivityCallbacksField =
                        clientTransactionClass.getDeclaredField("mActivityCallbacks");
            } catch (NoSuchFieldException e) {
                e.printStackTrace();
            }
            // 设置成员字段可见性
            mActivityCallbacksField.setAccessible(true);
            // 反射获取
            // private List<ClientTransactionItem> mActivityCallbacks; 成员字段实例
            Object mActivityCallbacksObject = null;
            try {
                mActivityCallbacksObject = mActivityCallbacksField.get(msg.obj);
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            }
            // 将
            // private List<ClientTransactionItem> mActivityCallbacks; 成员字段实例
            // 强转为 List 类型 , 以用于遍历
            List mActivityCallbacksObjectList = (List) mActivityCallbacksObject;
            for (Object item : mActivityCallbacksObjectList) {
                Class<?> clazz = item.getClass();
                // 只处理 LaunchActivityItem 的情况
                if (clazz.getName().equals("android.app.servertransaction.LaunchActivityItem")) {
                    // 获取 LaunchActivityItem 的 private Intent mIntent; 字段
                    // 该 Intent 中的 Activity 目前是占坑 Activity 即 StubActivity
                    // 需要在实例化之前 , 替换成插件包中的 Activity
                    Field mIntentField = null;
                    try {
                        mIntentField = clazz.getDeclaredField("mIntent");
                    } catch (NoSuchFieldException e) {
                        e.printStackTrace();
                    }
                    mIntentField.setAccessible(true);
                    // 获取 LaunchActivityItem 对象的 mIntent 成员 , 即可得到 Activity 跳转的 Intent
                    Intent intent = null;
                    try {
                        intent = (Intent) mIntentField.get(item);
                    } catch (IllegalAccessException e) {
                        e.printStackTrace();
                    }
                    // 获取 启动 插件包 组件的 Intent
                    Intent pluginIntent = intent.getParcelableExtra("pluginIntent");
                    if (pluginIntent != null) {
                        // 使用 包含插件包组件信息的 Intent ,
                        // 替换之前在 Ams 启动之前设置的 占坑 StubActivity 对应的 Intent
                        try {
                            mIntentField.set(item, pluginIntent);
                        } catch (IllegalAccessException e) {
                            e.printStackTrace();
                        }
                    }
                }
            }
        }
        return false;
    }
}




6、Hook Instrumentation 代理类


通过 Hook 方式修改 Activity 中的 Resources 资源 ;


package kim.hsl.plugin;
import android.app.Activity;
import android.app.Application;
import android.app.Instrumentation;
import android.content.Context;
import android.content.Intent;
import android.content.pm.ActivityInfo;
import android.content.res.Resources;
import android.os.Bundle;
import android.os.IBinder;
import java.lang.reflect.Field;
public class InstrumentationProxy extends Instrumentation {
    private static final String TAG = "InstrumentationProxy";
    /**
     * 持有被代理对象
     * 有一些操作需要使用原来的 Instrumentation 进行操作
     */
    private final Instrumentation mBase;
    /**
     * 在构造方法中注入被代理对象
     * @param mBase
     */
    public InstrumentationProxy(Instrumentation mBase) {
        this.mBase = mBase;
    }
    public ActivityResult execStartActivity(
            Context who, IBinder contextThread, IBinder token, Activity target,
            Intent intent, int requestCode, Bundle options) {
        ActivityResult result = null;
        // 反射调用 Instrumentation mBase 成员的 execStartActivity 方法
        result = Reflector.on("android.app.Instrumentation")
                .method("execStartActivity",    // 反射的方法名
                        Context.class,                // 后续都是方法的参数类型
                        IBinder.class,
                        IBinder.class,
                        Activity.class,
                        Intent.class,
                        int.class,
                        Bundle.class)
                .with(mBase)
                .call(who,                             // 后续都是传入 execStartActivity 方法的参数
                        contextThread,
                        token,
                        target,
                        intent,
                        requestCode,
                        options);
        return result;
    }
    /**
     * 在该方法中 , 可以拿到 Activity , 通过反射修改 Activity 中的 Resources 成员变量
     * @param cl
     * @param className
     * @param intent
     * @return
     * @throws ClassNotFoundException
     * @throws IllegalAccessException
     * @throws InstantiationException
     */
    public Activity newActivity(ClassLoader cl, String className, Intent intent) throws ClassNotFoundException, IllegalAccessException, InstantiationException {
        Activity activity = mBase.newActivity(cl, className, intent);
        // 替换 Activity 中的 Resources
        exchangeResourcesOfActivity(activity, intent);
        return activity;
    }
    /**
     * 在该方法中 , 可以拿到 Activity , 通过反射修改 Activity 中的 Resources 成员变量
     * @param clazz
     * @param context
     * @param token
     * @param application
     * @param intent
     * @param info
     * @param title
     * @param parent
     * @param id
     * @param lastNonConfigurationInstance
     * @return
     * @throws IllegalAccessException
     * @throws InstantiationException
     */
    @Override
    public Activity newActivity(Class<?> clazz, Context context, IBinder token, Application application, Intent intent, ActivityInfo info, CharSequence title, Activity parent, String id, Object lastNonConfigurationInstance) throws IllegalAccessException, InstantiationException {
        Activity activity = mBase.newActivity(clazz, context, token, application, intent, info, title, parent, id, lastNonConfigurationInstance);
        // 替换 Activity 中的 Resources
        exchangeResourcesOfActivity(activity, intent);
        return activity;
    }
    /**
     * 反射 Activity , 并设置 Activity 中 Resources 成员变量
     * @param activity
     * @param intent
     */
    private void exchangeResourcesOfActivity(Activity activity, Intent intent) {
        // 这里注意 : 所有的 Activity 创建 , 都会过这个方法 , 这里只将插件包中的 Activity 的资源替换
        // 这里要做一个判断
        //      不能修改宿主应用的资源
        //      只有插件包中的 Activity 才进行相应的修改
        // 在调用插件包中的组件时 , 在 Intent 中传入一个 isPlugin 变量 ,
        //      也可以传入插件的标志位 , 区分不同的插件包
        //      这里只有一个插件包 , 只设置一个 Boolean 变量即可
        if (!intent.getBooleanExtra("isPlugin", false)) return;
        // 获取插件资源
        Resources pluginResources = PluginManager.getInstance(activity).getResources();
        // 反射 ContextThemeWrapper 类 , Activity 是 ContextThemeWrapper 的子类
        // Resources mResources 成员定义在 ContextThemeWrapper 中
        Class<?> contextThemeWrapperClass = null;
        try {
            contextThemeWrapperClass = Class.forName("android.view.ContextThemeWrapper");
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
        // 反射获取 ContextThemeWrapper 类的 mResources 字段
        Field mResourcesField = null;
        try {
            mResourcesField = contextThemeWrapperClass.getDeclaredField("mResources");
        } catch (NoSuchFieldException e) {
            e.printStackTrace();
        }
        // 设置字段可见性
        mResourcesField.setAccessible(true);
        // 将插件资源设置到插件 Activity 中
        try {
            mResourcesField.set(activity, PluginManager.getInstance(activity).getResources());
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        }
    }
}




7、占坑 Activity


一个普通的 Activity , 在清单文件中正常注册 , 在 Hook Activity 启动过程中 , 起到占坑作用 ;


package kim.hsl.plugin;
import androidx.appcompat.app.AppCompatActivity;
import android.app.Activity;
import android.os.Bundle;
import android.util.Log;
/**
 * 该 Activity 主要用于占位
 * 实际上使用插件包中的 Activity 替换该 Activity
 */
public class StubActivity extends AppCompatActivity {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_stub);
        Log.i("plugin", "启动了占坑 Activity");
    }
}






四、插件应用


插件应用是普通的应用 , 与正常应用没有区别 , 不用作特别的操作 , 这也是 Hook 插件化框架的优点 , 对代码的侵入性很小 , 开发者可以按照正常的开发逻辑 , 开发插件应用 ;


image.png


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");
    }
    /*
    // 这种方式侵入代码 , 造成开发的差异性 , 建议使用 Hook 加载插件资源
    @Override
    public Resources getResources() {
        if (getApplication() != null && getApplication().getResources() != null) {
            return getApplication().getResources();
        }
        return super.getResources();
    }*/
}



目录
相关文章
|
3月前
|
物联网 区块链 vr&ar
未来已来:探索区块链、物联网与虚拟现实技术的融合与应用安卓与iOS开发中的跨平台框架选择
【8月更文挑战第30天】在科技的巨轮下,新技术不断涌现,引领着社会进步。本文将聚焦于当前最前沿的技术——区块链、物联网和虚拟现实,探讨它们各自的发展趋势及其在未来可能的应用场景。我们将从这些技术的基本定义出发,逐步深入到它们的相互作用和集成应用,最后展望它们如何共同塑造一个全新的数字生态系统。
|
13天前
|
算法 JavaScript Android开发
|
23天前
|
Java 程序员 API
Android|集成 slf4j + logback 作为日志框架
做个简单改造,统一 Android APP 和 Java 后端项目打印日志的体验。
89 1
|
2月前
|
前端开发 Java 数据库
💡Android开发者必看!掌握这5大框架,轻松打造爆款应用不是梦!🏆
在Android开发领域,框架犹如指路明灯,助力开发者加速应用开发并提升品质。本文将介绍五大必备框架:Retrofit简化网络请求,Room优化数据库访问,MVVM架构提高代码可维护性,Dagger 2管理依赖注入,Jetpack Compose革新UI开发。掌握这些框架,助你在竞争激烈的市场中脱颖而出,打造爆款应用。
346 3
|
2月前
|
编译器 Android开发 开发者
带你了解Android Jetpack库中的依赖注入框架:Hilt
本文介绍了Hilt,这是Google为Android开发的依赖注入框架,基于Dagger构建,旨在简化依赖注入过程。Hilt通过自动化的组件和注解减少了DI的样板代码,提高了应用的可测试性和可维护性。文章详细讲解了Hilt的主要概念、基本用法及原理,帮助开发者更好地理解和应用Hilt。
77 8
|
Java Android开发 容器
|
5天前
|
搜索推荐 Android开发 开发者
探索安卓开发中的自定义视图:打造个性化UI组件
【10月更文挑战第39天】在安卓开发的世界中,自定义视图是实现独特界面设计的关键。本文将引导你理解自定义视图的概念、创建流程,以及如何通过它们增强应用的用户体验。我们将从基础出发,逐步深入,最终让你能够自信地设计和实现专属的UI组件。
|
7天前
|
Android开发 Swift iOS开发
探索安卓与iOS开发的差异和挑战
【10月更文挑战第37天】在移动应用开发的广阔舞台上,安卓和iOS这两大操作系统扮演着主角。它们各自拥有独特的特性、优势以及面临的开发挑战。本文将深入探讨这两个平台在开发过程中的主要差异,从编程语言到用户界面设计,再到市场分布的不同影响,旨在为开发者提供一个全面的视角,帮助他们更好地理解并应对在不同平台上进行应用开发时可能遇到的难题和机遇。