[譯] 測試原生,Flutter 和 React Native 移動開發之間的效能差異

Shawnlee發表於2018-08-02

確定你們公司的移動應用程式是真正的原生應用還是採用跨平臺方法實現(如 React NativeFlutter)是一個很艱難的決定。經常會考慮的一個因素是速度問題 —— 我們都普遍認為大多數跨平臺方法比原生方法慢,但是很難說出具體的數字。因此,當我們考慮效能時,我們常常會靠直覺,而不是具體的資料。

因為希望在上述效能分析中新增一些結構,以及對 Flutter 如何實現其效能承諾的興趣,我決定構建一個非常簡單的應用程式分別對應原生版本,React Native 版本以及 Flutter 版本,進而比較他們的效能。

測試應用

我構建的應用程式儘可能簡單,同時確保至少仍能提供一些資訊。它是一個計時器應用 —— 具體來說,該應用程式顯示隨著時間的推移計數的一團文字。它顯示自應用程式啟動以來經過的分鐘數、秒數和毫秒數。相當簡單。

下面是它初始狀態的樣子:

[譯] 測試原生,Flutter 和 React Native 移動開發之間的效能差異

這是 1 分鐘 14 秒 890 毫秒後的樣子:

[譯] 測試原生,Flutter 和 React Native 移動開發之間的效能差異

鉚。

但是為什麼選計時器?

我選擇計時器應用有兩個原因:

  1. 它在每個平臺上都很容易開發。這個應用程式的核心是某種型別的文字檢視和重複計時器,很容易翻譯成三種不同的語言和堆疊。
  2. 它表明了底層系統在螢幕上繪製內容的效率。

讓我們看一看程式碼

幸運的是,這個應用足夠小,我可以直接在這裡新增相關程式碼。

原生 Android 應用

以下是原生 Android 應用的 MainActivity:

class MainActivity : AppCompatActivity() {

  val timer by lazy {
    findViewById<TextView>(R.id.timer)
  }

  override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    setContentView(R.layout.activity_main)
    initTimer()
  }

  private fun initTimer() {
    val startTime = elapsedRealtime()
    val handler = Handler()
    val runnable: Runnable = object: Runnable {
      override fun run() {
        val timeDifference = elapsedRealtime() - startTime
        val seconds = timeDifference / 1000
        val minutes = seconds / 60
        val leftoverSeconds = seconds % 60
        val leftoverMillis = timeDifference % 1000 / 10
        timer.text = String.format("%02d:%02d:%2d", minutes, leftoverSeconds, leftoverMillis)
        handler.postDelayed(this, 10)
      }
    }

    handler.postDelayed(runnable, 1)
  }
}
複製程式碼

React Native

這是 React Native 應用程式的 App.js 檔案:

export default class App extends Component {

  render() {
    return (
      <View style={styles.container}>
        <Timer />
      </View>
    );
  }
}

class Timer extends Component {
  constructor(props) {
    super(props);
    this.state = {
      milliseconds: 0,
      seconds: 0,
      minutes: 0,
    }

    let startTime = global.nativePerformanceNow();
    setInterval(() => {
      let timeDifference = global.nativePerformanceNow() - startTime;
      let seconds = timeDifference / 1000;
      let minutes = seconds / 60;
      let leftoverSeconds = seconds % 60;
      let leftoverMillis = timeDifference % 1000 / 10;
      this.setState({
        milliseconds: leftoverMillis,
        seconds: leftoverSeconds,
        minutes: minutes,
      });
    }, 10);
  }

  render() {
    let { milliseconds, seconds, minutes } = this.state;
    let time = sprintf("%02d:%02d:%2d", minutes, seconds, milliseconds);
    return (
      <Text>{time}</Text>
    )
  }
}

const styles = StyleSheet.create({
  container: {
    flex: 1,
    justifyContent: 'center',
    alignItems: 'center',
    backgroundColor: '#F5FCFF',
  }
});
複製程式碼

Flutter

最後這是我們的 Flutter main.dart 檔案:

void main() => runApp(new MyApp());

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return new MaterialApp(
      home: new MyHomePage(),
    );
  }
}

class MyHomePage extends StatefulWidget {
  MyHomePage({Key key}) : super(key: key);

  @override
  _MyHomePageState createState() => new _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  int _startTime = new DateTime.now().millisecondsSinceEpoch;
  int _numMilliseconds = 0;
  int _numSeconds = 0;
  int _numMinutes = 0;

  @override
  void initState() {
    super.initState();
    Timer.periodic(new Duration(milliseconds: 10), (Timer timer) {
      int timeDifference = new DateTime.now().millisecondsSinceEpoch - _startTime;
      double seconds = timeDifference / 1000;
      double minutes = seconds / 60;
      double leftoverSeconds = seconds % 60;
      double leftoverMillis = timeDifference % 1000 / 10;
      setState(() {
        _numMilliseconds = leftoverMillis.floor();
        _numSeconds = leftoverSeconds.floor();
        _numMinutes = minutes.floor();
      });
    });
  }

  @override
  Widget build(BuildContext context) {
    return new Scaffold(
        body: new Center(
          child: new Text(
            sprintf("%02d:%02d:%2d", [_numMinutes, _numSeconds, _numMilliseconds]),
          ),
        )
    );
  }
}
複製程式碼

