以實(shí)戰(zhàn) iOS 連續(xù)崩潰檢測與自修復(fù)

實(shí)戰(zhàn) iOS 連續(xù)崩潰檢測與自修復(fù)

背景 在近期 iOS 上線的版本,友盟在它的升級(jí)版本中默認(rèn)就自動(dòng)進(jìn)行用戶的崩潰收集上報(bào)匆瓜。 如果只是惡心一下開發(fā)也就算了锐锣,然而在發(fā)版本時(shí),卻發(fā)現(xiàn)友盟的代碼沒有對(duì)上報(bào)的數(shù)據(jù)著洼,做類型的安全判斷樟遣,導(dǎo)致讀取數(shù)據(jù)每次都會(huì)crash. 搞崩潰檢測的,自己卻導(dǎo)致 App 不斷崩潰 : ) 堆棧如下:

image.png

從方法名稱 [UMCrash initUMCrash:channel:] 和 [WPKSetup sendAllReports] 可以很容易看出來, 這是友盟在做初始化的時(shí)候身笤,對(duì)崩潰信息進(jìn)行了一次檢測豹悬,然后做上報(bào),在處理數(shù)據(jù)的時(shí)候掛掉了液荸。 看不到具體代碼瞻佛,控制臺(tái)看到直接原因是: [NSTaggedPointerString objectForKey:]: unrecognized selector sent to instance 由于友盟的初始化發(fā)生在很早的時(shí)候(我們一般都放在啟動(dòng)階段)。這就導(dǎo)致大多服務(wù)還沒起來娇钱,應(yīng)用就已經(jīng)崩潰了伤柄。只要出現(xiàn)了這種情況,每次打開App, 都會(huì)因?yàn)橐粯拥膯栴}忍弛,而連續(xù)閃退响迂。 連續(xù)崩潰的后果 那么像這樣的連續(xù)崩潰,會(huì)造成什么后果呢细疚? 可以總結(jié)為以下的 3 點(diǎn):

  • 開發(fā)無感知.由于在啟動(dòng)階段就直接崩潰掉蔗彤,崩潰收集平臺(tái) bugly / 友盟 都沒有相關(guān)信息。更無從談修復(fù)疯兼。

  • 用戶無法反饋.因?yàn)槊看芜M(jìn)來都崩潰然遏,也無法反饋相關(guān)信息給到客服。

  • 新用戶 0 體驗(yàn) .當(dāng) App 連續(xù) 3 次都進(jìn)入就閃退吧彪,我自己來說待侵,是肯定不再使用這一款無法體驗(yàn)的 App。

解決方案 面對(duì)上述的情況姨裸,因?yàn)檎莆湛蓮?fù)現(xiàn)的場景秧倾,通過技術(shù)手段,我們的確可以 hook 掉它的上傳方法傀缩,從而解決崩潰問題那先。 例如這次,對(duì)我們發(fā)生的路徑中的 [WPKSetup sendAllReports] 進(jìn)行截獲,不再執(zhí)行赡艰。那么它當(dāng)然不再崩潰售淡。 但是上面解決方法有 3 點(diǎn)問題:

  • 成本取舍問題,原有的功能因?yàn)橐淮闻棘F(xiàn)崩潰場景而直接禁用。

  • SDK 功能缺失揖闸,甚至也可能引入新的問題揍堕。

  • 只解決了當(dāng)下的場景,缺乏必要手段解決其它的連續(xù)崩潰問題汤纸。也就是說衩茸,保得了一時(shí),保不了一世蹲嚣。

進(jìn)行連續(xù)崩潰檢測 在前面有提到過递瑰,連續(xù)崩潰的一大問題是--開發(fā)無感知。 也就是說隙畜,我們連問題發(fā)生了都不知道抖部,所以首要做到的是發(fā)現(xiàn)問題。 通常最先想到的思路议惰,就是和崩潰上報(bào)框架一樣慎颗,通過捕獲異常,來觀察它的每次崩潰言询。 捕獲異常的操作俯萎,也存在兩個(gè)缺點(diǎn):

  • 會(huì)與已有處理異常的代碼重復(fù),耦合

  • 與第三方 crash 收集框架的沖突运杭,導(dǎo)致漏檢測

