如果觉得细节太细,直接跳过看结论即可。
本人能力有限,如若发现描述不当之处,欢迎留言批评指正。
学到老活到老,路漫漫其修远兮。与众君共勉 !
引子
HOOK系列是 今年年初大概3月份写的,其中《手把手讲解 Android Hook-实现无清单启动Activity》 这一篇的文章链接为:《
请峰子在这里替换为享学公众号的这篇文章的链接
》
Demo地址为:https://github.com/18598925736/ActivityHookDemo/tree/startActivityWithoutRegiste
当时是基于最新的SDK 28 android 9 进行的hook,但是近期有一位朋友提出,在SDK 28的设备上,hook之后会导致作为LauncherActivity
的生命周期完全失效。并且在SDK 29 android10的设备上,会崩溃。
这位朋友解决了崩溃的问题,在此对他(github名为:fangding
)表示感谢!
崩溃的问题我大致看过,也验证过,没有问题,已经合并到 开发分支上,很简单,只是SDK 29改了一些类名,各位可以到 github上去自行查看。现在要解决的是生命周期失效的问题。
声明一个debug源码的坑
hook开发的初期,一般不要用真机。因为真机的系统都是经过了手机厂家深度定制的,如果你想要进行代码debug,使用真机做不到的,因为代码的行数根本对应不上。推荐使用谷歌原生的模拟器。
本文采用的是 android 9
sdk 28
谷歌原生androidStudio自带AVD模拟器。
适合阅读的人群
如果你对hook 有概念,并且对具体如何去hook有 兴趣深入了解,那么这篇文章可以帮到你很多。
正文
- bug 表征
- 源码探索
- 解决方案
- 完美效果
- 可能隐患
bug 表征
Demo : https://github.com/18598925736/ActivityHookDemo/tree/startActivityWithoutRegiste
请切换到 git时间点: 945df9
git checkout 945df9
如果结果为:HEAD is now at 945df96 使用androidX
则切换成功。
这里是,已经出现问题的版本节点。
运行Demo,启动as自带模拟器 sdk28版本。进行跳转,
然后发现,生命周期函数并不执行。
跳转过程中,只出现了onCreate onStart onResume
, 照理说,跳转之后应该有onPause onStop
. 并且我回到这个Activity时,应该会有onRestart onStart onResume
, 但是也没有。
源码探索
为什么生命周期函数都不执行了?要找到这个原因,我必须先弄清楚一个问题: Activity的生命周期函数到底是由谁来调用的。
前期准备:
这里我不使用Demo工程,而是另外自己新建一个工程,写一个普通的startActivity跳转(这个我就不贴代码了). 因为我们要观察的是正常跳转。
开工,进入源码(SDK 28 注意,app module的sdk也要28,必须用 androidStudio自带的AVD SDK 28模拟器才能 debug ):
(为了确保源码探索的完整流程,我们从 startActivity开始 . )
这里产生2个分支,但是仔细观察之后,其实他们最终都走到了同一段逻辑:
Instrumentation.ActivityResult ar =
mInstrumentation.execStartActivity(
this, mMainThread.getApplicationThread(), mToken, child,
intent, requestCode, options);
if (ar != null) {
mMainThread.sendActivityResult(
mToken, child.mEmbeddedID, requestCode,
ar.getResultCode(), ar.getResultData());
}
来解析这一段逻辑:
两句代码,一个是
mInstrumentation.execStartActivity
看来这一段并没有涉及到Activity生命周期函数的逻辑。
那么,看下一段:
mMainThread.sendActivityResult
,从 mInstrumentation.execStartActivity 执行之后,得到了一个ar
,现在把这个ar
交给mMainThread(ActivityThread类的对象),于是,进入 ActivityThread源码:
这里的scheduleTransaction
方法,在ActivityThread的父类ClientTransactionHandler
中:
看到sendMessage,就怀疑,这里可能和Handler
扯上关系了。
还记不记得ActivityThread
的父类ClientTransactionHandler
scheduleTransaction()
方法中sendMessage(ActivityThread.H.EXECUTE_TRANSACTION, transaction);
用到了ActivityThread.H.EXECUTE_TRANSACTION
. 我们在H extends Handler
中找到这个switch case的分支:
一共就两句代码可能和Activity的生命周期函数调用有关,那么我有理由怀疑就是这一段代码在执行 生命周期函数. 那么 如何验证我的猜想是否成立? 答: debug源码(前面之所以要源码版本,AVD模拟器版本,项目版本gradle SDK版本都写成28,就是为了这里debug)
加上断点之后,开始debug,按下跳转按钮, 我们发现了惊人的现象:
一次跳转,我们debug发现了3个可能和生命周期函数有关的细节:
PauseActivityItem
,ResumeActivityItem
,StopActivityItem
,这3个是不是分别对应了 Activity的3个生命周期函数?
继续探索:
找到TransactionExecutor
类的execute()
方法:
我们需要跟踪的是transaction参数
(因为这里只有一个参数…不跟踪它跟踪谁呢)
然而,这里,使用到这个参数的是两个方法,executeCallbacks()
和 executeLifecycleState()
,
而,在这两个方法中,我都找到了类似下面这样的代码:
final ActivityLifecycleItem finalStateRequest = transaction.getLifecycleStateRequest();
item.execute(mTransactionHandler, token, mPendingActions);
item.postExecute(mTransactionHandler, token, mPendingActions);
推断出,生命周期函数一定和ActivityLifecycleItem
的execute()
和postExecute()
有关,还记不记得之前我们debug出来的PauseActivityItem
,ResumeActivityItem
,StopActivityItem
. 这3个类就是ActivityLifecycleItem
的子类,进去看看:
debug 一下:
发现了ActivityThread
,自然就联想到它的H extends Handler
,ok, 就快接近真相了,表激动,暂且压制一下,我们还没有找到真凭实据。
又是一个抽象方法,直接找他的子类ActivityThread
,
终于接近真相了,这里已经很明显了,参数是Activity对象,并且执行了 activity.performPause()
.
真相大白,原来一个Activity的生命周期函数 onPause
是这么调用的,绕了一大圈,最终我们重写的onPause()
方法才被执行。
小结论
从我们startActivity开始,如果用一张图来展示 Activity生命周期函数如何被执行。无谓的绕绕绕去的省略中间环节,只展示重点环节,那就是这样。
解决方案
现在可以运行我的Demo工程,注意 请切换到 git时间点:
945df9
, 按照上面的步骤去debug一下,结果发现,
上一章节中图示的正常现象PauseActivityItem
,ResumeActivityItem
,StopActivityItem
,在这里并未看到,或者说,这里 EXECUTE_TRANSACTION 分支缺失了。肯定是因为我们hook的代码导致ActivityThread
的H mH = new H()
,class H extends Handler
的void handleMessage(Message msg)
部分switch case
没有执行。
既然已经确定是 ActivityThread的 mH 有问题, 那么应该检查的,则是 hook mH的时候。
现在进入hook代码:回顾一下handler的责任链模式:
按照我通常的hook思路,mH通常执行的是 第三级 成员函数handlerMessage(msg)
的逻辑,我hook
一下,给mH的成员变量mCallback
赋值。 然后可以通过return
返回值来控制要不要继续执行 原handlerMessage(msg)
的逻辑。如果mCallback.handlerMessage(msg)
返回了true,那么就没有后续了,handlerMessage(msg)
永远不会执行。如果是return false,handlerMessage(msg)
仍会执行。
已经很接近真凶了。我确定是mCallback.handlerMessage(msg)
的 return true
导致的问题。就检查一下我hook的时候,哪里return true
了。Debug一下:
这里
list.size()
是0,刚好这里return true
了。
这里return
一下,是因为源码中使用了list.get(0)
,我不能让它在list为0的情况下去get(0)
. 对,就是这么单纯,没有别的想法。
完美效果
size()是0,刚好这里
return true了。 这里
return一下,是因为源码中使用了
list.get(0),我不能让它在list为0的情况下去
get(0)`. 对,就是这么单纯,没有别的想法。