Android開發;Activity-Hook你了解多少?一起來debug

享學課堂特邀作者:周周
轉載請聲明出處!

前言

手把手講解系列文章,是我寫給各位看官,也是寫給我自己的。文章可能過分詳細,但是這是為了幫助到盡量多的人,畢竟工作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時間點:945df9``git 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 交給mMainThread(ActivityThread類的對象),于是,進入 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);

推斷出,生命周期函數一定和 ActivityLifecycleItemexecute()postExecute()有關,還記不記得之前我們debug出來的 PauseActivityItem , ResumeActivityItem, StopActivityItem . 這3個類就是 ActivityLifecycleItem的子類,進去看看:

debug 一下:image

發現了 ActivityThread,自然就聯想到它的 HextendsHandler ,ok, 就快接近真相了,表激動,暫且壓制一下,我們還沒有找到真憑實據。

又是一個抽象方法,直接找他的子類 ActivityThread,

終于接近真相了,這里已經很明顯了,參數是Activity對象,并且執行了 activity.performPause().

真相大白,原來一個Activity的生命周期函數 onPause是這么調用的,繞了一大圈,最終我們重寫的 onPause()方法才被執行。

小結論

從我們startActivity開始,如果用一張圖來展示 Activity生命周期函數如何被執行。無謂的繞繞繞去的省略中間環節,只展示重點環節,那就是這樣。

解決方案

現在可以運行我的Demo工程,注意 請切換到 git時間點:945df9, 按照上面的步驟去debug一下,結果發現,

上一章節中圖示的正常現象 PauseActivityItem , ResumeActivityItem, StopActivityItem,在這里并未看到,或者說,這里 EXECUTE_TRANSACTION 分支缺失了。肯定是因為我們hook的代碼導致 ActivityThreadH mH=newH()classHextendsHandlervoidhandleMessage(Messagemsg) 部分 switchcase 沒有執行。

既然已經確定是 ActivityThread的 mH 有問題, 那么應該檢查的,則是 hook mH的時候。

現在進入hook代碼:

回顧一下handler的責任鏈模式:

按照我通常的hook思路,mH通常執行的是 第三級 成員函數 handlerMessage(msg)的邏輯,我 hook一下,給mH的成員變量 mCallback賦值。 然后可以通過 return 返回值來控制要不要繼續執行 原 handlerMessage(msg)的邏輯。如果 mCallback.handlerMessage(msg)返回了true,那么就沒有后續了, handlerMessage(msg)永遠不會執行。如果是 returnfalse,handlerMessage(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)]

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/386078.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/386078.shtml
英文地址,請注明出處:http://en.pswp.cn/news/386078.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

牛客網筆記之JAVA運算符

計算機的最基本用途之一就是執行數學運算,作為一門計算機語言,Java也提供了一套豐富的運算符來操縱變量。我們可以把運算符分成以下幾組: 算術運算符 關系運算符 位運算符 邏輯運算符 賦值運算符 其他運算符 邏輯運算符結果 ~a是按位非&#…

Android架構師談:View-Pager-性能優化之-無限循環

作者:享學課堂Alvin老師 轉載請聲明出處! ViewPager實現無限滑動 **方案一:**將viewpager上限設置成一個很大的數,第一個頁面設置到中間。然后滑動的時候,用當前的序號與viewpager頁面數取余得到目標頁面的序號&#…

你知道如何用面向對象思想寫好并發編程嗎?

在工作中,我發現很多人在設計之初都是直接按照單線程的思路來寫程序的,而忽略了本應該重視的并發問題;等上線后的某天,突然發現詭異的 Bug,再歷經千辛萬苦終于定位到問題所在,卻發現對于如何解決已經沒有了…

你知道怎么在生產環境下部署tomcat嗎?

享學課堂特邀作者:老顧 轉載請聲明出處! 一、前言 小伙伴們在網上看到的很多文章,都是對tomcat的一些介紹,什么配置啊,怎么啟動。其實在生產環境中怎么部署,和網上介紹的有很大區別。這篇文章老顧就帶著大…

淺談HashMap

Java集合類的整體架構 比較重要的集合類圖如下: 有序否 允許元素重復否 Collection 否 是 List 是 是 Set AbstractSet 否 否 HashSet TreeSet 是(用二叉樹排序) Map AbstractMap 否 使用 key-value 來映射和存儲數據, Key 必須惟…

matlab實現一元線性回歸和多元線性回歸

在回歸分析中,如果有兩個或兩個以上的自變量,就稱為多元回歸。事實上,一種現象常常是與多個因素相聯系的,由多個自變量的最優組合共同來預測或估計因變量,比只用一個自變量進行預測或估計更有效,更符合實際。 在實際經濟問題中,一個變量往往受到多個變量的影響。例如,家…