對(duì)于第二點(diǎn)與第三方崩潰收集框架的沖突夫啊,是影響最大的地方,因?yàn)樗麄兊拇a通常對(duì)我們來說都是看不到的辆憔。

  • 持久化一個(gè) crashCount 變量

  • 每次啟動(dòng) crashCount = crashCount +1

  • 在 x 秒后撇眯,crashCount = 0

通過 crashCount 來代表崩潰次數(shù),每次啟動(dòng)的時(shí)候讓它加 1,如果 App 存活過一段時(shí)間,那么證明沒有發(fā)生連續(xù)崩潰,將它置空復(fù)位部默。一旦超過我們?cè)O(shè)定的次數(shù)閾值,證明連續(xù)這幾次時(shí)間閾值內(nèi)砂碉,都沒存活過去,發(fā)生了異常崩潰。 當(dāng)然也存在誤報(bào)的情況,比如用戶在這段時(shí)間閾值內(nèi)煎楣,主動(dòng)殺掉 App。這一點(diǎn)通過調(diào)整次數(shù)和時(shí)間兩方面的閾值车伞,可以控制择懂。 控制誤報(bào) 我們可以在原來的方案中,更進(jìn)一步控制誤報(bào)帖世,想辦法監(jiān)聽用戶主動(dòng)殺App的場景:

  • 用戶在前臺(tái)殺APP

  • 用戶在后臺(tái)殺APP

對(duì)于誤報(bào)的情況,大多數(shù)都是第一種,在幾秒之內(nèi)日矫,啟動(dòng)時(shí)前臺(tái)殺APP赂弓,iOS 中通過 UIApplicationWillTerminateNotification 來監(jiān)聽,收到通知后哪轿,將次數(shù)置空清零盈魁。 自動(dòng)修復(fù)連續(xù)崩潰 要對(duì)于崩潰進(jìn)行修復(fù),首先需要知道這類問題的常見原因窃诉。 對(duì)于代碼 bug 的問題杨耙,如果固定進(jìn)入就必現(xiàn)崩潰的話,在測試流程就一般還是會(huì)暴露出來飘痛。當(dāng)然并不完全排除代碼崩潰的情況珊膜。 清除數(shù)據(jù) 造成線上問題連續(xù)崩潰的,肯定是一個(gè)“變量”宣脉,那么應(yīng)該是:

  • 數(shù)據(jù)庫

  • 存儲(chǔ)文件

  • 服務(wù)端數(shù)據(jù)

對(duì)于 數(shù)據(jù)庫 和 存儲(chǔ)文件 的修復(fù)车柠,我們都做一個(gè)清理操作,以本地?cái)?shù)據(jù)的清理塑猖,來保證 App 的正常流程竹祷。 對(duì)于重要的數(shù)據(jù)定義,可以先傳入云端存起來羊苟。 這次我們出現(xiàn)的友盟崩潰塑陵,也正是因?yàn)樽x取了存在本地的問題數(shù)據(jù)而導(dǎo)致連續(xù)閃退的。 重新請(qǐng)求/運(yùn)行熱修復(fù)包 而對(duì)于服務(wù)端數(shù)據(jù)處理的失敗蜡励,通過與服務(wù)端排查令花,返回正常的數(shù)據(jù)進(jìn)行解決。也可以提供入口讓用戶上報(bào)或者直接與我們聯(lián)系巍虫。 甚至考慮引入動(dòng)態(tài)修復(fù)手段彭则,解決代碼 bug ,請(qǐng)求以及運(yùn)行熱修復(fù)包占遥。 具體處理 按照 微信讀書團(tuán)隊(duì)的處理俯抖,是在 didFinishLaunching 的階段做 hook。當(dāng)觸發(fā)崩潰限制數(shù)量后瓦胎,進(jìn)入修復(fù)芬萍,修復(fù)完成后再調(diào)用原方法 didFinishLaunching ,來按照原來的流程進(jìn)入到 App. 結(jié)合我們的工程實(shí)際情況搔啊,自動(dòng)修復(fù)流程與有細(xì)節(jié)差異:

  • Appdelegate 的 initialize 就開始有日志初始化柬祠。

  • 在 willFinishLaunching 的階段,有一些數(shù)據(jù)和服務(wù)的初始化负芋。

  • applicationDidBecomeActive 也有邏輯漫蛔。

  • 進(jìn)入首頁需要拉取的請(qǐng)求和模塊邏輯復(fù)雜。 需要解決這些問題,不止是對(duì) didFinishLaunching 的階段做 hook莽龟,還要分別對(duì)上述的情況進(jìn)行處理蠕嫁,在崩潰數(shù)超過限制后進(jìn)行攔截。 而中間整合毯盈,也發(fā)生了如服務(wù)找不到崩潰的問題等剃毒,需要一點(diǎn)點(diǎn)解決和整合。 目前臨時(shí)增加一個(gè)方法:

