Android hook式插件化详解

news2024/11/20 16:25:47

引言

Android插件化是一种将应用程序的功能模块化为独立的插件,并动态加载到主应用程序中的技术。通过插件化,开发者可以将应用程序的功能分解成独立的模块,每个模块可以作为一个插件单独开发、测试和维护,然后通过动态加载的方式集成到主应用程序中,实现功能的动态扩展和更新。

Android插件化通常涉及到动态加载、组件化、插件生命周期管理、插件间通信等技术,开发者需要使用相关的框架和工具来实现插件化功能。插件化可以帮助开发者更好地管理应用程序的复杂性,提高开发效率,同时也能够实现应用程序的功能动态更新和扩展,为用户提供更好的体验。

1.startActivity源码解析

我们上篇提到,调用 startActivity 启动一个没有在 Manifest.xml 文件中注册的 Activity 会报异常,那么我们今天从 startActivity 开始分析为什么会报异常。

startActivity 的流程大概是上面所示,最后执行到 AMS.startActivity 后如果没有在 Manifest.xml 中注册,软件就会发生崩溃, 那么怎么样才能避免这样呢。其实非常简单,我们只要把我们未注册的 Activity 替换成我们已经在 Manifest.xml 注册不就行了吗。那么问题又来了,我们应该如何实现替换操作呢?说到替换,我们不得不借助动态代理的方式来实现了。

从上图可以看出,创建一个动态代理对象需要传递三个参数,下面介绍一下这些参数

ClassLoader loader: 类加载器

Class<?>[] interfaces: 需要监听的接口

InvocationHandler h: 监听的回调

说到这里,我们又延伸到另一个问题,我们应该监听哪一个接口呢?,其实从上面我们已经分析过了,我们应该要监听 AMS.startActivity 这一步,所以我们要监听的接口就是 IActivityTaskManager(9.0及以下是IActivityManager) 这个接口。接下来我们就对这个接口实现动态代理。

2.动态代理的实现

第一步:

我们可以通过 Class.forName() 得到 IActivityTaskManager 或者 IActivityManager 这个类,如下面代码所示:

Class<?> mIActivityManagerClass;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
    mIActivityManagerClass = Class.forName("android.app.IActivityTaskManager");
} else {
    mIActivityManagerClass = Class.forName("android.app.IActivityManager");
}

有了类之后,我们可以创建动态代理对象了,但是 IActivityTaskManager 或者 IActivityManager 接口的方法那么多,我们没必要全部监听,我们只需要监听我们关注的 "startActivity" 这个方法就好了,如下代码所示:

//创建动态代理
Object mActivityManagerProxy = Proxy.newProxyInstance(
    getClassLoader(),//类加载器
    new Class[]{mIActivityManagerClass},//要监听的回调接口
    new InvocationHandler() {//回调的监听
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {

        if ("startActivity".equals(method.getName())) {
            //做自己的业务逻辑
            //换成可以通过AMS检测的Activity
           
        }
        //为了程序能够正确执行,我们需要系统的IActivityManager实例
        return method.invoke(需要系统的IActivityManager实例, args);
    }
);

从上述代码中可以看出,要使动态代理生效,我们还需要一个 IActivityManager,查阅源码 ActivityTaskManager(8.0~9.0以下看 ActivityManager,8.0以下看 ActivityManagerNative) 可发现:

ActivityTaskManager.java:(Androdid10.0)

/** @hide */
public static IActivityTaskManager getService() {
    return IActivityTaskManagerSingleton.get();
}

ActivityManager.java:(Android 8.0 ~ Android9.0)

/**
* @hide
*/
public static IActivityManager getService() {
    return IActivityManagerSingleton.get();
}

ActivityManagerNative.java:(Android 7.0及以下)

/**
* Retrieve the system's default/global activity manager.
*/
static public IActivityManager getDefault() {
    return gDefault.get();
}

通过 getService() 或者 getDefault() 可以返回一个我们需要的对象实例,我们接下来可以反射来执行该方法来获取 IActivityTaskManager 或者 IActivityManager 实例对象。代码如下:

//获取 ActivityManager 或 ActivityManagerNative 或 ActivityTaskManager
Class<?> mActivityManagerClass;
Method getActivityManagerMethod;
if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.N_MR1) {
    mActivityManagerClass = Class.forName("android.app.ActivityManagerNative");
    getActivityManagerMethod = mActivityManagerClass.getDeclaredMethod("getDefault");
} else if (Build.VERSION.SDK_INT > Build.VERSION_CODES.N_MR1 && Build.VERSION.SDK_INT < Build.VERSION_CODES.Q){
    mActivityManagerClass = Class.forName("android.app.ActivityManager");
    getActivityManagerMethod = mActivityManagerClass.getDeclaredMethod("getService");
} else {
    mActivityManagerClass = Class.forName("android.app.ActivityTaskManager");
    getActivityManagerMethod = mActivityManagerClass.getDeclaredMethod("getService");
}
getActivityManagerMethod.setAccessible(true);
//这个实例本质是 IActivityManager或者IActivityTaskManager
final Object IActivityManager = getActivityManagerMethod.invoke(null);

