需要使用Isolates来加速我的应用程序中的文件处理



应用程序的功能:

  • 点击FAB将:
  • 创建一个包含多个页面或多个png文件的pdf文件。(后者被选为默认值,可以通过参数中的布尔值进行更改(
  • 该函数将返回一个文件列表

问题出在哪里:

  • 当页面超过4个时,主隔离(应用程序(将开始滞后,如果数量超过2位数,应用程序将崩溃。我正在努力加快处理速度,希望能防止崩溃

我尝试过的:

  • 正在学习如何生成隔离,但我仍然会遇到很多错误
  • 使用Isolate是非常低级的编程,而且用于它的资源非常少

有些事情可能没有意义,因为这是一个更大代码的精简版本。但一切都应该正常,在这个例子中,它可以创建pdf或png文件,我更喜欢png文件这是最耗时的文件。

完整代码https://github.com/fenchai23/pdf_isolate_test.git

这是一个孤立物的例子,来自隔离物pdf包的作者,但它非常简单,不是我想做的。

这是完整的main.dart文件:

import 'dart:io';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:path_provider/path_provider.dart';
import 'package:pdf/pdf.dart';
import 'package:pdf/widgets.dart' as pw;
import 'package:printing/printing.dart';
void main() async {
WidgetsFlutterBinding.ensureInitialized();
runApp(MyApp());
}
class MyApp extends StatelessWidget {
// This widget is the root of your application.
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
home: MyHomePage(title: 'Pdf Isolate Test'),
);
}
}
class MyHomePage extends StatefulWidget {
MyHomePage({Key key, this.title}) : super(key: key);
final String title;
@override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
final List _invoice = [
{
'qty': '1',
'code': '27274-7J125 SET',
'desc': 'A/A NIS PATHFINDER NEW 2015 - 2PC  ALMERA  27275-1N605',
'invt': '615',
'codealt': 'AC2503 SET',
'descalt': '',
'group': '10.FILTRO A/A',
'price': '5.53',
'disc1': '4.42',
'globalPrice': '5.53',
'total': '5.53'
},
{
'qty': '1',
'code': '27274-EA000 SET',
'desc':
'A/A NIS FRONTIER VQ40  YD25 PATHFIADER D40  VITARRA J20 27274-EL00A 27277-4JA0A',
'invt': '1018',
'codealt': 'AC2507SET',
'descalt': '27277-4JA0A' 'GRAN VITARRA J20',
'group': '10.FILTRO A/A',
'price': '4.25',
'disc1': '3.40',
'globalPrice': '4.25',
'total': '4.25'
}
];
static Future<List<File>> generateInvoice(Map args) async {
print('creating pdf');
final String _cartSubTotal = '1.00';
final String _cartTotal = '1.01';
final String _cartItbms = '7.00%';
final DateTime _startTime = DateTime.now();
final String dateTimeStamp = DateTime.now().toString();
PdfImage _logo;
List<Product> products = [];
final List customRecordData = args['customRecordData'];
final String customClientName = args['customClientName'];
final String customClientDirection = args['customClientDirection'];
final String customClientSeller = args['customClientSeller'];
final bool isPdf = args['isPdf'];
for (final k in customRecordData) {
Product productsHolder = Product(
k['qty'],
k['code'],
k['desc'],
k['globalPrice'],
k['total'],
);
products.add(productsHolder);
}
final pw.Document pdf = pw.Document();
_logo = PdfImage.file(pdf.document,
bytes: (await rootBundle.load('assets/icons/appIcon.png'))
.buffer
.asUint8List());
pw.Widget _pdfHeader(pw.Context context) {
return pw.Column(
children: [
pw.Row(
crossAxisAlignment: pw.CrossAxisAlignment.start,
children: [
pw.Expanded(
child: pw.Column(
children: [
pw.Container(height: 2),
pw.Container(
decoration: pw.BoxDecoration(
borderRadius: 2,
border:
pw.BoxBorder(color: PdfColors.blue, width: 2)),
padding: const pw.EdgeInsets.only(
left: 10, top: 5, bottom: 5, right: 10),
alignment: pw.Alignment.centerLeft,
height: 60,
child: pw.DefaultTextStyle(
style: pw.TextStyle(
color: PdfColors.black,
fontSize: 10,
),
child: pw.Column(
mainAxisSize: pw.MainAxisSize.min,
crossAxisAlignment: pw.CrossAxisAlignment.start,
mainAxisAlignment: pw.MainAxisAlignment.spaceEvenly,
children: [
pw.Text('Cliente:  $customClientName', maxLines: 1),
pw.Text('Dir:  $customClientDirection',
maxLines: 1),
pw.Text('Vend:  $customClientSeller', maxLines: 1),
],
),
),
),
],
),
),
pw.Expanded(
child: pw.Column(
mainAxisSize: pw.MainAxisSize.min,
children: [
pw.Container(
alignment: pw.Alignment.topRight,
padding: const pw.EdgeInsets.only(bottom: 8, left: 30),
height: 72,
child: pw.Image(_logo),
),
],
),
),
],
),
if (context.pageNumber > 1) pw.SizedBox(height: 20),
],
);
}
pw.Widget _contentTable(pw.Context context) {
const tableHeaders = ['CANT', 'CODIGO', 'DESCRIPCION', 'PRECIO', 'TOTAL'];
return pw.Table.fromTextArray(
context: context,
border: null,
cellAlignment: pw.Alignment.centerLeft,
headerDecoration: pw.BoxDecoration(
borderRadius: 2,
color: PdfColors.blue,
),
headerHeight: 25,
cellHeight: 25,
cellAlignments: {
0: pw.Alignment.center,
1: pw.Alignment.centerLeft,
2: pw.Alignment.centerLeft,
3: pw.Alignment.center,
4: pw.Alignment.centerRight,
},
headerStyle: pw.TextStyle(
color: PdfColors.white,
fontSize: 10,
fontWeight: pw.FontWeight.bold,
),
cellStyle: const pw.TextStyle(
color: PdfColors.black,
fontSize: 10,
),
rowDecoration: pw.BoxDecoration(
border: pw.BoxBorder(
bottom: true,
color: PdfColors.grey,
width: .5,
),
),
headers: List<String>.generate(
tableHeaders.length,
(col) => tableHeaders[col],
),
data: List<List<String>>.generate(
products.length,
(row) => List<String>.generate(
tableHeaders.length,
(col) => products[row].getIndex(col),
),
),
);
}
pw.Widget _contentFooter(pw.Context context) {
return pw.Row(
crossAxisAlignment: pw.CrossAxisAlignment.start,
children: [
pw.Expanded(
flex: 2,
child: pw.Column(
crossAxisAlignment: pw.CrossAxisAlignment.start,
children: [
pw.Text(
'',
style: pw.TextStyle(
color: PdfColors.black,
fontWeight: pw.FontWeight.bold,
),
),
pw.Container(
margin: const pw.EdgeInsets.only(top: 20, bottom: 8),
child: pw.Text(
'',
style: pw.TextStyle(
color: PdfColors.black,
fontWeight: pw.FontWeight.bold,
),
),
),
pw.Text(
'',
style: const pw.TextStyle(
fontSize: 8,
lineSpacing: 5,
color: PdfColors.black,
),
),
],
),
),
pw.Expanded(
flex: 1,
child: pw.DefaultTextStyle(
style: const pw.TextStyle(
fontSize: 10,
color: PdfColors.black,
),
child: pw.Column(
crossAxisAlignment: pw.CrossAxisAlignment.start,
children: [
pw.Row(
mainAxisAlignment: pw.MainAxisAlignment.spaceBetween,
children: [
pw.Text('Sub Total:'),
pw.Text(_cartSubTotal),
],
),
pw.SizedBox(height: 5),
pw.Row(
mainAxisAlignment: pw.MainAxisAlignment.spaceBetween,
children: [
pw.Text('I.T.B.M.S:'),
pw.Text(_cartItbms),
],
),
pw.Divider(color: PdfColors.black),
pw.DefaultTextStyle(
style: pw.TextStyle(
color: PdfColors.black,
fontSize: 14,
fontWeight: pw.FontWeight.bold,
),
child: pw.Row(
mainAxisAlignment: pw.MainAxisAlignment.spaceBetween,
children: [
pw.Text('Total:'),
pw.Text(_cartTotal),
],
),
),
],
),
),
),
],
);
}
pw.Widget _pdfFooter(pw.Context context) {
return pw.DefaultTextStyle(
style: pw.Theme.of(context).defaultTextStyle.copyWith(
color: PdfColors.grey,
),
child: pw.Row(
mainAxisAlignment: pw.MainAxisAlignment.spaceBetween,
children: [
pw.Container(
child: pw.Text(dateTimeStamp),
margin: const pw.EdgeInsets.only(top: 1.0 * PdfPageFormat.cm),
),
pw.Container(
alignment: pw.Alignment.centerRight,
margin: const pw.EdgeInsets.only(top: 1.0 * PdfPageFormat.cm),
child: pw.Text(
'Página ${context.pageNumber} de ${context.pagesCount}',
),
),
],
),
);
}
// Here is where PDF (Invoice) is created
pdf.addPage(
pw.MultiPage(
pageTheme: pw.PageTheme(
buildBackground: (pw.Context context) => pw.FullPage(
ignoreMargins: true,
child: pw.Container(
color: PdfColors.white,
),
),
pageFormat: PdfPageFormat.letter.copyWith(
marginBottom: 1.5 * PdfPageFormat.cm,
marginTop: 1.5 * PdfPageFormat.cm,
marginLeft: 1 * PdfPageFormat.cm,
marginRight: 1 * PdfPageFormat.cm,
),
),
header: _pdfHeader,
footer: _pdfFooter,
build: (pw.Context context) => <pw.Widget>[
pw.Padding(
padding: pw.EdgeInsets.only(bottom: 12.0),
),
_contentTable(context),
pw.Padding(
padding: pw.EdgeInsets.only(bottom: 12.0),
),
_contentFooter(context),
],
),
);
final output = await getTemporaryDirectory();
final timef = dateTimeStamp;
// get a safe name for filenames
final safeClientName =
customClientName.replaceAll(RegExp("[\\/:*?"<>|]"), '');
final String fileName = '$timef--$customClientSeller--$safeClientName';
File file;
var imageList = List<File>();
var pagesToPrint;
pagesToPrint = await Printing.raster(pdf.save()).length;
if (!isPdf) {
int pageIndex = 1;
await for (var page in Printing.raster(pdf.save(), dpi: 300)) {
final image = await page.toPng();
final String pageQty =
(pagesToPrint > 1) ? '$pageIndex-$pagesToPrint' : '1-1';
file = File("${output.path}/$fileName--$pageQty.png");
await file.writeAsBytes(image);
imageList.add(file);
pageIndex++;
}
} else {
file = File("${output.path}/$fileName.pdf");
await file.writeAsBytes(pdf.save());
imageList.add(file);
}
print('done creating pdf');
final int elapsedTime = DateTime.now().difference(_startTime).inSeconds;
print('time elapsed: $elapsedTime seconds');
return imageList;
}
@override
void initState() {
final tempInvoice = [];
for (final Map k in _invoice) {
print(k);
tempInvoice.add(k);
}
for (int i = 1; i <= 100; i++) {
_invoice.addAll(tempInvoice);
}
print(_invoice);
print(_invoice.length);
super.initState();
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: Container(
child: Center(
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
CircularProgressIndicator(),
SizedBox(
height: 20,
),
Text('If animation lags then we know isolate failed.')
],
))),
floatingActionButton: FloatingActionButton(
onPressed: () async {
await getTemporaryDirectory()
..delete(recursive: true);
var result = await compute(generateInvoice, {
'customRecordData': _invoice,
'customClientName': 'Obama',
'customClientDirection': 'USA',
'customClientSeller': '1',
'isPdf': false,
});
print(result);
},
child: Icon(Icons.picture_as_pdf),
),
);
}
}
class Product {
const Product(
this.qty,
this.code,
this.desc,
this.price,
this.total,
);
final String qty;
final String code;
final String desc;
final String price;
final String total;
String getIndex(int index) {
switch (index) {
case 0:
return qty;
case 1:
return truncateWithEllipsis(25, code.trim());
case 2:
return truncateWithEllipsis(50, desc.trim());
case 3:
return price;
case 4:
return total;
}
return '';
}
}
String truncateWithEllipsis(int cutoff, String myString) {
return (myString.length <= cutoff)
? myString
: '${myString.substring(0, cutoff)}...';
}