+ (BOOL)needFixCrashes{    NSInteger launchCrashes = [self crashCount];        if (launchCrashes >= kContinuousCrashOnLaunchNeedToReport) {        return YES;    }    return NO;}

通過 needFixCrashes 來在各處做控制:


if(needFixCrashes){   return;}//原有正常邏輯處理

在修復(fù)后搂赋,我們?cè)俜謩e調(diào)用對(duì)應(yīng)流程赘阀。相當(dāng)于 [A do],[B do]..一個(gè)個(gè)去調(diào)用。 更好的思路

ps;作為iOS開發(fā)人員必看ios資料大全脑奠,其中你有想象不到的面試題和學(xué)習(xí)資料等等

其實(shí)對(duì)于上述流程基公,還有一個(gè)更好的做法,限于業(yè)務(wù)時(shí)間沒做捺信。

我們可以將流程中要 hook 的對(duì)象和方法酌媒,都想辦法存儲(chǔ)起來,如使用 NSMapTable 等迄靠。

在結(jié)束修復(fù)后秒咨,再按順序遍歷出來對(duì)象和方法一個(gè)個(gè)調(diào)用,走完一套啟動(dòng)的流程掌挚。

這里的好處在于復(fù)用雨席,可以直接 addObject:Selector: 的方式就增加進(jìn)去,之后修復(fù)完成吠式,不需要再寫 hard code 一個(gè)個(gè)調(diào)用陡厘。 3.3 最終流程

最終檢測流程為:

  1. 啟動(dòng)App, crash=crash+1

  2. 檢查 crash<maxCrash

  3. crash < maxCrash,則進(jìn)入正常啟動(dòng)流程, 一段時(shí)間后就置空

  4. crash >= maxCrash, 進(jìn)入修復(fù)引導(dǎo)

修復(fù)的流程設(shè)計(jì)為:

  1. 設(shè)置根控制器為新的控制器,并彈出修復(fù)框特占,提示“檢測到應(yīng)用可能已損壞糙置,是否嘗試修復(fù)?”

  2. 用戶選擇"取消",則上報(bào)信息到平臺(tái)是目,然后 App 退出到后臺(tái)

  3. 用戶選擇"修復(fù)",則進(jìn)行我們的數(shù)據(jù)清理操作(重要數(shù)據(jù)考慮先云備份)谤饭,然后上報(bào)信息。

  4. 修復(fù)完成后懊纳,直接重新初始化全部服務(wù)揉抵,進(jìn)入首頁。

  5. 最壞的情況嗤疯,數(shù)據(jù)清理也仍然無濟(jì)于事冤今,記錄下一段時(shí)間內(nèi)的“修復(fù)”次數(shù)。提供方式直接聯(lián)系到平臺(tái)茂缚,在條件的情況下解決閃退戏罢。