现在有了 IActivityTaskManager 或者 IActivityManager 实例对象我们就可以让程序继续能够执行下去了。

第二步: 既然我们自己创建了 IActivityTaskManager 或者 IActivityManager 的动态代理,我们就要把原来系统的 IActivityTaskManager 或者 IActivityManager 实例对象给替换掉。还是通过上面的 getService() 或者 getDefault() 方法入手,我们继续跟踪代码发现: ActivityManager 或 ActivityManagerNative 或 ActivityTaskManager 都有一个 Singleton 共同的属性,我们查看一下这个类的源码:

public abstract class Singleton<T> {
    private T mInstance;

    protected abstract T create();

    public final T get() {
        synchronized (this) {
            if (mInstance == null) {
                mInstance = create();
            }
            return mInstance;
        }
    }
}

里面的 mInstance 属性正好是 IActivityTaskManager 或者 IActivityManager 实例,所以我们直接替换掉 mInstance 值就可以了。代码如下:

//获取 IActivityTaskManagerSingleton 或者 IActivityManagerSingleton 或者 gDefault 属性
Field mSingletonField;
if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.N_MR1) {
    mSingletonField = mActivityManagerClass.getDeclaredField("gDefault");
} else if (Build.VERSION.SDK_INT > Build.VERSION_CODES.N_MR1 && Build.VERSION.SDK_INT < Build.VERSION_CODES.Q){
    mSingletonField = mActivityManagerClass.getDeclaredField("IActivityManagerSingleton");
} else {
    mSingletonField = mActivityManagerClass.getDeclaredField("IActivityTaskManagerSingleton");
}
mSingletonField.setAccessible(true);
Object mSingleton = mSingletonField.get(null);

//替换点
Class<?> mSingletonClass = Class.forName("android.util.Singleton");
Field mInstanceField = mSingletonClass.getDeclaredField("mInstance");
mInstanceField.setAccessible(true);
//将我们创建的动态代理设置到 mInstance 属性当中
mInstanceField.set(mSingleton, mActivityManagerProxy);

到这里我们的动态代理算是实现好了,完整的代码如下:

