我正在使用Flutter开发一个应用程序,并在物理iOS设备(iPhone 7)上测试该应用程序。
iOS版本为:15.3.1
Flutter版本为:2.10.3
在测试应用程序时,我偶尔会遇到崩溃情况。崩溃会显示以下错误信息。它不总是在同一位置崩溃,因此我不知道在此处应该分享哪些代码。对我来说,错误消息本身并没有说太多内容,我也找不到关于这个错误的有用信息。
* thread #1, queue = 'com.apple.main-thread', stop reason = EXC_BAD_ACCESS (code=1, address=0x443b8000443b8000)
frame #0: 0x0000000198405048 libobjc.A.dylib`objc_msgSend + 8
libobjc.A.dylib`objc_msgSend:
-> 0x198405048 <+8>: ldr x13, [x0]
0x19840504c <+12>: and x16, x13, #0xffffffff8
0x198405050 <+16>: mov x15, x16
0x198405054 <+20>: ldr x11, [x16, #0x10]
Target 0: (Runner) stopped.
Lost connection to device.
Exited (sigterm)
接下来可以检查什么?
补充说明
我想知道我在使用Stream Builder方面是否有做错的地方。这是我的代码缩短后的版本:
class PrepareList extends StatefulWidget {
final String place;
const PrepareList({
Key? key,
required this.place,
}) : super(key: key);
@override
State<PrepareList> createState() =>
_PrepareListState();
}
class _PrepareListState
extends State<PrepareList> {
late final Stream? _listStream;
@override
void initState() {
super.initState();
String dbChild = "events/" + widget.place + "/";
final db = FirebaseDatabase.instance
.ref()
.child(dbChild)
.orderByKey()
.limitToLast(1500);
_listStream = db.onValue;
}
@override
Widget build(BuildContext context) {
return StreamBuilder(
stream: _listStream,
builder: (context, AsyncSnapshot<dynamic> dbEvent) {
if (dbEvent.hasError) {
return CircularProgressIndicator();
}
else if (dbEvent.hasData) {
DataSnapshot dataSnapshot = dbEvent.data!.snapshot;
List<EventDetails> placeEventList = [];
if (dataSnapshot.value != null) {
(dataSnapshot.value as Map<dynamic, dynamic>)
.forEach((key, value) {
placeEventList.add(EventDetails.fromRTDB(value));
});
placeEventList
.sort((a, b) => a.dateEST.compareTo(b.dateEST));
return AttendeeList(placeEventList: placeEventList, place: place);
} else {
return PlaceDataNotAvailable(place: widget.place);
}
} else {
return CircularProgressIndicator();
}
});
}
}
class AttendeeList extends StatefulWidget {
final List<EventDetails> placeEventList;
final String place;
const AttendeeList({
Key? key,
required this.placeEventList,
required this.place,
}) : super(key: key);
@override
State<AttendeeList> createState() =>
_AttendeeListState();
}
class _AttendeeListState
extends State<AttendeeList> {
late final Stream? _attendeeListStream;
@override
void initState() {
super.initState();
String dbChild = "attendees/" + widget.place + "/";
final db = FirebaseDatabase.instance
.ref()
.child(dbChild)
.orderByKey()
.limitToLast(1500);
_listStream = db.onValue;
}
@override
Widget build(BuildContext context) {
return StreamBuilder(
stream: _attendeeListStream,
builder: (context, AsyncSnapshot<dynamic> dbEvent) {
if (dbEvent.hasError) {
return CircularProgressIndicator();
}
else if (dbEvent.hasData) {
DataSnapshot dataSnapshot = dbEvent.data!.snapshot;
List<AttendeeDetails> attendeeList = [];
if (dataSnapshot.value != null) {
(dataSnapshot.value as Map<dynamic, dynamic>)
.forEach((key, value) {
attendeeList.add(EventDetails.fromRTDB(value));
});
attendeeList
.sort((a, b) => a.dateEST.compareTo(b.dateEST));
return Scaffold(
body: ShowLists(placeEventList, attendeeList);
} else {
return PlaceDataNotAvailable(place: widget.place);
}
} else {
return CircularProgressIndicator();
}
});
}
}
上面的小部件可以在应用程序的生命周期中多次调用。用户通过选择初始屏幕上的place
来到达此屏幕,这将执行PrepareList
有状态小部件中的代码,该小部件又调用AttendeeList
有状态小部件。
我想强调的是,PrepareList
和AttendeeList
都使用流。每次执行这些小部件中的代码时,会从数据库下载大量节点(每个小部件为1500)。
一次执行可能如下所示:
PrepareList("London");
另一个执行可能看起来像以下内容,在同一屏幕上呈现一个新的项目列表:
PrepareList("Manhattan");
我所观察到的是:
当我第一次运行PrepareList("London");
时,需要等待一些时间(3至4秒)才能在屏幕上看到内容。然后我运行PrepareList("Manhattan");
,同样需要大约3至4秒才能显示内容。但当我再次运行PrepareList("London");
时,内容会非常快地出现在屏幕上,大约1秒钟。
为了能够调用PrepareList()
,我需要进入另一个屏幕,这意味着 - 在我的理解中 - 每次离开与上述2个小部件相关联的屏幕时,我的流订阅被取消了。但是,流本身是否被取消,数据是否保留在内存中?
我的怀疑:在使用应用程序时,由于我多次调用PrepareList(...)
,它会在内存中加载越来越多的数据,并且永远不会清除。过一段时间后,应用程序会消耗所有可用内存并崩溃,给我提供了无意义的错误提示。
随着应用程序使用PrepareList(...)
的次数越来越多,我能够明显感觉到Iphone 7会变得发热。我甚至用Iphone 12进行了测试,虽然不像Iphone 7那样发热,但仍然会崩溃。
我甚至尝试将以下内容添加到两个类中:
@override
void dispose() {
super.dispose();
}
...但仍然没有帮助。
我的流实现是正确的吗?我怀疑这可能是导致崩溃的潜在问题吗?
附录2
我继续尝试。 我以一种方式使用应用程序,以便多次触发PrepareList("...");
。我也在 devtools 中观察内存使用情况。我可以观察到随着时间的推移内存使用量会增加。这次我得到了一个新的错误,更具体地说:
[ServicesDaemonManager] interruptionHandler is called. -[FontServicesDaemonManager connection]_block_invoke
[tcp] tcp_input [C17.1.1:3] flags=[R] seq=3749683210, ack=0, win=0 state=LAST_ACK rcv_nxt=3749683210, snd_una=3584722489
[tcp] tcp_input [C17.1.1:3] flags=[R] seq=3749683210, ack=0, win=0 state=CLOSED rcv_nxt=3749683210, snd_una=3584722489
* thread #46, name = 'DartWorker', stop reason = EXC_RESOURCE RESOURCE_TYPE_MEMORY (limit=1450 MB, unused=0x0)
frame #0: 0x0000000108ef4d0c Flutter`dart::CompilerPass_TypePropagation::DoBody(dart::CompilerPassState*) const + 1644
Flutter`dart::CompilerPass_TypePropagation::DoBody:
-> 0x108ef4d0c <+1644>: str xzr, [x20, x28, lsl #3]
0x108ef4d10 <+1648>: ldr x8, [x22, #0x48]
0x108ef4d14 <+1652>: cmp x24, x8
0x108ef4d18 <+1656>: b.ge 0x108ef4d84 ; <+1764>
Target 0: (Runner) stopped.
Lost connection to device.
这次报错说是EXC_RESOURCE RESOURCE_TYPE_MEMORY(limit=1450 MB,unused=0x0)
。看起来,内存使用量随时间增加。为什么用户离开手机屏幕后内存没有被释放呢?(我怀疑流即使在用户移动到另一个屏幕后仍然占用内存...)