互聯網寒冬!“996”為什么還沒實行?我還等著早點下班呢!

“喊了十多個月的‘996’,說要實行‘996’,上班上到現在,影子都沒看到,我還能早點下班嗎?” 我一個在廣州上班的朋友小李,在我去廣州出差期間,與他聊天的時候發出了這樣的牢騷,我剛…

matlab求解常微分方程組/傳染病模型并繪制SIR曲線

看了很多關于傳染病模型的matlab程序,大都是繪制出兩條曲線(I、S)的,本文最大的不同是繪出SIR三條曲線。 先給出SIR微分方程組 函數文件: run的程序:

互聯網寒冬!技術站最全MySQL數據庫實戰規范

享學課堂特邀作者:老顧 轉載請聲明出處! 前言 我們小伙伴們經常使用到mysql數據庫,一般就這么一用,很少會考慮mysql里面的細節問題,如sql語句的規范,或索引有沒有起到相應的效果,今天老顧就給大…

SQL求一個表中非重復數據及其出現的次數

mysql中,我們可以用distinct求不重復的數據有多少,也可以用group by。 這里有個例子,如下表sheet1,共有5411條數據 查詢語句 共有3446條不重復數據,每條不重復數據出現的次數在第二列顯示:

什么是微服務擴展性和高可用-可擴展性、高可用性和性能

歡迎關注專欄:Java架構技術進階。里面有大量batj面試題集錦,還有各種技術分享,如有好文章也歡迎投稿哦。 Overview 可擴展性、高可用性和性能 術語可擴展性、高可用性、性能和關鍵任務對于不同的組織或組織內的不同部門來說可能意味著不同的…

SQL實現當前行等于前面兩行數據之和

sql實現類似斐波那契數列的功能,即當前數據等于前面兩個數據之和,詳看本文例子 原表: sql語句(此處要熟悉JION ON的用法) 結果

【大牛系列教學】靠著這份面試題跟答案

開篇閑扯 打工人,打工魂,我們生而人上人。當“資本主義”逐漸禁錮我們人(大)上(韭)人(菜)肉體的時候,那一刻我才明白那個日不落帝國資本主義收割機瑞民族之光幸瑞幸咖啡…

matlab實現二項分布

二項分布 1. 求n次獨立重復試驗中事件A恰好發生k次的概率P。 命令:pdf 或 binopdf 格式:pdf (‘bino’, k, n, p) 或 binopdf (k, n, p) 說明:該命令的功能是計算二項分布中事件A恰好發生k次的概率。pdf為通用函數,bino表示二項分布,binopdf為專用函數,n為試驗總次數,…

【工作感悟】成功入職阿里月薪45K

前言 苦苦尋覓找工作之間,殊不知今日之時乃我心之痛,難道是我不配擁有工作嘛。自面試后他所謂的等待都過去一段時日,可惜在下京東上的小金庫都要見低啦。每每想到不由心中一緊。正處為難之間,手機忽然來了個短信預約后續面試。 我…

Mysql優化技巧

索引優化,查詢優化,查詢緩存,服務器設置優化,操作系統和硬件優化,應用層面優化(web服務器,緩存)等等。這里的記錄的優化技巧更適用于開發人員,都是從網絡上收集和自己整理…

【工作經驗分享】不會真有人覺得mybatis很難學吧

什么是自旋鎖和互斥鎖? 由于CLH鎖是一種自旋鎖,那么我們先來看看自旋鎖是什么? 自旋鎖說白了也是一種互斥鎖,只不過沒有搶到鎖的線程會一直自旋等待鎖的釋放,處于busy-waiting的狀態,此時等待鎖的線程不會…

華為面試題

技術面試: java: java學了幾年,看過什么書,有沒有看過thinking in java這本書 java的公共父類是什么,object類中有什么方法 object中有個hashcode方法,這個方法是用來做什么的, equal 與 的區…

【工作經驗分享】這些新技術你們都知道嗎

前言 近年來,微服務架構(Microservices Architecture)已經成為一種主流的軟件開發方法論,所謂微服務( Microservices ),就是一些具有足夠小的粒度、能夠相互協作且自治的服務體系。 微服務架構基于分布式系統,同時借助了面向服務架構和企業服…

京東面試題(JAVA)

京東17號一面問題與回答情況(Java崗) 作者:牛客190525號 Q1: HashMap的原理, 以及HashMap如何擴充bucket的大小 A1: 原理答上來了,如何擴容瞎答的,之前不知道擴容之后已經哈希的MapEntry如何處理,當時就…