private void hookAMSAction() throws Exception {
        //动态代理
        Class<?> mIActivityManagerClass;
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
            mIActivityManagerClass = Class.forName("android.app.IActivityTaskManager");
        } else {
            mIActivityManagerClass = Class.forName("android.app.IActivityManager");
        }
        //获取 ActivityManager 或 ActivityManagerNative 或 ActivityTaskManager
        Class<?> mActivityManagerClass;
        Method getActivityManagerMethod;
        if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.N_MR1) {
            mActivityManagerClass = Class.forName("android.app.ActivityManagerNative");
            getActivityManagerMethod = mActivityManagerClass.getDeclaredMethod("getDefault");
        } else if (Build.VERSION.SDK_INT > Build.VERSION_CODES.N_MR1 && Build.VERSION.SDK_INT < Build.VERSION_CODES.Q){
            mActivityManagerClass = Class.forName("android.app.ActivityManager");
            getActivityManagerMethod = mActivityManagerClass.getDeclaredMethod("getService");
        } else {
            mActivityManagerClass = Class.forName("android.app.ActivityTaskManager");
            getActivityManagerMethod = mActivityMa

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1294152.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

骨传导耳机是怎么传声的?骨传导和入耳式哪个危害大一点?

先说结论&#xff0c;骨传导耳机通过人体骨骼来传递声音&#xff0c;骨传导和入耳式耳机&#xff0c;入耳式耳机的危害要大一些&#xff01; 一、骨传导耳机传声原理 骨传导耳机利用骨传导技术&#xff0c;通过将声音转化为机械振动信号&#xff0c;然后通过头骨、颌骨等头骨…

第二十一章——网络通信总结

网络程序设计基础 局域网与互联网 为了实现两台计算机的通信&#xff0c;必须用一个网络线路连接两台计算机。如下图所示 网络协议 1.IP协议 IP是Internet Protocol的简称&#xff0c;是一种网络协议。Internet 网络采用的协议是TCP/IP协议&#xff0c;其全称是Transmission…

基于Java SSM框架实现二手车交易网站系统项目【项目源码+论文说明】

基于java的SSM框架实现二手车交易网站系统演示 摘要 二手车交易网站采用B/S模式&#xff0c;促进了二手车交易网站的安全、质量、快捷的发展。传统的管理模式还处于手工处理阶段&#xff0c;管理效率极低&#xff0c;随着用户的不断增多&#xff0c;传统基于手工管理模式已经无…

ECharts的颜色渐变

目录 一、直接配置参数实现颜色渐变 二、使用ECharts自带的方法实现颜色渐变 一、两种渐变的实现方法 1、直接配置参数实现颜色渐变 横向的渐变&#xff1a; //主要代码 option {xAxis: {type: category,boundaryGap: false,data: [Mon, Tue, Wed, Thu, Fri, Sat, Sun]},yA…

国产化软件突围!怿星科技eStation产品荣获2023铃轩奖“前瞻优秀奖”

11月11日&#xff0c;2023中国汽车供应链峰会暨第八届铃轩奖颁奖典礼在江苏省昆山市举行。怿星科技凭借eStation产品&#xff0c;荣获2023铃轩奖“前瞻智能座舱类优秀奖”&#xff0c;怿星CEO潘凯受邀出席铃轩奖晚会并代表领奖。 2023铃轩奖“前瞻智能座舱类优秀奖” 铃轩奖&a…

倚天屠龙:Github Copilot vs Cursor

武林至尊&#xff0c;宝刀屠龙。号令天下&#xff0c;莫敢不从。倚天不出&#xff0c;谁与争锋&#xff01; 作为开发人员吃饭的家伙&#xff0c;一款好的开发工具对开发人员的帮助是无法估量的。还记得在学校读书的时候&#xff0c;当时流行CS架构的RAD&#xff0c;Delphi和V…

CDN是什么?对网站的作用大吗?

CDN即内容分发网络。CDN是构建在现有网络基础之上的智能虚拟网络&#xff0c;依靠部署在各地的边缘服务器&#xff0c;通过中心平台的负载均衡、内容分发、调度等功能模块&#xff0c;使用户就近获取所需内容&#xff0c;降低网络拥塞&#xff0c;提高用户访问响应速度和命中率…

带你解锁Python操作文件的姿势

什么是文件 文件是计算机中用于存储数据的一种数据结构。它可以是文本文件、图像文件、音频文件、视频文件等等。文件由一系列字节组成&#xff0c;每个字节都有一个唯一的地址。文件可以在计算机的硬盘、固态硬盘、光盘等存储介质上存储&#xff0c;并且可以在需要时被读取和…

2024 年甘肃省职业院校技能大赛信息安全管理与评估赛项规程

2024 年甘肃省职业院校技能大赛高职学生组电子与信息大类信息安全管理与评估赛项规程 一、赛项名称 赛项名称&#xff1a;信息安全管理与评估 赛项类别&#xff1a;团体赛 赛项归属&#xff1a;电子与信息大类 二、竞赛目的 极安云科专注技能竞赛&#xff0c;包含网络建设…

CentOS系统中设置反向代理服务器的步骤

在CentOS系统中设置反向代理服务器可以帮助你隐藏原始服务器的细节&#xff0c;并提高服务器的安全性。以下是在CentOS系统中设置反向代理服务器的步骤概述&#xff1a; 安装反向代理软件&#xff1a; 常见的反向代理软件包括Nginx和Apache。你可以选择其中之一来作为你的反向…

Java的Font类createFont方法创建 +~JF 临时文件

一、问题背景 有一个创建图片的项目&#xff0c;每个图片都包含很多的文字&#xff0c;项目中需要生成海量的这类图片。在windows电脑上运行程序发现C盘的存储空间不断下降&#xff0c;直至为0。 二、问题定位 1、定位磁盘的问题文件 当C盘存储空间为0时&#xff0c;使用《全…

针对Google 的 fuzzer-test-suite 的 pcre2-10.00 编译失败的解决方法

针对Google 的 fuzzer-test-suite 的 pcre2-10.00 编译失败的解决方法 查看文件 fuzzer-test-suite/pcre2-10.00/build.sh&#xff0c;文件内容如下&#xff1a; get_svn_revision svn://vcs.exim.org/pcre2/code/trunk 183 SRCbuild_lib build_fuzzer我们获悉&#xff0c;由…

OpenCvSharp从入门到实践-(07)绘制图形

目录 1、线段的绘制 1.1实例1-绘制线段拼成一个"王"字 2、矩形的绘制 2.1实例2-绘制一个矩形边框 2.2实例3-绘制一个实心矩形 3、圆的绘制 3.1实例4-绘制"交通灯" 4、多边形绘制 4.1实例5-绘制等腰梯形 5、文字的绘制 5.1实例6-绘制文字OpenCvS…

直流负载箱的技术发展趋势和创新有哪些?

直流负载箱广泛应用于电子、通信、航空航天等领域&#xff0c;随着科技的不断发展&#xff0c;直流负载箱也在不断创新和改进&#xff0c;直流负载箱在负载电流和电压的测量方面要求高精度和高稳定性。未来的发展趋势是提高负载箱的测量精度和稳定性&#xff0c;以满足更高要求…

2023年12月7日:QT实现登陆界面

#include "mywidget.h"MyWidget::MyWidget(QWidget *parent): QWidget(parent) {//窗口设置this->resize(600,500);//重新设置窗口大小this->setWindowTitle("QQ-盗版");//设置窗口名为QQ-盗版this->setWindowIcon(QIcon("D:\\Qt\\funny\\pi…

gma 空间绘图实战(1):绘制多个子图,连接并展示局部放大区域

安装 gma&#xff1a;pip install gma 本文基于&#xff1a;gma 2.0.3&#xff0c;Python 3.10 本文用到的矢量数据为&#xff1a;CTAmap 1.12。来源于 https://www.shengshixian.com/ 。&#xff08;感谢锐多宝&#xff09; 绘图目标 参考代码 import matplotlib.pyplot as p…

网站建设app开发小程序制作|企业软件定制

网站建设app开发小程序制作|企业软件定制 网站建设和软件开发是现代社会非常重要的领域&#xff0c;它们对于企业、机构和个人来说都具有非常大的意义。随着移动互联网的快速发展&#xff0c;小程序制作也逐渐成为一种非常受欢迎的方式。 在过去&#xff0c;建立一个网站需要具…

避雷针防雷接地工程应用方案

避雷针是一种用于防止建筑物或其他设施被雷击的装置&#xff0c;它的原理是利用避雷针的尖端产生的电晕放电来释放空气中的电荷&#xff0c;从而降低雷电的危险性。 地凯科技避雷针的类型主要有以下几种&#xff1a; 普通避雷针&#xff1a;这是最常见的避雷针&#xff0c;它由…

Unitree B2:打破波士顿动力Spot垄断地位的机器狗

原创 | 文 BFT机器人 在技术高速发展的时代&#xff0c;机器狗的出现标志着科技领域在机器人技术方面的显著进步&#xff0c;这些曾经只存在于幻想中的机器狗现在已经成为各领域的新星&#xff0c;为安防巡逻、应急救援、工业检测、教育科研等各行各业带来了新的可能性和机遇…

12.07

#include "mywidget.h"MyWidget::MyWidget(QWidget *parent): QWidget(parent) {//窗口设置//去掉表头this->setWindowFlags(Qt::FramelessWindowHint);//重新设置大小this->resize(800,420);//设置背景颜色this->setStyleSheet("background-color:whi…