https://cbtdev.net/dart-compute/
Flutter の非同期処理では async/await を使いますが、UI が固まるような CPU 負荷が高い処理をする場合は compute が便利なので、これらの使用方法について解説します。
async/await
サーバーの応答を待つだけといった、CPU 負荷は低いけど時間がかかる可能性がある処理で使われます。(メインスレッドで実行されるので CPU 負荷が高い場合は固まります。)
Future<Hoge> myFunc() async {
// await で何かの処理を待つ
var hoge = await hogehoge();
return hoge;
}
Flutter ではお馴染みだと思うので詳細は省きます。
compute
計算コストが高く、UI が固まるような CPU 負荷が高い処理では compute を利用すると便利です。(この処理は別スレッドで実行されます)
インポートするライブラリ
compute 関数を利用する場合は以下のライブラリをインポートします。
import 'package:flutter/foundation.dart';
使い方
compute で呼び出すグローバル関数、又は static な関数を定義します。
以下は String を入力として、Stringを返すグローバル関数の例です。
// compute で処理する関数
String calc(String data) {
// ここで何か重い処理
return data;
}
ウィジェット内のコードで以下のように呼び出せば、別スレッドで実行されます。第一引数が関数名で、第二引数が関数に渡すデータです。
data = await compute(calc, data);
compute の戻り値は Future なので、以下のように書くこともできます。
compute(calc, data)
.then((result) {
// 結果が返ってきた時に呼ばれる
data = result;
});
// この下は非同期で直ぐ呼ばれる
Future.wait
複数の非同期処理をまとめて完了待機したい場合は、Future.wait を使います。
// compute で書いてますが async/await でも同じです
var future1 = compute(calc1, _data1);
var future2 = compute(calc2, _data2);
var future3 = compute(calc3, _data3);
// まとめてリストにする
var futureList = Future.wait([future1, future2, future3]);
// 全ての処理を待機
await futureList.then((results) => _data = results[0]);
// 処理後の更新など
setState(() {});
results もリストで返ってくるので、適宜処理します。
サンプルコード
async/await と compute の動作を比較できるサンプルコードです。
同じグローバル関数を「async/await」と「compute」でそれぞれ実行します。
前者は UI のインジケーターアニメーションがガッツリ固まりますが、後者では UI が固まらずに処理できているのが確認できると思います。
import 'package:flutter/material.dart';
import 'package:flutter/foundation.dart'; // compute
import 'dart:io'; // sleep
// async/await と compute 共通で呼び出すグローバル関数
Future<String> calc(String data) async {
// ここで重い処理(今回は3秒sleepで代用)
sleep(Duration(seconds: 3));
data = '処理完了';
return data;
}
void main() => runApp(MyApp());
// MyApp ウィジェットクラス
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
theme: ThemeData.light(),
home: MyHomePage(title: 'Flutter Demo Home Page'),
);
}
}
// MyHomePage ウィジェットクラス
class MyHomePage extends StatefulWidget {
MyHomePage({Key key, this.title}) : super(key: key);
final String title;
@override
_MyHomePageState createState() => _MyHomePageState();
}
// MyHomePage ステートクラス
class _MyHomePageState extends State<MyHomePage> {
// ダミーデータ
String _dummyData = '';
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
CircularProgressIndicator(),
// ----- async/await の場合 -----
RaisedButton(
child: Text('async/await'),
onPressed: () async {
_dummyData = 'スタート';
setState(() {});
_dummyData = await calc(_dummyData);
setState(() {});
},
),
// ----- compute の場合 -----
RaisedButton(
child: Text('compute'),
onPressed: () async {
_dummyData = 'スタート';
setState(() {});
_dummyData = await compute(calc, _dummyData);
setState(() {});
},
),
// ----- 結果表示 -----
Text('$_dummyData'),
],
),
),
);
}
}
実行画面:
※ 比較のためグローバル関数を Future/async で書いていますが、compute 前提なら不要です。
0 コメント:
コメントを投稿