Slide 1

Slide 1 text

ReactNative製アプリがAndroidだと遅いと いう問題に立ち向かった話 株式会社stand.fm エンジニア 三堀 裕 2021/05/21 React Native Meetup #12 LT大会!

Slide 2

Slide 2 text

自己紹介 - 三堀 裕(みつほり ゆう) - ホリー - stand.fm所属(2021/01~) - Android, Flutterなどモバイル開発がメイン - ReactNativeはstand.fmで初めて触りました - 趣味:ダーツ、コーヒー、旅行、(麻雀) @1013Youmeee youmitsu @youmeee 2

Slide 3

Slide 3 text

- stand.fm Androidが重いという話 - パフォーマンスチューニングの話 a. react-navigationの画面遷移速度の改善 b. MaskedViewのレンダリング負荷への対応 c. Hermesの導入 d. setIntervalのclear漏れ - まとめ アジェンダ

Slide 4

Slide 4 text

- stand.fm Androidが重いという話 - パフォーマンスチューニングの話 a. react-navigationの画面遷移速度の改善 b. MaskedViewのレンダリング負荷への対応 c. Hermesの導入 d. setIntervalのclear漏れ - まとめ アジェンダ

Slide 5

Slide 5 text

stand.fmは誰でもかんたんに
 アプリで収録・LIVE配信ができる音声 プラットフォーム
 


Slide 6

Slide 6 text

stand.fm Androidが重いという話 - ユーザーの方々の声 - Androidアプリが重いのでどうにかしてほしい - 画面遷移が遅い(遷移するまで 10秒かかる) - 画面が固まる(fpsが落ちる) - etc... 6 パフォーマンス最適化のための改善をいくつか実施 今回はその中で4つ紹介します

Slide 7

Slide 7 text

- stand.fm Androidが重いという話 - パフォーマンスチューニングの話 a. react-navigationの画面遷移速度の改善 b. MaskedViewのレンダリング負荷への対応 c. Hermesの導入 d. setIntervalのclear漏れ - まとめ アジェンダ

Slide 8

Slide 8 text

8 react-navigationの画面遷移速度改善 
 react-navigationの画面遷移時のアニメーションをブロックしてしまうのが原因 
 - componentDidMountでの非同期処理(async-await) 
 - 最初にマウントされるコンポーネントの量が多い 
 
 
 1. componentDidMountのタイミングでawaitを使わない 
 2. ReactNativeのInteractionManagerを使う 
 3. 初期化時にmountされるコンポーネントを減らす 
 https://reactnavigation.org/

Slide 9

Slide 9 text

9 ReactNativeのInteractionManagerを使う 
 - InteractionManagerのrunAfterInteractionと呼ばれるコールバックを使用し、画面遷移アニメーション が終わってから初期化処理やレンダリングなどを実行させるようにする
 state = { afterInteractions: false, } componentDidMount = () => { this.interactionPromise = InteractionManager.runAfterInteractions(() => { this.setState({ afterInteractions: true }) }) } render () { return ( { this.state.afterInteractions && } ) }

Slide 10

Slide 10 text

10 hooksでrunAfterInteractionを使う 
 // カスタムフックを定義 export const useAfterInteractions = (func: () => any) => useEffect(() => { const interactionPromise = InteractionManager.runAfterInteractions(() => { func() }) return () => interactionPromise.cancel() }, []) const [afterInteractions, setAfterInteractions] = useState(false) useAfterInteractions(() => { setAfterInteractions(true) }) return ( { afterInteractions && } )

Slide 11

Slide 11 text

11 対応後の画面遷移速度の比較 
 - ボタンをタップしてからチャンネル画面への画面遷移が完了するまでの所要時間を計測(施行 回数:30回)
 - 平均で約300ミリ秒速度が改善
 平均: 1550ms
 平均: 1245ms
 約 300ms 改善

Slide 12

Slide 12 text

- stand.fm Androidが重いという話 - パフォーマンスチューニングの話 a. react-navigationの画面遷移速度の改善 b. MaskedViewのレンダリング負荷への対応 c. Hermesの導入 d. setIntervalのclear漏れ - まとめ アジェンダ

Slide 13

Slide 13 text

