本文來自於騰訊bugly開發者社群,非經作者同意,請勿轉載,原文地址:http://dev.qq.com/topic/577e16a7640ad7b4682c64a7
“8小時內拼工作,8小時外拼成長”這是大家共同的理想。除了每天忙於工作外,我們都希望能更多地區吸收領域內的新知識與新技能,從而走向人生巔峰。
Dev Club 是一個交流移動開發技術,結交朋友,擴充套件人脈的社群,成員都是經過稽核的移動開發工程師。每週都會舉行嘉賓分享,話題討論等活動。
上一期我們邀請了騰訊SNG工程師“王少鳴”分享了《React Native專案實戰總結》。
2016-07-14,下週四,我們將邀請騰訊WXG iOS開發工程師“姚海波”為大家分享《微信讀書iOS效能優化》。
。
如何加入 Dev Club?
移動端開發經驗 >= 2 年,微信掃描下方群管理微信二維碼,備註姓名-公司(或產品) 申請加入。
—————-下面是上一期分享內容整理—————-
內容簡介:
你是否想體驗Web一樣的釋出節奏來發布終端的特性?
你是否想低成本讓先前的H5特性擁有Native一樣的流暢體驗?
快來一起擁抱ReactNative吧!
分享人介紹:
王少鳴,社交平臺部工程師。對前端系統的設計和開發有豐富的經驗,先後從事PC Qzone前端開發,小Q機器人智慧終端開發及微信群,手機Qzone終端開發等工作。
Hello,大家晚上好,我是來自QQ空間的王少鳴,大家可以叫我mango,目前主要負責 Qzone 玩吧業務,專注Hybrid開發,[QQJsSDK] 研發負責人,專注ReactNative在Qzone與手Q的應用和推廣。照片裡的這個人就是我,上週在GMTC分享拍的,請大家多多指教。
接下來進入正題,請大家先認識下這幾個簡寫:
fb = Facebook
rn = ReactNative
jsc = Javascript Core
cxt = Context
ReactNative 讓開發者使用 JavaScript 和 React 編寫應用,利用相同的核心程式碼就可以建立 基於Web,iOS 和 Android 平臺的原生應用。Facebook 在2015.9.15釋出了 ReactNative for Android,把JavaScript 開發技術擴充套件到了Android平臺,至此已覆蓋當流主流平臺。
目前ReactNative的版本節奏大概是兩週一個版本,空間從11的版本便開始嘗試接入,第一個上線的版本是15的版本,後面我們升級到20的版本,由於一些歷史包袱的原因,導致我們升級並不能隨時跟隨fb的升級節奏。手Q方面目前使用的是17的版本。
我們從fb rn的官網中的showcase頁面可以看到,目前已經有大量的app接使用了rn的技術,當前,還能看到我們公司的不少app,如QQ,Qzone,QQ音樂,全民k歌等,這個大家如果有興趣想要把自己的app放到官網上,只需要要向fb提交一個pr即可。
目前在空間有三個業務使了用rn進行開發,有話題圈,情侶空間,結合版留言版等。在手Q方面,有訊息流和資料卡等,當然,還有全民k歌的一些活動頁。
接下來我們來對比下rn與原生開發或hybrid開發有些哪優劣勢。
優勢:
-
原生控制元件的體驗,Web的版本節奏,Web的開發效率,跨平臺等
劣勢:
-
版本支援度 Android 4.1 (API 16) & iOS 7.0
- 專案尚不成熟,容易Crash,部分機器存在相容性問題
- 效能,在高低端機呈現兩極
- Android僅基於Gradle,目前業界較多大平臺專案均基於 Ant,如Qzone,手Q等
特別是最後一點,這個也是我們前面說的,版本升級不能跟隨fb rn版本的原因,我們的每次升級都需要將rn原始碼進行改造和合並,介面適配等,對開發有一定的額外的工作量。
接下來,我們來看下,使用rn開發的版本整體流程。
首先,我們的app整合了rn的sdk,另外,jsbundle也就是我們使用rn開發的上層業務邏輯程式碼。我們在釋出app時,我們會內建一份。當上層業務邏輯變化時,我們會重新向我們的cdn釋出一份新的jsbundle。在app啟動時,我們檢查當時外網有沒有jsbundle需要更新,如果有,我們們更新並存放本地。在點選應用入口時,我們會優先使用新下載的這份檔案,否則使用內建的,最後通過JSC進行渲染,得到我們最終的頁面。
ok,瞭解了版本的整體流程,再簡單來看看rn原理。
前面我們講到了jsc,那jsc就是橋接web<>native的一個元件,在一定意義上等同於我們的瀏覽器核心。那講完jsc,那原理就比較容易理解了,就是通過jsc去解析我們的jsbundle,並將資訊傳遞給native,最後由native不斷去處理來自js層的呼叫,最終得到我們的native頁面。
網上分析rn在iOS的文章也比較多了,我這邊也不多講,主要講下在android方面的。
主要分為三層:
- java層:主要有ReactCore,主要處理與js的互動,還有處理圖片的fresco,網路庫okhttp,還有一些support和utils等。
- C++層:主要有我們的jsc,bridge,,jsloader等
- JS層:這層相對大家就比較熟悉了,主要是包含元件的邏輯處理和一些佈局,當然,這些佈局資訊最終也是轉成我們終端的佈局模型。
接下來,我們來看一下js跟native的通訊機制。
這塊內容其實是rn裡很重要的內容,這塊主要是流程比較長,我們今天就簡單講一下,詳細的有興趣的同學,可以去公眾號看我之前寫文章,或者直接找我。
先來看看從java層到js層的呼叫。
我們的業務邏輯其實是在js裡面,那就這裡出現了我們的啟動邏輯,這裡其實就是java層到js層呼叫的一個例子,由java去呼叫js的某個啟動函式。
那js層到java層的呼叫就更多了,比如像點選圖片點看大圖浮層,這裡更多是業務性質的呼叫。
當然,還有另外一個可能大家比較關注的問題,就是前端寫的標籤,比如我們的 這樣一個元件,是怎麼轉換到終端的一個view?這裡其實原理也很簡單,js層會將控制元件標籤轉換成js對終端UI模組的一次呼叫,如比像這種UIManager.creaeView或者UIManager.removeView我們無論是java到js還是js到java,中間都必須經過我們的jsc進行橋接。
上一張稍微複雜點的呼叫鏈
這個講起來時間稍微有點長,其實就是說明標籤到控制元件的一個完整呼叫鏈,有興趣的同學我們下來再討論。
好了,前面原理講得比較多,因為可能有先同學先前沒接觸過rn,我這裡就先講些基礎知識。接下來我們講些可能大家比較感興趣的,就是我們踩坑和填坑之路。
那在接下rn之前,我們會遇到了下面幾個問題:
- 包大小:Android HelloWorld工程 約7m
- 穩定性:業界尚未有真實外網資料,內部僅能通過大量機器進行穩定性測試,但未能覆蓋所有機型
- 安全性:Jsbundle可能發生被攔截等情況,容易導致Native Crash
- 相容性:Android 4.1 (API 16) & iOS 7.0 Jsbundle與Native版本相容 支援版本對機型相容
- 效能:業界尚未有真實外網資料,僅靠實驗室有限資料支撐
基於上面這幾個問題,我們決定第一個版本,使用rn來改造之前的情侶空間業務(H5),我們使用獨立外掛的技術來實現。
獨立外掛通過非同步下載,並且,我們使用獨立程式來承載它防止拖垮主程式,最後,再加個雲開關,假如外網有問題可以隨時將rn切到h5。
這裡第一個專案的細節就不細講了,在這個專案,主要是想通過這個第一涉水專案,我們發現了哪些問題,以及我們是怎麼解決的。主要是首屏方面,我們通過實驗log資料,得知wifi下,首屏將近6s才能完全渲染出來。其次,fps主觀上沒明顯示卡頓,中低端機對比老版本下降明顯,這個兩個問題。
接下來,我們決定進行第二個業務話題圈使用rn進行改造。背景是話題圈先前是h5的,裡面的視訊元件無法支援自己播放及續播相關邏輯,那元件開發相開發時間關係這裡就不細講,有興趣大家可以下來討論。
那我們將先前情侶空間的log進行分塊,主要是 外掛及程式啟動 2.1s+RN上下文啟動+1.1s 首屏資料+2s Render 0.9s。接下來我們分塊解決這些問題。
外掛及程式啟動:消滅這份耗時其實比較容易,rn和Qzone整合並綁在主程式即可。但是整合我們遇到一個問題就是Qzone是基本ant的構建,rn是基於gradle的構建,兩者無法直接融合,那隻能是將rn改造ant的構建。(qzone改造gradle代價比較大,週期長)關於更改構建這裡,大家可以去看我km的一篇文章即可,時間有限,便不細講了。
那我們話題圈最終改造的計劃是:
ant改造rn -> 整合到Qzone -> 業務開發 -> 效能優化
時間問題,我們著重看效能優化方面的知識,可能有些我也講得不深,也有些針對性,希望能給大家帶一些新啟發更關鍵。
整合到Qzone主要是方法數的問題,那業務開發,我簡單講一個點,就是在開發前,終端同學需要與前端同學定製好你們業務的所有介面。這裡我有個建議,形參建議只保留兩個,一個是你們業務資料的json,另外一個就是cb。因為rn在介面呼叫方法,如果引數個數對不上,會直接導致應用crash。
那我們接下來看效能優化方面的東西,我們這裡已經將Qzone與rn進行整合了,那關於外掛啟動和程式啟動的耗時已經可以消除,那接下來我們看下,怎麼去消除首屏和上下文的耗時。
那我們先來看一下,一個傳統的啟動流程:
點選入口 -> native cxt -> web cxt -> 前端發起資料請求 -> 回包 -> 渲染
很明顯,這裡的流程是序列的,那我們是不是把一些東西並行起來,或者提前先做呢?
和其他啟動優化類似,還是記憶體換時間,我們這裡加了預載入的邏輯。
預載入啥呢?我們這裡預載入的是native cxt。假如你們的業務在二級頁的話,我覺得預載入是沒啥問題的,當然在主頁面或者整個app都是用rn的話,可能就得換種思路了。
另外資料的話我們是這樣做的。首先,我們給前端提供一個資料模組,這個模組提供可以讀寫本地資料的介面。我們點選入口的時候,終端會先去拉後臺資料並存到sdcard,那當我們點選入口,前端再使用的我們提供的資料模組,讀取快取的資料進行渲染。當時,我們前端還是照樣會發請求給後臺拉最新的資料,隨後覆蓋到sdcard,假如資料有更新話,前臺頁面再重新渲染。最終我們的首屏定格在1.5s左右。(由於話題圈頁面相對比較重,我們之前使用的是wns-html技術,這個資料其實已經超越了之前該頁面的首屏速度)
那首屏講完了,我們來看下FPS,對比我們之前的情侶空間,我們這次FPS同樣有很多的提升,其實很多的優化思路我們是從前端優化思想中借鑑過來,像FPS就是,我們做了以下的優化:
UI方面:
- 減少View層級巢狀
- 合理設定背景色透明
JS方面:
- JS層使Listview控制元件渲染資料,廢棄使用ScrollView控制元件
- 避免滑動做過多事情,減少JS執行緒掉幀
最終FPS基本定格在53-54左右。
我們最後再來看一下包精簡的方案。原始的rn接近7m左右,那裡面是不是有些優化空間呢?很多人在說,我們平臺也有自己的網路庫,真得還得使用rn裡面的okhttp麼?圖片庫也是,不能複用麼?其實是可以,我們一起來看下包精簡。那包精簡主要分為下面4部分:
SO:
- 放棄對x86的支援
- 借用TBS能力,移除JSC
Java:
- 去除暫時不需要Module&UIManager
- Release 去除Dev Support
Jar:
- 對接平臺網路庫(WNS,MSF),移除OKHTTP
- 對接平臺圖片庫(ImageManager),移除Fresco
-
平臺 Support 閹割庫 複用補齊
Res:
-
移除無用的Res檔案,language val
-
平臺 Support Res複用補齊
接下來,我們來看下crash方面的一些小經驗。我們從三方面來分析crash。
- 先來看看so層的crash,這層的crash會比較被動,因為基本沒辦法去解決,一般都只能等官方來更新解決,先前我們碰到一個處理cookie的crash,其實我們並沒有呼叫那個api,但系統自己啟動呼叫了,我們當時的解決方案是將java的呼叫入口註釋了。當然,還有一些so相容的crash等等。
- 那java層的crash相對還好,原始碼在手,天下任走,這塊我們可以隨意修復,但建議是自己一定要把修改備註好,否則後面升級等都是災難。
-
最後來看看js層的crash,前面幾天我在gmtc分享的時候,就有個兄弟專門過來我問了我這個問題,他們遇到了js層一些屬性轉換的問題,並且直接表現就是實然crash。那這個問題,其實也困攏了我很久,我這邊也沒太好的解決方案,我們和前端同學定位過,屬性型別肯定是沒問題的,最後我們時間問題,解決方案是改動原始碼。原始碼在處理屬性等問題上,稍微一言不和就直接throws exception,那我們這裡臨時的解決方案也只能是將throws改動log的標記,臨時解決然後靜等升級。
這裡再稍微講一下,我們在手Q方面的一些實踐,當然這塊參與的同學比較多了,這些也不單單由我一個人完成的。先來看一下我們這邊又做了哪些優化。
主要有以下兩方面:
- 分包。前面我們講預載入的知識。我們當時預載入的僅是native cxt,那我們可不可以把前端cxt也載入了呢?其實肯定是可以 ,我們試過直接預載入到整一個view的級別,但是,這樣會增加近40m的記憶體,當然是秒開,但是還是要保證記憶體和首屏的權衡。後來,我們進一步思考,假如後續業務眾多,每次業務切換是否每次都去重新載入那個1m多的jsbundle呢?後來,我們有了另外一個方案,就是將jsbundle進行拆分為base和busi兩個zip。base我們進行預載入,並且,我們在業務切換的時候,我們會進行復用,能更有效地提高首屏的速度。
-
獨立程式操作so。這個主要是因為手Q對記憶體要求比較嚴格,在介面退出的時候所有連帶記憶體必須清理乾淨。那rn在這方面其實做的並沒有那麼好,有部分so層的記憶體一直佔用著。那我們解決方案只能是將JNI拆分為獨立程式並且在介面退出時,直接退出dalvik,回收所有記憶體。當然,這裡就有些老技巧了,無非是程式預載入和延遲銷燬。
接下來,我們看下整體在手Q這邊次優化後,我們整體的資料對比
最後,我們分享下幾個工具:
ReactNative Tools
- adb logcat *:S ReactNative:V ReactNativeJS:V
- Developer menu.
System Tools
- 除錯GPU過度繪製
- GPU呈現模式
Android Tools
- TraceView
- Memory Monitor
- TraceView
-
SysTrace
好了,今天分享就到這了,感謝大家耐心聽我叨叨了這麼多,謝謝大家,如果大家有疑問的話,可以按序發在群裡,能力範圍內的我按順序回覆大家,感謝!
問答環節
Q1:rn中用到js實現的邏輯容易出現效能問題嗎?
A1:效能瓶頸還是在終端上,前端目前暫沒發現嚴重的效能問題。
Q2:目前的空間和QQ都是部分使用RN,後面會繼續擴大使用範圍嗎?
A2:肯定會的。rn目前是快速成長期,我們對rn有長期的歸劃。同樣,希望更多兄弟團隊一起來推動rn的成長,一起來貢獻一份力
Q3:之前聽到外界有團隊抱怨 RN 相對原生來說元件太少,反而加大了開發量,什麼都要自己來,現在是否有改善了呢?
A3:目前來看,其實元件是滿足大部分開發者的,除非像某特些定製化的元件,比如像emojtext這種,我們才需要自己去自定義,當然,現在git上面也很多rn的元件了,應該能滿足你
Q4:剛才分享的一些crash的例子好像都會導致rn直接崩掉,那你們用什麼手段來定位究竟是那段js程式碼導致問題呢?
A4:這裡還要講明一點。主是掌握原始碼才有真正的主動權。很多問題,我們也都是去閱讀原始碼發現的。其實原始碼並不複雜,裡面很多知識沉澱,我個人是非常建議去讀原始碼的。
Q5:我是做安卓的,不知道要不要嘗試一下這個新技術? 現在團隊做一些應用也不是很重,安卓和ios搞兩套實在有些蛋疼
A5:android iOS兩套這個問題,其實只是某些基礎元件屬於平臺特有,但大部分邏輯還是有共性的。
Q6:目前rn發展還未穩定,預測一下未來是否會大規模應用並取代傳統的native開發,真正實現一次編寫,到處執行?
A6:動態更新其實這個問題很早前就有人提出,像外掛啊,熱更新其實目的也是一致的,rn只是另外一鍾思路,weex也類似。完全取代native 開發個人覺得不太現實,因為像rn還是需要native的一些開發工作的。
Q7:自定義ui元件需要對rn適配嗎?
A7:需要。但適配工作量並不高,僅對接相應介面,暴露一些屬性即可
Q8:RN整套框架是基於原生構建的,UI介面升級的時候整套框架能夠及時保持相容嗎?
A8:這裡還要說明一下,jsbundle的版本跟native的版本其實是不相容的。這裡建議jsbundle的url都使用後臺下發。另外,介面在升級後可能會有些改變,這裡需要前終端一起配合
Q9:rn跨平臺的話是不是還是需要維護兩套js,只是有些元件可以跨平臺共用呢?
A9:其實維護的js程式碼是一套。但可能在構建成特別平臺的jsbundle前有小小的修改。大部分元件還是可以複用的,除了平臺特有的,比如像actionbar這種
Q10:關於使用rn遇到的crash情況,你覺得rqd可以在哪些方面優化,幫助更好解決問題?
A10:目前業務使用rn的大平臺還比較少,其實很多crash我們也在放量外網之後才發現的。這裡我比較建議是在使用rn前,先大致讀下rn的原始碼,並正式釋出前多加一層保險開關。當前,目前就qzone使用的版本我也提交了一些pr,有些也收錄了,後面相信 crash等會越來越少的
Q11:從Android的機型問題看,最怕的就是平臺還不成熟,並且還開源。rn會不會重蹈覆轍?
A11:rn目前是開源的。目前開發者社群都是高活躍,應該不會存在kpi專案之類的問題,並且動態更新肯定是趨勢,我覺得可能會有其他方案,但暫時來看,rn還是相當優秀的解決方案的
更多精彩內容歡迎關注bugly的微信公眾賬號:
騰訊 Bugly是一款專為移動開發者打造的質量監控工具,幫助開發者快速,便捷的定位線上應用崩潰的情況以及解決方案。智慧合併功能幫助開發同學把每天上報的數千條 Crash 根據根因合併分類,每日日報會列出影響使用者數最多的崩潰,精準定位功能幫助開發同學定位到出問題的程式碼行,實時上報可以在釋出後快速的瞭解應用的質量情況,適配最新的 iOS, Android 官方作業系統,鵝廠的工程師都在使用,快來加入我們吧!