Ccmmutty logo
Commutty IT
4 min read

【Flutter】非同期処理と並列処理でUI描画に与える影響を比較してみた

https://cdn.magicode.io/media/notebox/c06948a7-257e-4b86-8a63-cb4f10a8c65d.jpeg

はじめに

Flutterを利用する上でよく話題に上がる「画面がカクつく」問題。
今回は「並列処理を使うことで解決するかも?」の紹介をします。

ざっくり結論を言うと

  • Flutterの言語であるDartはUI描画・計算含め基本的に全てシングルスレッドで動作するよ
  • 重い処理をしてしまうとUI描画に影響が出てカクつきが生じるよ
  • 非同期処理は同じスレッドで動きカクつきの解消はできないよ
  • 並列処理は処理を別スレッドにでき、カクつきの解消になる可能性があるよ

非同期処理と並列処理の比較方法

非同期処理と並列処理の比較のため、円形のプログレスインジケーターを利用します。
  1. 処理を開始
  2. 円形のプログレスインジケーターを表示
  3. 処理が完了したら結果を表示 を行い、非同期処理と並列処理のそれぞれで2.がどのように見えるか確認します。

元になるコード

下記コードの関数Aに非同期関数や並列処理関数を設定して進めます。 設定する関数は下部の方に書いてあります。
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';

void main() {
  runApp(const MyApp());
}

class MyApp extends StatelessWidget {
  const MyApp({Key? key}) : super(key: key);

  // This widget is the root of your application.
  
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: FibonacciWidget(),
    );
  }
}

class FibonacciWidget extends StatelessWidget {
  
  Widget build(BuildContext context) {
    return FutureBuilder(
        future: 関数A, //今回はここを変えます
        builder: (_, snapshot) {
          if (!snapshot.hasData) {
            return Scaffold(
              appBar: AppBar(
                title: const Text("fibonacci"),
              ),
              body: const Center(
                child: CircularProgressIndicator(),
              ),
            );
          } else {
            return Scaffold(
              appBar: AppBar(
                title: const Text("fibonacci"),
              ),
              body: Center(
                child: Text("${snapshot.data}"),
              ),
            );
          }
        });
  }
}

// 非同期でn秒待つだけの関数
Future<String> delayTime(int n) async {
  await Future.delayed(Duration(seconds: num));
  return "${n}秒経過しました";
}

// 非同期でn番目のフィボナッチ数列を求める関数
Future<int> asyncFibonacci(int n) async {
  return n < 2 ? n : (await asyncFibonacci(n - 1) + await asyncFibonacci(n - 2));
}

// 並列処理でn番目のフィボナッチ数列を求める関数
int paraFibonacci(int n) {
  return n < 2 ? n : (paraFibonacci(n - 1) + paraFibonacci(n - 2));
}

軽い非同期処理の検証

非同期処理は関数Aが軽い処理である場合、有効な手段です。実際に検証しましょう。
関数AにdelayTime(3)をセットします。3秒だけ待ち結果を返す軽い処理です。 問題なく円形プログレスインジケーターが表示されてますね。

重い非同期処理

続いて重い非同期処理の場合に円形プログレスインジケーターがどうなるか検証します。
関数AにasyncFibonacci(35)をセットします。フィボナッチ数列の説明は省略しますが、計算に時間がかかる処理です。円形プログレスインジケーターは表示されず、結果が表示されています。これはasyncFibonacci(35)がスレッドを占有し、円形プログレスインジケーターが表示を妨げていることが原因です。

並列処理の検証

それでは並列処理を検証していきます。Flutterにはcompute(関数, 引数)とすることで並列処理が簡単に利用できます。関数Aにcompute(paraFibonacci, 42)をセットします。
※フィボナッチ数列は渡す数字が大きいほど時間がかかる処理で、並列処理の場合35だと目視ができないレベルだったので42としました。詳しくは手元で数字を変えて試してみてください。
非同期処理と異なり円形プログレスインジケーターが表示されてますね。これは別スレッドでcompute(paraFibonacci, 42)を実施することで、円形プログレスインジケーターの描画に影響を与えずに処理ができているためです。

結論

  • 並列処理(compute)を用いればUI描画への影響を避けつつ重い処理ができる
  • 軽い処理であれば非同期処理でも問題ない
  • 画面のカクつきが気になる時は並列処理(compute)を試すのがおすすめ

Discussion

コメントにはログインが必要です。