虽然应用程序运行良好,但当我尝试运行计算代码时,它会给我以下错误:

[ERROR:flutter/lib/ui/ui_dart_state.cc(166)] Unhandled Exception: Exception: ServicesBinding.defaultBinaryMessenger was accessed before the binding was initialized.

我试着给main((asyncWidgetsFlutterBinding.ensureInitialized();但是它继续调用相同的错误。

顺便说一句,这是最耗时的部分。

if (!isPdf) {
int pageIndex = 1;
await for (var page in Printing.raster(pdf.save(), dpi: 300)) {
final image = await page.toPng();
final String pageQty =
(pagesToPrint > 1) ? '$pageIndex-$pagesToPrint' : '1-1';
file = File("${output.path}/$fileName--$pageQty.png");
await file.writeAsBytes(image);
imageList.add(file);
pageIndex++;
}
} else {
file = File("${output.path}/$fileName.pdf");
await file.writeAsBytes(pdf.save());
imageList.add(file);
}

也许我没见过,但你在哪里生成隔离物?

在Isolate上生成可能有点棘手,但Flutter有一个很好的解决方法,它被称为计算函数。试试看!计算Flutter API

或者试试这个页面,它帮助我很好地了解了这个功能。使用Flutter 进行计算

也许值得一提的是,Dart是一种单线程语言,这意味着你可以在后台的第二个线程上运行你的代码,在主线程中你可以显示一个加载指示符。变通方法是生成更多的线程或向量化代码。Dart矢量化

Tom

相关内容

最新更新