13 MaskedViewのレンダリング負荷への対応 
 - masked-viewというReactNativeでマスク処理をするライブラリ 
 - https://www.npmjs.com/package/@react-native-community/ 
 masked-view
 - Live画面のコメントリストの境界をぼかすために使っていた 
 - FlatListをラップする形 
 - MaskedViewが頻繁にレンダリングされると端末のリソースが 
 著しく増加してしまう問題があった 
 MaskedViewを表示させないように修正 


Slide 14

Slide 14 text

- 以下の3つの指標の改善に効果があった 
 - CPU使用率、メモリ使用量、バッテリー使用率 
 - フレームレートの改善にも効果があった 
 14 MaskedViewのレンダリングによるメモリ使用量の比較 
 Mask表示 Mask非表示 ←メモリ→ ←CPU→
 ←バッテリー→ ←ネットワーク→ 「1GBくらいまで消費→GC」を頻繁に繰り返している メモリ使用量が平坦になっている

Slide 15

Slide 15 text

- stand.fm Androidが重いという話 - パフォーマンスチューニングの話 a. react-navigationの画面遷移速度の改善 b. MaskedViewのレンダリング負荷への対応 c. Hermesの導入 d. setIntervalのclear漏れ - まとめ アジェンダ

Slide 16

Slide 16 text

16 Hermesの導入
 - stand.fm Androidでは、JavaScriptエンジンにJavaScriptCore(jsc)を使っていたが、Hermesに移 行した
 - Hermesのメリット
 - アプリサイズの削減(jscoreより約2MB削減) 
 - 起動速度の高速化
 - メモリ使用量の削減 
 - クラッシュフリーレートの改善にも効果があった 
 - 97.5%→99.5%
 - https://github.com/facebook/hermes 


Slide 17

Slide 17 text

- stand.fm Androidが重いという話 - パフォーマンスチューニングの話 a. react-navigationの画面遷移速度の改善 b. MaskedViewのレンダリング負荷への対応 c. Hermesの導入 d. setIntervalのclear漏れ - まとめ アジェンダ

Slide 18

Slide 18 text

18 setIntervalのclear漏れ(バグ)
 - これが一番の原因だった
 - 放送音声の再生位置の取得のため、200ミリ秒ごとにネイティブモジュールの再生位置を取得 するメソッドを呼び出していた(setInterval) 
 - clearIntervalが正常に行われていなかったことによって、放送画面がUnmountされたあともずっ と再生位置の取得処理が行われる 
 - 別の音声を再生するとまたinterval処理が新たに実行されるため、放送を再生した数の分アプ リが重くなる現象が起きていた
 - 最悪の場合「Excessive number of pending callbacks: 501.」が出てアプリが落ちる 
 clearIntervalをcomponentWillUnmount時に実行するように修正

Slide 19

Slide 19 text

- stand.fm Androidが重いという話 - パフォーマンスチューニングの話 a. react-navigationの画面遷移速度の改善 b. MaskedViewのレンダリング負荷への対応 c. Hermesの導入 d. setIntervalのclear漏れ - まとめ アジェンダ

Slide 20

Slide 20 text

まとめ - iOSに比べると、Android端末はスペック差によるパフォーマンスの影響を受けやすい - react-navigation使用時は画面遷移時のアニメーションをブロックしないようにする - InteractionManagerを使う。componentDidMount時にawaitを極力使わない - 特にAndroidではアニメーションやマスク処理周りは端末リソースに負荷がかかる場合がある ので注意が必要 - Hermesは良いぞ、clearInterval忘れに注意 - 他にもいくつかパフォーマンス改善 issueを計画中。ユーザーに気持ちよくアプリを使ってもらう ための改善を諦めずにやっていきたい 20

Slide 21

Slide 21 text

We are hiring! エンジニア積極的に募集中です https://corp.stand.fm/recruit 詳細はこちら ● CTO候補 ● VPoE候補 ● クライアントエンジニア ● バックエンドエンジニア ● 機械学習エンジニア ● 配信基盤エンジニア ● QAエンジニア ● エンジニアリングマネージャー ● UI/UXデザイナー 積極募集しているプロダクト開発メンバー

Slide 22

Slide 22 text

No content