我有一个动画小部件。如果我为它制作动画片,那么就转发它。然后退出,应用程序不会崩溃。但是,如果我尝试重复动画,应用程序会在退出时崩溃(Android上的后退按钮)。
问题:如何停止动画并正确清理动画,以便退出时不会显示应用程序墓碑?
我使用以下内容重复动画:
animation.addStatusListener((status) {
if (status == AnimationStatus.completed) {
controller.reverse();
} else if (status == AnimationStatus.dismissed) {
controller.forward();
}
});
controller.forward();
(从Flutter docs复制/粘贴)...我也尝试过使用controller.repeat(),但同样的事情发生了。
应用程序在退出时崩溃并显示以下内容:
中止消息:'以std :: __ ndk1 :: system_error类型的未捕获异常终止:互斥锁失败:设备或资源忙'
追溯是:
#00 pc 0000000000021abc /system/lib64/libc.so (abort+124)
#01 pc 00000000009c7070 /data/app/crashtest.com.crashtest-oEV70lFQG32MrmIJyVK_Mw==/lib/arm64/libflutter.so
#02 pc 00000000009c7180 /data/app/crashtest.com.crashtest-oEV70lFQG32MrmIJyVK_Mw==/lib/arm64/libflutter.so
#03 pc 00000000009c4bec /data/app/crashtest.com.crashtest-oEV70lFQG32MrmIJyVK_Mw==/lib/arm64/libflutter.so
#04 pc 00000000009c45a4 /data/app/crashtest.com.crashtest-oEV70lFQG32MrmIJyVK_Mw==/lib/arm64/libflutter.so
#05 pc 000000000099e18c /data/app/crashtest.com.crashtest-oEV70lFQG32MrmIJyVK_Mw==/lib/arm64/libflutter.so
#06 pc 00000000001a2b60 /data/app/crashtest.com.crashtest-oEV70lFQG32MrmIJyVK_Mw==/lib/arm64/libflutter.so
#07 pc 000000000015df78 /data/app/crashtest.com.crashtest-oEV70lFQG32MrmIJyVK_Mw==/lib/arm64/libflutter.so
#08 pc 0000000000003338 /data/app/crashtest.com.crashtest-oEV70lFQG32MrmIJyVK_Mw==/oat/arm64/base.odex (offset 0x3000) (io.flutter.view.VsyncWaiter.nativeOnVsync+168)
#09 pc 0000000000004990 /dev/ashmem/dalvik-jit-code-cache (deleted) (io.flutter.view.VsyncWaiter.access$000+48)
#10 pc 0000000000005364 /dev/ashmem/dalvik-jit-code-cache (deleted) (io.flutter.view.VsyncWaiter$1.doFrame+84)
#11 pc 0000000000004a88 /dev/ashmem/dalvik-jit-code-cache (deleted) (android.view.Choreographer$CallbackRecord.run+152)
这是有问题的代码(完整的演示应用,所以它相当长,但你应该只能复制/粘贴到一个空的Flutter应用程序,它会工作!):</ p>
import 'package:flutter/material.dart';
void main() => runApp(new MyApp());
class MyApp extends StatelessWidget {
// This widget is the root of your application.
@override
Widget build(BuildContext context) {
return new MaterialApp(
title: 'Flutter Demo',
theme: new ThemeData(
primarySwatch: Colors.blue,
),
home: new MyHomePage(title: 'Crash on exit Demo'),
);
}
}
class MyHomePage extends StatelessWidget {
MyHomePage({Key key, this.title}) : super(key: key);
final String title;
@override
Widget build(BuildContext context) {
return new Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[
new Container(
height: 100.0,
width: 100.0,
child: new _AnimatedBlox(),
),
]
);
}
}
class _AnimatedBlox extends StatefulWidget {
_AnimatedBloxState createState() => new _AnimatedBloxState();
}
class _AnimatedBloxState extends State<_AnimatedBlox> with TickerProviderStateMixin {
Animation<int> animation;
AnimationController controller;
List<int> path = new List<int>();
initState() {
super.initState();
controller = new AnimationController(duration: const Duration(seconds: 4), vsync: this);
animation = new _PathWalker(path).animate(controller);
animation.addStatusListener((status) {
if (status == AnimationStatus.completed) {
controller.reverse();
} else if (status == AnimationStatus.dismissed) {
controller.forward();
}
});
controller.forward();
}
@override
void dispose() {
controller.stop();
controller.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return new LayoutBuilder(
builder: (context, constraints) {
return new _BlockGroup(path, constraints.maxHeight, animation: animation);
});
}
}
class _BlockGroup extends AnimatedWidget {
List<int> _path;
double _blockSize;
_BlockGroup(this._path, blockSize, {Key key, Animation<int> animation})
: super(key: key, listenable: animation) {
// 4 blocks, 3 gaps
double gap = 2.0;
_blockSize = (blockSize - 3 * gap) / 4.0;
}
Widget _getBox(int index) {
bool isInPath = _path.contains(index);
return new Container(
width: _blockSize,
height: _blockSize,
color: isInPath ? Colors.white : Colors.blueAccent);
}
@override
Widget build(BuildContext context) {
final Animation<int> animation = listenable;
var ensure_animation_runs_by_getting_the_value = animation.value;
return new Column(
mainAxisAlignment: MainAxisAlignment.spaceAround,
mainAxisSize: MainAxisSize.max,
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[
new Row(
mainAxisAlignment: MainAxisAlignment.spaceAround,
mainAxisSize: MainAxisSize.max,
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[
_getBox(0), _getBox(1), _getBox(2),
]
),
new Row(
mainAxisAlignment: MainAxisAlignment.spaceAround,
mainAxisSize: MainAxisSize.max,
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[
_getBox(3), _getBox(4), _getBox(5),
]
),
new Row(
mainAxisAlignment: MainAxisAlignment.spaceAround,
mainAxisSize: MainAxisSize.max,
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[
_getBox(6), _getBox(7), _getBox(8),
]
),
]
);
}
}
class _PathWalker extends Animatable<int> {
int lastUpdateTracker = 0; // tracks the index of the last call to animation from 0->15
List<int> _path;
_PathWalker(this._path);
// We get handed a double that scaled from 0.0 -> 1.0
// map this to 0 -> 8, and only change the current cell, when it changes
@override
int evaluate(Animation<double> animation) {
int value = (9 * animation.value).round();
if (lastUpdateTracker != value) {
lastUpdateTracker = value;
int newFirstElementIndex = 0;
if (_path.length > 0) {
newFirstElementIndex = _path.first;
}
switch (newFirstElementIndex) {
case 0:
newFirstElementIndex = 1;
break;
case 1:
newFirstElementIndex = 2;
break;
case 2:
newFirstElementIndex = 5;
break;
case 3:
newFirstElementIndex = 6;
break;
case 4:
newFirstElementIndex = 3;
break;
case 5:
newFirstElementIndex = 4;
break;
case 6:
newFirstElementIndex = 7;
break;
case 7:
newFirstElementIndex = 8;
break;
case 8:
newFirstElementIndex = 0;
break;
}
_path.insert(0, newFirstElementIndex);
// trim snake
while (_path.length > 3) {
_path.removeLast();
}
}
return value; // need to return something, but updates are done via the path
}
}
答案 0 :(得分:1)
每当您制作动画时,都应该在小部件的dispose()
方法中清理它。
要清理动画,我们应该处理负责执行AnimationController
的 animation
@override
void dispose() {
animationController?.dispose();
super.dispose();
}
答案 1 :(得分:1)
似乎是一个Android P错误。在模拟器中的Android O上不会出现。