編輯:關於Android編程
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進行渲染,得到我們最終的頁面。
了解了版本的整體流程,再簡單來看看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,回收所有內存。當然,這裡就有些老技巧了,無非是進程預加載和延遲銷毀。
最後,我們分享下幾個工具:
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還是相當優秀的解決方案的
上一篇面試總結,主要講了Activity 和Intent ,這篇主要講剩下的四大組件,面試中基礎較多的就是四大組件,需要了解的知識點也很多。上一篇文章 &nb
我們要理解Android的消息系統,Looper,Handle,View等概念還是需要從消息系統的基本原理及其構造這個源頭開始。從這個源頭,我們才能很清楚的看到Andro
前言很多人要實現輪播圖都會想到使用ViewPager + Handler來完成輪播圖的效果。但是在RxJava快速發展的情況下,已經可以使用RxJava來代替Handle
提醒一下,更新的操作不要放到Application中,有的人想程序啟動的時候就馬上檢查更新,然後就把更新的操作放在了Application中,Application不是可