每個應用程式都遵循相同的基本結構 —— 它們都有一個計時器,每 10 毫秒重複一次,並重新計算自計時器啟動以來經過的分鐘數、秒和毫秒數。

我們如何測量效能?

對於那些不熟悉 Android 開發的人來說,Android Studio 是構建 Android 應用程式的首選編輯器/環境。它還附帶了一系列有用的分析器來分析你的應用程式 —— 具體來說,它有一個 CPU 分析器,一個記憶體分析器和一個網路分析器。所以我們將使用這些分析器來判斷效能。所有測試都在 Thoughtbot 的 Nexus 5X 和我自己的第一代 Google Pixel 上執行。React Native 應用程式將在 --dev 標誌設定為 false 的情況下執行,Flutter 應用程式將在 profile 配置中執行,以模擬釋出應用程式而不是 JIT 編譯的除錯應用程式。

給我看資料!

到了這篇文章最有趣的部分了。讓我們看一下在 Thoughtbot 辦公室的 Nexus 5X 上執行時的結果。

Nexus 5X 上面原生應用的結果

[譯] 測試原生,Flutter 和 React Native 移動開發之間的效能差異

Nexus 5X 上面 React Native 應用的結果

[譯] 測試原生,Flutter 和 React Native 移動開發之間的效能差異

Nexus 5X 上面 Flutter 應用的結果

[譯] 測試原生,Flutter 和 React Native 移動開發之間的效能差異

這些結果首先表明的是,當涉及到效能時,原生 Android 應用程式勝過 React Native 和 Flutter 應用程式可不是一點半點。原生應用程式上的 CPU 使用率不到 Flutter 應用程式的一半,與 React Native 應用程式相比,Flutter 佔用的 CPU 更少一些,但是差別不大。原生應用程式的記憶體使用率同樣很低,並且在 React Native 和 Flutter 應用程式上記憶體使用率都有所增加,不過這次 React Native 應用表現得比 Flutter 應用更好。

下一個有趣的內容是 React Native 和 Flutter 應用程式在效能上是如此相近。雖然這個應用程式無疑是微不足道的,但我原本以為 JavaScript 橋接器會受到更多的影響,因為應用程式如此快速地通過該橋接器傳送瞭如此多的訊息。

現在讓我們看看在 Pixel 上測試時的結果。

Pixel 上面原生應用的結果

[譯] 測試原生,Flutter 和 React Native 移動開發之間的效能差異

Pixel 上面 React Native 應用的結果

[譯] 測試原生,Flutter 和 React Native 移動開發之間的效能差異

Pixel 上面 Flutter 應用的結果

[譯] 測試原生,Flutter 和 React Native 移動開發之間的效能差異

所以,我立馬就對 Pixel 上顯然更高的 CPU 佔用感到驚訝。它肯定是比 Nexus 5X 更強大(在我看來就是更流暢)的手機,所以我自然而然假設同一應用程式的 CPU 利用率將_更低_,而不是更高。我可以理解為什麼記憶體使用會更高,因為 Pixel 上有更大的記憶體空間而且 Android 上遵循一條“使用它或者浪費它”的策略來保持記憶體。如果讀者中有任何人知道的話,我很想了解一下為什麼 CPU 使用率會更高!

第二個有趣的收穫是,Flutter 和 React Native 與原生應用相比在他們的優勢和劣勢方面有了_更明顯_的差別。React Native 只比原生應用程式佔用的記憶體略微高一點,而 Flutter 的記憶體使用率比原生應用程式高出近 50%。另一方面,Flutter 應用程式更接近於原生應用程式的 CPU 使用率,而 React Native 應用程式則難以保持低於 30% 的 CPU 使用率。

最重要的是,我對 5X 和 Pixel 之間結果的差異之大感到驚訝。

結論

我可以很有信心地說原生 Android 應用的效能優於 React Native 應用或 Flutter 應用。不過,我_沒有_信心說 React Native 應用將表現得比 Flutter 應用更好,反之亦然。還需要做更多的測試才能弄清楚 Flutter 是否能真正提供比 React Native 更高的真實效能。

注意事項

上面所做的分析是並不是最終結果。我執行的一小部分測試不能用來表示 React Native 比 Flutter 更快或者相反。它們只應被解釋為分析跨平臺應用程式這個大問題的一部分。還有很多這個小應用程式沒有觸及的東西會影響現實世界的效能和使用者體驗。值得指出的是,在 debug 模式和 release 模式下,所有三個應用程式都執行順暢。

如果發現譯文存在錯誤或其他需要改進的地方,歡迎到 掘金翻譯計劃 對譯文進行修改並 PR,也可獲得相應獎勵積分。文章開頭的 本文永久連結 即為本文在 GitHub 上的 MarkDown 連結。


掘金翻譯計劃 是一個翻譯優質網際網路技術文章的社群,文章來源為 掘金 上的英文分享文章。內容覆蓋 AndroidiOS前端後端區塊鏈產品設計人工智慧等領域,想要檢視更多優質譯文請持續關注 掘金翻譯計劃官方微博知乎專欄

相關文章