网站改版策划书,做网站运营要了解哪些,企业网站首页,企业网站 模版享学课堂特邀作者#xff1a;周周 转载请声明出处#xff01; 前言 手把手讲解系列文章#xff0c;是我写给各位看官#xff0c;也是写给我自己的。文章可能过分详细#xff0c;但是这是为了帮助到尽量多的人#xff0c;毕竟工作5,6年#xff0c;不能老吸血#xff0c;… 享学课堂特邀作者周周 转载请声明出处 前言 手把手讲解系列文章是我写给各位看官也是写给我自己的。文章可能过分详细但是这是为了帮助到尽量多的人毕竟工作5,6年不能老吸血也到了回馈开源的时候。 这个系列的文章 1、用通俗易懂的讲解方式讲解一门技术的实用价值 2、详细书写源码的追踪源码截图绘制类的结构图尽量详细地解释原理的探索过程 3、提供Github 的 可运行的Demo工程,但是我所提供代码更多是提供思路抛砖引玉请酌情cv 4、集合整理原理探索过程中的一些坑或者demo的运行过程中的注意事项 5、用gif图最直观地展示demo运行效果 如果觉得细节太细直接跳过看结论即可。本人能力有限如若发现描述不当之处欢迎留言批评指正。 学到老活到老路漫漫其修远兮。与众君共勉 !
引子 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时间点945df9git checkout945df9如果结果为HEADisnow at945df96使用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 交给mMainThreadActivityThread类的对象,于是进入 ActivityThread源码 这里的 scheduleTransaction方法在ActivityThread的父类 ClientTransactionHandler中: 看到sendMessage就怀疑这里可能和 Handler扯上关系了。 还记不记得 ActivityThread的父类 ClientTransactionHandler scheduleTransaction()方法中 sendMessage(ActivityThread.H.EXECUTE_TRANSACTION,transaction); 用到了 ActivityThread.H.EXECUTE_TRANSACTION. 我们在 HextendsHandler中找到这个switch case的分支 一共就两句代码可能和Activity的生命周期函数调用有关那么我有理由怀疑就是这一段代码在执行 生命周期函数. 那么 如何验证我的猜想是否成立 答debug源码前面之所以要源码版本AVD模拟器版本项目版本gradle SDK版本都写成28就是为了这里debug加上断点之后开始debug按下跳转按钮, 我们发现了惊人的现象 一次跳转我们debug发现了3个可能和生命周期函数有关的细节PauseActivityItem , ResumeActivityItem, StopActivityItem,这3个是不是分别对应了 Activity的3个生命周期函数继续探索找到 TransactionExecutor类的 execute()方法: 我们需要跟踪的是 transaction参数 (因为这里只有一个参数…不跟踪它跟踪谁呢) 然而这里使用到这个参数的是两个方法 executeCallbacks() 和 executeLifecycleState(), 而在这两个方法中我都找到了类似下面这样的代码
finalActivityLifecycleItem finalStateRequest transaction.getLifecycleStateRequest();
item.execute(mTransactionHandler, token, mPendingActions);
item.postExecute(mTransactionHandler, token, mPendingActions);推断出生命周期函数一定和 ActivityLifecycleItem的 execute()和 postExecute()有关,还记不记得之前我们debug出来的 PauseActivityItem , ResumeActivityItem, StopActivityItem . 这3个类就是 ActivityLifecycleItem的子类进去看看 debug 一下 发现了 ActivityThread自然就联想到它的 HextendsHandler ,ok, 就快接近真相了表激动暂且压制一下我们还没有找到真凭实据。 又是一个抽象方法直接找他的子类 ActivityThread, 终于接近真相了这里已经很明显了参数是Activity对象并且执行了 activity.performPause(). 真相大白原来一个Activity的生命周期函数 onPause是这么调用的绕了一大圈最终我们重写的 onPause()方法才被执行。
小结论 从我们startActivity开始如果用一张图来展示 Activity生命周期函数如何被执行。无谓的绕绕绕去的省略中间环节只展示重点环节那就是这样。 解决方案 现在可以运行我的Demo工程注意 请切换到 git时间点945df9, 按照上面的步骤去debug一下结果发现 上一章节中图示的正常现象 PauseActivityItem , ResumeActivityItem, StopActivityItem在这里并未看到或者说这里 EXECUTE_TRANSACTION 分支缺失了。肯定是因为我们hook的代码导致 ActivityThread的 H mHnewH() classHextendsHandler 的 voidhandleMessage(Messagemsg) 部分 switchcase 没有执行。 既然已经确定是 ActivityThread的 mH 有问题, 那么应该检查的则是 hook mH的时候。 现在进入hook代码 回顾一下handler的责任链模式 按照我通常的hook思路mH通常执行的是 第三级 成员函数 handlerMessage(msg)的逻辑我 hook一下给mH的成员变量 mCallback赋值。 然后可以通过 return 返回值来控制要不要继续执行 原 handlerMessage(msg)的逻辑。如果 mCallback.handlerMessage(msg)返回了true那么就没有后续了 handlerMessage(msg)永远不会执行。如果是 returnfalsehandlerMessage(msg)仍会执行。 已经很接近真凶了。我确定是 mCallback.handlerMessage(msg) 的 returntrue导致的问题。就检查一下我hook的时候哪里 returntrue了。Debug一下 这里 list.size()是0刚好这里 returntrue了。这里 return一下是因为源码中使用了 list.get(0)我不能让它在list为0的情况下去 get(0). 对就是这么单纯没有别的想法。 完美效果
行动吧把 这里的return true 改为 return false。然后再 重新运行观察日志依然是从1跳转到2。 日志如下 生命周期函数已经完整。问题解决! 可能隐患 问题解决可喜可贺。当我兴高采烈地想在新买的华为mate30手机上试验效果时发现。正常的Activity跳转和hook之后的跳转速度截然不同. 肉眼可见的速度差别hook之后慢了不止一拍。其他手机尚未发现。也不知道是不是华为底层做了什么事情。总之这又是下一阶段应该考虑的问题了。 结语 技术研究就是这样问题是无止境的优化是无止境的一项技术的诞生永远会伴随这无穷无尽的优化重构升级增强扩展。学习也是如此所谓活到老学到老技术人应该保持对技术的热情,执着和追求坚持学习。像是今天Activity 生命周期函数是如何被调用的以前只是疑惑现在终于解开谜团,完完全全抽丝剥茧得到真相。 所谓坑坑更健康做技术不可以害怕坑坑洞洞有坑解决了坑自己才能有收获。而且类似这种问题我解决问题就是将true改为false花了1秒。但是我检查问题找出真相的过程花了1天。我相信工作中类似这种问题不在少数。在没有足够了解核心代码逻辑的情况下去编程很有可能出一些细节性的小错误这些错误往往是致命的。 所以虽然SDK系统源码一些第三方库源码很大很复杂很多弯弯绕绕各种回掉各种映射各种设计模式看上去很可怕但是我们没有退路退缩不前只会让35岁被离职的风险加大。花点时间去补充基础知识的缺失鼓起勇气去读源码只要掌握正确的方法也许能在源码的世界中找到一片不一样的天空。 2019又一年过去了与 各位技术人共勉 喜欢本文的话可以关注我们的官方账号第一时间获取资讯。 你的关注是对我们更新最大的动力哦~ 最后
给大家送一个小福利 资料都是免费分享的附送高清脑图高清知识点讲解教程以及一些面试真题及答案解析。送给需要的提升技术、准备面试跳槽、自身职业规划迷茫的朋友们。点我免费领取 转存中…(img-PvzvTlZK-1623556289498)]
资料都是免费分享的附送高清脑图高清知识点讲解教程以及一些面试真题及答案解析。送给需要的提升技术、准备面试跳槽、自身职业规划迷茫的朋友们。点我免费领取
[外链图片转存中…(img-x6M0Sz3i-1623556289498)]