$30 off During Our Annual Pro Sale. View Details »

ReactNative製アプリがAndroidだと遅いという問題に立ち向かった話/the problem that React Native apps are slow on Android

ReactNative製アプリがAndroidだと遅いという問題に立ち向かった話/the problem that React Native apps are slow on Android

stand.fm

May 21, 2021
Tweet

More Decks by stand.fm

Other Decks in Programming

Transcript

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  5. stand.fmは誰でもかんたんに

    アプリで収録・LIVE配信ができる音声
    プラットフォーム


    View Slide

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

    View Slide

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

    View Slide

  8. 8
    react-navigationの画面遷移速度改善

    react-navigationの画面遷移時のアニメーションをブロックしてしまうのが原因

    - componentDidMountでの非同期処理(async-await)

    - 最初にマウントされるコンポーネントの量が多い



    1. componentDidMountのタイミングでawaitを使わない

    2. ReactNativeのInteractionManagerを使う

    3. 初期化時にmountされるコンポーネントを減らす

    https://reactnavigation.org/

    View Slide

  9. 9
    ReactNativeのInteractionManagerを使う

    - InteractionManagerのrunAfterInteractionと呼ばれるコールバックを使用し、画面遷移アニメーション
    が終わってから初期化処理やレンダリングなどを実行させるようにする

    state = {
    afterInteractions: false,
    }
    componentDidMount = () => {
    this.interactionPromise = InteractionManager.runAfterInteractions(() => {
    this.setState({ afterInteractions: true })
    })
    }
    render () {
    return (

    { this.state.afterInteractions && }

    )
    }

    View Slide

  10. 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 && }

    )

    View Slide

  11. 11
    対応後の画面遷移速度の比較

    - ボタンをタップしてからチャンネル画面への画面遷移が完了するまでの所要時間を計測(施行
    回数:30回)

    - 平均で約300ミリ秒速度が改善

    平均: 1550ms
 平均: 1245ms

    約 300ms 改善

    View Slide

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

    View Slide

  13. 13
    MaskedViewのレンダリング負荷への対応

    - masked-viewというReactNativeでマスク処理をするライブラリ

    - https://www.npmjs.com/package/@react-native-community/ 

    masked-view

    - Live画面のコメントリストの境界をぼかすために使っていた

    - FlatListをラップする形 

    - MaskedViewが頻繁にレンダリングされると端末のリソースが

    著しく増加してしまう問題があった

    MaskedViewを表示させないように修正

    View Slide

  14. - 以下の3つの指標の改善に効果があった

    - CPU使用率、メモリ使用量、バッテリー使用率 

    - フレームレートの改善にも効果があった

    14
    MaskedViewのレンダリングによるメモリ使用量の比較

    Mask表示 Mask非表示
    ←メモリ→
    ←CPU→

    ←バッテリー→
    ←ネットワーク→
    「1GBくらいまで消費→GC」を頻繁に繰り返している メモリ使用量が平坦になっている

    View Slide

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

    View Slide

  16. 16
    Hermesの導入

    - stand.fm Androidでは、JavaScriptエンジンにJavaScriptCore(jsc)を使っていたが、Hermesに移
    行した

    - Hermesのメリット

    - アプリサイズの削減(jscoreより約2MB削減) 

    - 起動速度の高速化

    - メモリ使用量の削減 

    - クラッシュフリーレートの改善にも効果があった

    - 97.5%→99.5%

    - https://github.com/facebook/hermes

    View Slide

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

    View Slide

  18. 18
    setIntervalのclear漏れ(バグ)

    - これが一番の原因だった

    - 放送音声の再生位置の取得のため、200ミリ秒ごとにネイティブモジュールの再生位置を取得
    するメソッドを呼び出していた(setInterval)

    - clearIntervalが正常に行われていなかったことによって、放送画面がUnmountされたあともずっ
    と再生位置の取得処理が行われる

    - 別の音声を再生するとまたinterval処理が新たに実行されるため、放送を再生した数の分アプ
    リが重くなる現象が起きていた

    - 最悪の場合「Excessive number of pending callbacks: 501.」が出てアプリが落ちる 

    clearIntervalをcomponentWillUnmount時に実行するように修正

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  22. View Slide