實(shí)際操作當(dāng)中屋谭,有不少業(yè)務(wù)待我們梳理,光做到在所有服務(wù)之前檢測龟糕,如果之前沒有專門的類收攏處理戴而,就要花時(shí)間來做,或者在各處進(jìn)行判斷翩蘸。總的來說淮逊,最主要的思路是:

  • 崩潰檢測要在整個(gè) App 里催首,做到最先啟動(dòng)。代碼足夠干凈和簡單泄鹏。

  • 修復(fù)時(shí)的數(shù)據(jù)要分類郎任,哪些重要的要備份,哪些直接刪除备籽。

  • 修復(fù)后進(jìn)入 App,路徑要足夠完整舶治,做到順暢進(jìn)入。

4. 總結(jié)


連續(xù)崩潰問題的發(fā)生车猬,可以說是一個(gè) App 最嚴(yán)重的問題霉猛,一般來說并不會(huì)出現(xiàn)。

而隨著用戶的增多珠闰,任何問題也有可能被無限放大惜浅。

所以作為技術(shù)人員,需要做好兜底的策略伏嗜,盡量來消除此類問題坛悉,保證好用戶體驗(yàn),通過技術(shù)保護(hù)手段多留住一個(gè)用戶就是一個(gè)用戶承绸。

何況事實(shí)證明裸影,未雨綢繆是應(yīng)該的,這次連續(xù)崩潰問題不就發(fā)生在了自己身上了嗎军熏?

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末轩猩,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子羞迷,更是在濱河造成了極大的恐慌界轩,老刑警劉巖,帶你破解...
    沈念sama閱讀 218,451評(píng)論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件衔瓮,死亡現(xiàn)場離奇詭異浊猾,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)热鞍,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,172評(píng)論 3 394
  • 文/潘曉璐 我一進(jìn)店門葫慎,熙熙樓的掌柜王于貴愁眉苦臉地迎上來衔彻,“玉大人,你說我怎么就攤上這事偷办〖瓒睿” “怎么了?”我有些...
    開封第一講書人閱讀 164,782評(píng)論 0 354
  • 文/不壞的土叔 我叫張陵椒涯,是天一觀的道長柄沮。 經(jīng)常有香客問我,道長废岂,這世上最難降的妖魔是什么祖搓? 我笑而不...
    開封第一講書人閱讀 58,709評(píng)論 1 294
  • 正文 為了忘掉前任,我火速辦了婚禮湖苞,結(jié)果婚禮上拯欧,老公的妹妹穿的比我還像新娘。我一直安慰自己财骨,他們只是感情好镐作,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,733評(píng)論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著隆箩,像睡著了一般该贾。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上捌臊,一...
    開封第一講書人閱讀 51,578評(píng)論 1 305
  • 那天靶庙,我揣著相機(jī)與錄音,去河邊找鬼娃属。 笑死六荒,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的矾端。 我是一名探鬼主播掏击,決...
    沈念sama閱讀 40,320評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢(mèng)啊……” “哼秩铆!你這毒婦竟也來了砚亭?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,241評(píng)論 0 276
  • 序言:老撾萬榮一對(duì)情侶失蹤殴玛,失蹤者是張志新(化名)和其女友劉穎捅膘,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體滚粟,經(jīng)...
    沈念sama閱讀 45,686評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡寻仗,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,878評(píng)論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了凡壤。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片署尤。...
    茶點(diǎn)故事閱讀 39,992評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡耙替,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出曹体,到底是詐尸還是另有隱情俗扇,我是刑警寧澤,帶...
    沈念sama閱讀 35,715評(píng)論 5 346
  • 正文 年R本政府宣布箕别,位于F島的核電站铜幽,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏串稀。R本人自食惡果不足惜啥酱,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,336評(píng)論 3 330
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望厨诸。 院中可真熱鬧,春花似錦禾酱、人聲如沸微酬。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,912評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽颗管。三九已至,卻和暖如春滓走,著一層夾襖步出監(jiān)牢的瞬間垦江,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,040評(píng)論 1 270
  • 我被黑心中介騙來泰國打工搅方, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留比吭,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 48,173評(píng)論 3 370
  • 正文 我出身青樓姨涡,卻偏偏與公主長得像衩藤,于是被迫代替她去往敵國和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子涛漂,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,947評(píng)論 2 355

推薦閱讀更多精彩內(nèi)容