【騰訊Bugly乾貨分享】JSPatch 成長之路

本文來自於騰訊bugly開發者社區,非經做者贊成,請勿轉載,原文地址:http://dev.qq.com/topic/579efa7083355a9a57a1ac5b前端

Dev Club 是一個交流移動開發技術,結交朋友,擴展人脈的社羣,成員都是通過審覈的移動開發工程師。每週都會舉行嘉賓分享,話題討論等活動。vue

本期,咱們邀請了騰訊WXG iOS開發工程師——bang 陳振焯,爲你們分享**《JSPatch成長之路》**。react

如何加入 Dev Club?git

移動端開發經驗 >= 2 年,微信掃描下方羣管理微信二維碼,備註姓名-公司(或產品) 申請加入。github

外部羣二維碼


分享內容簡介: JSPatch 是 iOS 上的動態更新框架,只須要引入小小的引擎文件,就能夠用 JS 調用和替換任意 OC 方法。目前被廣泛用於實時修復 bug,已有超過2500個 APP 接入,本次分享介紹 JSPatch 發展過程當中遇到的問題和解決思路。 (此內容已在 GMTC 線下分享過,本次從新整理爲線上分享)數據庫

內容大致框架:json

  1. 起步:介紹 JSPatch 的誕生和當時碰到的難題
  2. 發展:介紹 JSPatch 如何補全周邊功能變得更好用
  3. 下一步:介紹 JSPatch 下一步的計劃

分享人介紹:後端

bang 陳振焯 廣州研發部 目前負責開發微信讀書iOS端,博客 http://blog.cnbang.net緩存


下面是本期分享內容整理安全


你們好,我是 bang,目前在廣州研發部作微信讀書 iOS 端,今天分享的主題是《JSPatch 成長之路》。

我在去年5月發佈了 JSPatch (https://github.com/bang590/JSPatch) 這個開源庫,如今普遍應用於 iOS 的熱修復,今天分享一下 JSPatch 過去一年以來的成長。

**分享共分爲三個部分: **

  1. 起步 —— 介紹JSPatch的誕生和當時碰到的難題
  2. 發展 —— 介紹JSPatch如何補全周邊功能變得愈來愈好用
  3. 下一步 —— 介紹JSPatch下一步的計劃

1、起步

先說下起步階段。當時碰到的一個問題是:APP 線上 bug 修復週期長,成本高,版本發佈出去後,發現一個 bug,要修復這個 bug 就必須得另外發一個版本,也就是要經歷:測試——打包——發佈——審覈——用戶下載,這一系列過程,成本很是高,最後還很難讓全部用戶都升級上來。

當時業界已有一個解決方案,叫 waxPatch,它是在APP裏嵌入 lua 引擎,而後經過 OC 的 runtime 接口在 lua 裏調用和替換 OC 方法,這樣就能夠下發 lua 腳本替換原生代碼,動態修復 bug。

waxPatch: https://github.com/mmin18/WaxPatch

這是個不錯的方案,但當時的 waxPatch 存在不少缺陷:

首先是 wax 已經多年不維護了,致使不支持一些 block/64 位等新特性,此外當時 wax 還有文檔不足,測試不足,線程不安全,難以調試等坑。

因而開始探求更好的解決方案。很容易想到若是用 JavaScript 作這個事情的話,相對 lua 原生就有一些優點:

  1. iOS 裏已內置 JavascriptCore 引擎,無需再另外嵌入。
  2. JS 在終端應用普遍,不少混合開發內嵌 H5 頁面就是用 JS。
  3. 符合蘋果審覈規則,蘋果在文檔裏說明不能夠下載可執行的代碼,由 JavascriptCore 執行的除外

那麼有沒有人試過這樣作呢?用 Javascript 調用和替換 OC 方法,固然是有的。

  • 在當時有一個開源庫 JavascriptBridge (https://github.com/kishikawakatsumi/JavaScriptBridge), 它能夠用 JS 調用 OC 接口。
不過它用的是 JavascriptCore 原生的接口作的,須要事先在 OC 裏定義好要調用的接口,沒有事先定義的不能調,這致使它的實現很臃腫,由於要在 OC 定義大量的方法。此外它也不能替換 OC 方法,實用性很低。

  • 當時國外還有一個熱補丁服務叫 rollout (http://rollout.io)。 它是一個服務平臺,底層也是用 JS 調用和替換 OC 方法去實時修復 bug,不過它不是開源的,只能在這個平臺上用,另外它的 JS 寫法是比較複雜的,看看這個例子就知道,這致使它不得不在平臺上作一些便捷的功能,把一些經常使用的操做封裝起來,減小使用者寫代碼。

總的來講,當時並無一個更好的方案,因而想本身造個。

當時指望作到的效果是這樣的:

我在 JS 寫 UIView.allOC(), 而後傳給 JavascriptCore 執行,JavascriptCore 把我要調用的信息,這裏類名是 UIView,類方法名是 alloc 傳遞給 OC,OC runtime 就能夠找到這個類和方法進行調用。這是最基本的一個語句調用。

實際上當時實現這個最基本的調用就遇到一個檻,在 JS 裏這條語句根本沒法執行:

要讓這條語句在 JS 環境中能夠執行,在 JS 的語言規則下,UIView 必須是一個對象,alloc 必須這個對象的一個方法。

也就是說要像這樣定義後才能夠執行:

UIView 必須是一個對象這點沒問題,在調用前定義就能夠,但 UIView 的方法必須在調用前定義就很糟糕,這意味着若是你想調用任意 UIView 的方法,你就須要提早把全部 UIView 的方法都找出來,每個方法都要預先定義好。

也就是說在使用UIView以前,須要先去 OC 把UIView全部方法找出來,而後構建UIView對象,每一個方法都在這個對象裏生成對應的函數,而後你才能夠調用UIView的任意方法。

JSPatch 在開發時就嘗試過這種方案,後來發現這些對象的方法太多了,僅 NSObject 基類的實例方法就有830個,類方法有118個,這致使在JS生成的對應的對象佔用內存極高,NSObject就佔了1.3M,UIView佔2M。這根本不可用。

對此我還進行了一些優化嘗試,例如去除掉裏面的下劃線開頭的私有方法,在 JS 構造繼承鏈共用基類方法。但這些優化都沒多少效果,佔用內存依舊很高。當時就以爲不太可能實現。

實際上當時我陷入了一個思惟定勢,作終端久了,思惟停留在 iOS 的 OC 世界,寫代碼必須遵照語言的規則,上述的困難也是在遵照 JS 語言規則這個前提下碰到的。

若是有方法不遵照語言規則呢?實際上在 JS 界,有個很經常使用的伎倆,就是預編譯:

也就是咱們寫的腳本不直接拿給 JS 引擎執行,而是進行一些轉換後才執行,在現代框架這個用法很常見, react/vue 都用了,甚至還有像 coffieScript 這樣把 JS 徹底換成另外一種語言的作法。

想到這一點,剛纔的問題就很好解決了。

只須要把全部函數調用在執行前都替換一遍,變成去調用一個固定的 __c 函數, 這個 __c 函數模擬了 ruby/lua 等語言的元方法機制,對方法調用進行轉發。

仍是以調用 UIViewalloc 方法爲例,這個語句在執行以前會被替換爲調 UIView 的 __c 方法,把 "alloc" 做爲字符串參數穿進去,在 __c 方法裏判斷調用者 UIView 是否是 OC 對象。若是是,就把類名和傳進來的方法名傳到 OC 層進行調用,若是不是,就調用回 JS 這個對象的方法。

這樣作簡潔高效地解決了前面的問題,不須要去 OC 遍歷每一個類的方法,不須要存儲這些方法,就能夠調用任意 OC 方法,只須要給 JS 基類定義一個 __c 方法就能夠了。正則替換後不管調用 OC 的什麼方法,都不會有語法錯誤,由於都變成調用這個 __c 方法,在這個 __c 方法裏作處理去 OC 層調用相應的方法就好了。

使用這種方案後內存的佔用降低了 99%,甚至更多,也使 JSPatch 的使用成爲可能,這是 JSPatch 最核心的一點。

解決這個核心問題後,後面就是細化功能了,JSPatch 發佈之後一直在完善,包括最基本的調用和替換OC方法,還有支持64位,支持 block,支持包括 c指針/Class/結構體等類型,支持 c函數的調用等,這裏面的細節原理我以爲看文章會比較清晰,相關文章均可以在 github wiki (https://github.com/bang590/JSPatch/wiki) 上找到,這裏就很少說了。

2、發展

接下來講說 JSPatch 是怎樣進行進一步發展的。

在完善 JSPatch 的同時,我也在想,如何把 hotfix 作得更好。

主要有兩個思路:

  1. 下降使用門檻
  2. 提升使用效率

咱們一個點一個點來看對這兩個問題是怎麼作的。

首先 JSPatch 在易用性上一直堅持着一個理念,就是 keep it simple and tiny,用中文說就是保持精巧,保持好用。JSPatch 從開源到如今一年多,增長和完善了不少功能,但它的使用方式和接口都沒有變過,一直以來都是隻有三個文件,拖入項目直接可使用,也會很謹慎地新增接口,不會影響到舊接口的使用,不會出現同一份代碼在舊版本能用,在新版本不能用的狀況,在易用性上下降使用門檻。

另外一個問題是安全問題

JSPatch 能夠調用和替換任意 OC 方法,權限很大,若是在傳輸過程當中被第三方攻擊,替換了下發的代碼去執行,會對用戶和 APP 自己形成很大傷害。若是每一個接入 JSPatch 的人都要考慮這個安全問題,接入門檻就會很高,也可能會由於考慮不周全致使 APP 處於危險狀態。

對此當時詳細考慮了安全策略。對這種狀況:

  • 最簡單的方案是直接對腳本加密,後臺使用固定密鑰加密腳本後下發,客戶端使用一樣的密鑰解密。這種方案的優勢是簡單,缺點是這個密鑰必須存在客戶端,黑客很容易破解拿到這個密鑰,而後經過傳輸過程第三方攻擊,下發一樣用這個密鑰加密的惡意代碼,就沒有安全可言了。

  • 第二個方案是讓腳本經過 https 傳輸,這個方案的優勢是安全性高,只要客戶端對證書進行過足夠的驗證,就能很好地保證安全性。缺點是這個方案門檻高,部署繁瑣,須要購買證書,對一些中小 APP 來講可能難以接受,而且若是用戶手機信任了一些惡意證書,也仍是存在被攻擊的危險。

  • 第三個方案是使用RSA簽名驗證。 整個流程是這樣:

第一步服務端計算腳本文件的MD5值,用存在服務端的私鑰對這個MD5值進行加密,而後把這個MD5值和腳本一塊兒打包下發給客戶端。客戶端拿到腳本和加密後的MD5值,用存在客戶端的公鑰進行解密,拿到服務端計算的MD5值,本地再計算一遍腳本文件的MD5值,對比這兩個值是否一致,若一致則表示傳輸過程當中沒有被篡改。

若是第三方要截獲請求下發惡意腳本,第三方必須用私鑰加密這個惡意腳本的MD5值一塊兒下發,才能經過驗證執行,只要服務端不被攻破,第三方就沒有私鑰,也就沒法進行篡改。

能夠看到這第三個方案門檻低,通用性高,部署簡單,安全性也高,對服務端和客戶端都沒有什麼特殊要求。

我把這一套安全方案作成一個組件,叫 JPLoader,也開源在 JSPatch 項目上(https://github.com/bang590/JSPatch/tree/master/Loader), 須要部署 JSPatch 的同窗能夠直接使用這套組件,解決安全性問題,客戶端的工做就完成了,只剩下後端的工做。

前面把安全性問題解決了,只剩下後端的工做,但搭建後臺對使用者來講也是挺麻煩的事,特別是做爲 iOS 開發者,在中小公司本身搭後臺麻煩,在大公司要後臺幫你搭也不容易,這又會致使使用 JSPatch 的門檻仍是很高。

因而在想這部分工做能不能也幫使用者省了呢?

對此我搭建了 JSPatch 平臺 (http://JSPatch.com), 讓使用 JSPatch 的人不須要搭建後臺,直接經過平臺下發補丁代碼。

這個平臺幾個月前已經開放註冊,如今全部人均可以使用。

在搭建這個平臺時,碰到一個問題值得分享一下,就是如何支持高併發?

因爲 JSPatch 的補丁特性,補丁須要及時推送給用戶,也就是說至少須要在每次啓動時向服務端請求詢問 APP 是否有新的補丁,有的話下發執行。這裏詢問的請求量是很高的,單個 APP 能夠控制,但平臺要面對多個 APP,累計起來的請求數量會很是多,併發會很高,怎樣支撐這樣的高併發?

正常來講這樣一個系統總體設計大體是這樣的:

平臺用戶把腳本放到平臺服務端,服務端的數據庫保存着腳本的各類信息和內容,APP 客戶端向平臺發起請求詢問是否有新腳本,平臺服務端接收到請求後經過 CGI 處理請求參數,根據 APPkey 等參數從數據庫拿出這個 APP的信息,而後組裝數據告訴APP客戶端有沒有新腳本。

這裏的詢問請求至少時 APP 每次啓動都要發一次請求,才能保證腳本的更新能儘快下發。請求量大時,這裏從數據庫取出數據很容易成爲整個系統的瓶頸,CGI 處理請求參數和組裝數據也要耗很多資源。

對此我改用了另外一種方式:

平臺用戶上傳腳本到平臺服務器時,服務端除了把 APP 信息存在 DB 外,同時會另外上傳一份 JSON 靜態文件到靜態雲服務器,JSON 裏保存了當前補丁的版本,而這個靜態資源的文件名是由 APPkey/APP 版本號組成的。

例如這裏腳本補丁版本號是10,這個JSON靜態文件的內容就是 {v:10}。能夠想象靜態文件的訪問路徑就是:

http://JSPatch.com/{APPkey}/{APP_version}.json

而後 APP 客戶端再也不向平臺服務端發請求,而是向這個靜態資源服務器發請求,根據 APPKey 和 APP 版本直接請求到這個 JSON 文件,裏面帶的版本號信息就能夠告訴 APP 腳本是否有更新。

整個流程就變成了:

APP 向靜態服務器詢問是否有新補丁,靜態服務器直接返回預先設置好的 JSON,就結束了。

這樣 APP 永遠不會跟平臺服務器打交道,只需跟靜態資源服務器打交道,靜態資源的高併發處理起來就簡單得多,成本也低不少,如今有不少靜態資源雲存儲,直接接入就能夠了,以這些雲存儲的能力,支持多高的併發都沒有問題,用戶量多大的 APP 接入均可以支撐到。就是這樣 JSPatch 平臺解決了高併發問題,能夠投入使用。

接下來在開發效率上,有一個問題是轉換代碼效率低。

咱們用 JSPatch 修復 bug 時時以方法爲單位進行替換的,若原方法有上百行,你的需求只是修改其中一兩行代碼,你也要把這上百行代碼人工翻譯成 JS 才行。對此我開發了JSPatch Convertor 這個工具,能夠自動把 OC 代碼轉爲 JSPatch 代碼,提高開發效率。

這個工具也開源在 github 上(https://github.com/bang590/JSPatchConvertor), 支持了大部分語法特性,但目前還作不到支持全部特性,像私有變量/靜態變量/宏這些還不支持,因此轉換後須要人工修改,但仍是很大地提升了使用 JSPatch 的效率。

總結下來,在下降使用門檻上,JSPatch 保證了易用性,封裝了安全方案,提供了 JSPatch 平臺讓使用者能夠直接接入,另外還有完善的文檔和解析文章保證使用無障礙。提升使用效率上,作了 JSPatch Convertor 自動轉換代碼,也內置了一些擴展方便直接調用一些經常使用的 C函數。

通過不斷髮展,JSPatch 能夠說是 iOS hotfix 的最佳解決方案。

目前大部分應用都已經接入使用,據不徹底統計至少有 2500 個 APP 接入,通過了的大用戶量的考驗。

3、下一步

接下來講說下一步的計劃,JSPatch 在 hotfix 上已經作得不錯,目前下一步打算推進使用 JSPatch 開發功能模塊。

JSPatch 作這個事情跟 React Nativeweex 這類方案比起來,會有一些優點:

  • 首先 React Native 和 weex 都是從前端出發擴展到終端,是前端方案的延伸,他們的體系對於前端來講更熟悉,對於終端來講,意味着要從新學習前端的一套知識,學習成本較高,而 JSPatch 是從終端出發,編碼體系也差很少是直譯 OC,學習成本較低。

  • 第二點是 ReactNative 和 Weex 是比較大型的框架,環境配置都很複雜,也會增大很多安裝包的大小,若是說只想擴展實現一兩個小功能,接入這麼大型的框架不合適。而 JSPatch 前面也說了,屬於微型框架,只有三個文件,也無需環境配置。

  • 第三點是 ReactNative 和 Weex 的組件都是要一個個封裝好,難以複用現有的 OC 組件,而且他們都是大型框架,在未成熟階段框架自己實現上的坑會不少,而 JSPatch 能夠直接複用全部 OC 現有組件,而且只是薄薄的轉接層,坑會較少。

但 JSPatch 要用於開發功能,有兩個問題:

  1. 開發效率較低
  2. 運行效率較低。

在開發效率上,我作了兩件事去提升,第一個是 JSPatchX 代碼補全插件 (https://github.com/bang590/JSPatchX)。

寫 JSPatch 代碼時並不像 OC 那樣有代碼補全,在調用 OC 長長的方法時效率會很低,並且用 JSPatch 寫功能時,不像 hotfix 那樣有對應的 OC 代碼,也沒法使用前面說的 JSPatchConvertor 進行轉換。因而作了 JSPatchX 去彌補這個缺陷,能夠在 XCode 自動提示補全 JSPatch 代碼。

另外一個是 Playground 即時刷新範例 (https://github.com/bang590/JSPatch/tree/master/Demo/iOSPlayground)

能夠實時預覽 JSPatch 腳本執行的結果,無需像原生代碼那樣每一次修改都要 build 重啓才能看到效果,這也是腳本語言的優點。使用者能夠仿照這個 playground 的實現,在開發功能時在本身的頁面實現這樣的即時刷新,這樣必定程度上提升了開發效率。

接下來看看運行效率。

JSPatch 寫功能時運行效率低,因而着手進行優化,第一步是肯定瓶頸,發現運行速度最慢的在於在 JS 調用 JS 上定義的新方法。

例如這裏新定義了一個dribbbleView類,裏面有個新方法renderItem,在 JS 裏調用這個新方法時,速度很慢。

分析下這個調用過程:

主要問題在於這個新定義的方法與 OC 掛鉤,這一次普通的調用,須要在 JS 和 OC 之間不斷來回通訊,不斷進行參數轉換,通過這9個步驟後才能成功調用。

對此我經過一些手段作了優化,把這樣的方法直接放在 JS 環境上,在 JS 調用這個方法時無需再與 OC 通訊,整個調用流程就變成了只有兩步:

通過這個優化後,這樣的方法調用性能最高提升 700 倍,這才使 JSPatch 寫功能變成一件靠譜的事。

除此以外還作了一些其餘優化,包括提高新增 property 性能,提供跟定義 OC 類同樣的純 JS 類定義接口,自動轉換參數類型等,具體優化細節能夠在這篇文章(http://blog.cnbang.net/tech/3123/) 上看到。

我用 JSPatch 寫了個 Dribbble 客戶端 demo (https://github.com/bang590/JSPatch/tree/master/Demo/DribbbleDemo)  在 iPhone5C 上測試過,滑動性能沒有問題。

最後,能夠從這個腦圖看出 JSPatch 的現狀,周邊設施仍在繼續建設中。

我今天的分享就到這裏,謝謝。

問答環節:

Q1: JSPatch 的底層原理跟 ReactNative 是否是差很少呢?有受到其啓發麼?

JSPatch 的原理跟 ReactNative 是徹底不同的,JSPatch 是 OC 方法調用和替換的一層轉接,ReactNative 並不會去調用和替換 OC 方法,它有本身的一套通訊規則。

Q2: 自己基於OC runtime 對 Swift 的項目如何支持?

Swift 相關問題在 wiki 裏有提到:


  1. 只支持調用繼承自 NSObject 的 Swift 類
  2. 繼承自 NSObject 的 Swift 類,其繼承自父類的方法和屬性能夠在 JS 調用,其餘自定義方法和屬性一樣須要加 dynamic 關鍵字才行。
  3. 若方法的參數/屬性類型爲 Swift 特有(如 Character / Tuple),則此方法和屬性沒法經過 JS 調用。
  4. Swift 項目在 JSPatch 新增類與 OC 無異,能夠正常使用。

Swift 的原生類目前沒找到替換的方法,動態調用卻是能夠實現。

Q3: JSPatch 運行一次就會把JS轉換爲 OC 緩存起來?那咱們能夠利用它去作一些重複調用的事情?甚至用來開發?它的效率和原生相近吧?

會緩存一些 methodSignature,但仍是得經過反射 (className->class->imp) 去找到要調用的方法,效率會比原生低。但通常程序的瓶頸不會在語言這裏。

Q4: 對於 JSPatch 資源更新服務平臺仍是表示一些擔心,若是被別人攻破了,豈不是不少 APP 都受牽連了?

JSPatch 平臺就算平臺被人黑了,也沒法對平臺上的 APP 下發惡意代碼。只要使用者用了自定義的 RSA 密鑰就能夠了,只有使用者有私鑰,每次發佈腳本都要使用這個私鑰,平臺不會保存它,詳情可見:http://JSPatch.com/DOCs/rsa

Q5: 如今 iOS 加快了審覈速度,好像如今是24小時內審覈上線。那如今 JSPatch 前景還會好麼?

審覈只是一個環節,測試/打包/發佈/用戶下載,這些其餘環節仍是不可少,而且最大的問題仍是是用戶下載更新不可控。

Q6: Swift 屬於靜態編譯類型,是否是能夠利用相似 c函數替換的方法呢?像 fishhook 這樣的工具

fishhook 須要編譯時肯定要替換的函數指針,並不能在運行時替換任意 c函數

Q7: 我看網上的一些介紹說 JSPatch 對小的 bug 修復好點,大的仍是提交新的版本,可是我看您介紹使用靜態資源服務器管理.應該不存在數據量大,併發的問題.這個您怎麼看?

他指的大的 bug 應該是要寫不少代碼才能修復的 bug 吧?這點應該跟 JSPatch 開發效率問題有關,對於大量的代碼他不想原生 OC 寫一套修復,再用 JSPatch 寫一套,跟數據量和併發應該沒什麼關係。

Q8: 爲什麼 JSPatch 上面,QQmail 沒有接入?有什麼顧慮嗎?

由於QQ郵箱在 JSPatch 出現以前已接入 lua,剛出現時 JSPatch 還不是很成熟,團隊當時想同時使用兩種方案做對比,時間久了也沒有再切換過來了。

Q9: 有沒有可能進一步提高 JSPatch convertor 的能力。最終發到直接打開 Xcode 項目,尋找依賴,經過語法語義分析等,將 OC 轉換爲 JS

能夠作到的,不過這事要投入很大精力,以前有搞過一個 demo,直接用 OC 寫 Patch,而後在執行前轉換成 JSPatch 代碼,有一個開源庫 JSTalk (https://github.com/ccgus/JStalk) 有基本的 OC->JS 的轉換,但要作到好用還有不少工做。

Q10: 請問若是個人 APP 引入了 JSPatch, 可是產生 crash 的代碼並非經過 JS 寫的, 而是原生的 OC 代碼, 那麼 JSPatch 能夠經過下發 JS 腳本修復這種 crash 嗎, 若是能夠的話, 原理是怎樣的?

能夠,原理就是把致使 crash 出現的方法替換掉,OC 調用那個方法時轉成調用 JSPatch 裏寫的替換的方法,就不會 crash 了

Q11: 有沒有意識到 JSPatch 的性能瓶頸最終都取決於 JavascriptCore 的性能?因此低端機永遠是低性能,有沒有想過借鑑 JSX 作點事情呢?

JavascriptCore 的性能並無問題,性能瓶頸不是 JavascriptCore,目前來看瓶頸會是 OC 與 JS 通訊時大對象的參數轉換,但這是能夠避免的

Q12: JSPatch 效率怎麼樣啊

效率能夠試試上文說的 dribbbleDemo

Q13: 調試 JSPatch 時能不能打斷點,如何定位到 JS 的 crash 堆棧

能夠斷點,文檔有寫:https://github.com/bang590/JSPatch/wiki/JS-%E6%96%AD%E7%82%B9%E8%B0%83%E8%AF%95

若是你們對本次分享還有問題,請按如下格式在DEV社區(dev.qq.com)發問答帖

發帖格式:【bang@DEV Club 你問我答】 「問題」

最後,歡迎你們關注 JSPatch 公衆號:JSPatchDev,會即時推送 JSPatch 最新信息以及相關技術文章:

更多精彩內容歡迎關注bugly的微信公衆帳號:

騰訊 Bugly是一款專爲移動開發者打造的質量監控工具,幫助開發者快速,便捷的定位線上應用崩潰的狀況以及解決方案。智能合併功能幫助開發同窗把天天上報的數千條 Crash 根據根因合併分類,每日日報會列出影響用戶數最多的崩潰,精準定位功能幫助開發同窗定位到出問題的代碼行,實時上報能夠在發佈後快速的瞭解應用的質量狀況,適配最新的 iOS, Android 官方操做系統,鵝廠的工程師都在使用,快來加入咱們吧!

相關文章
相關標籤/搜索