我正在用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)
字符串
接下来我可以检查什么?
补遗
我想知道我是不是对我的流构建器做错了什么。下面是我的代码的简化版本:
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那样发热,但也会崩溃。
我甚至尝试将dispose添加到这两个类中:
@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)
。看起来,内存使用量随着时间的推移而增加。为什么当用户离开手机屏幕时内存没有释放?(我问这个问题是因为怀疑即使在用户移动到另一个屏幕后,流仍然占用内存...)
4条答案
按热度按时间iyr7buue1#
对于永久增加的内存使用量有几点想法,因为如果没有完整的例子,几乎不可能回答你的问题。
1.我问这个问题是因为怀疑即使在用户移动到另一个屏幕后,流仍然占用内存...
也许你正在使用
Navigator.push*
,并且从未弹出任何以前的路由,以前路由中的所有列表都保存在内存中。1.跟踪dispose调用,看看小部件是否得到清理。通过使用日志记录、断点甚至更好地使用DevTools内存视图来跟踪小部件计数:https://docs.flutter.dev/development/tools/devtools/memory内存快照可以准确地告诉您内存的使用情况。
1.一般来说,最好将排序从
build
函数中移除。但这更多的是一个性能提示,而不是一个问题。1.您的内存问题可能与
EXC_BAD_ACCESS
崩溃无关,但您应该首先尝试解决它们。这将更容易找到潜在的原因,如果有任何。ergxz8rk2#
我知道我的问题没有真正组织起来,我无法提供一个适当的代码来复制这个问题。经过多次尝试,我似乎解决了这个问题。由于它只发生在有时,图片对我来说仍然模糊,但我至少知道是什么解决了我的问题。
提供一个完整的代码仍然很困难,但我将尝试解释是什么解决了我的问题,在这里用一些伪代码,我希望这可能是有帮助的其他人谁可能遭受类似的问题。
不管我对它有多不满意,我在应用程序的某些部分有一些不可避免的嵌套流。在一个页面上,我有多个嵌套的流构建器,但我将在下面仅用2个流来说明情况:
字符串
从数据库阅读数据B时,需要用到StreamBuilder 1的一些输入。有一种依赖性。
这两个流偶尔会导致页面刷新。可能导致EXC_BAD_ACCESSSS的原因之一似乎是试图读取已经释放的内存部分。我“怀疑”的是,当StreamBuilder 2需要使用data A从DB中读取自己的数据时,data A偶尔会不在内存中。
我做了如下改动。。
型
我用FutureBuilder替换了StreamBuilder 2,意外的崩溃消失了。
我基本上有多个嵌套的流构建器。这将是伟大的有流为所有他们,但它不是真的必须的。只有一个数据提要需要用流构建器实现,我做到了。我用future builder替换了所有其他流。从那以后,我再也没有遇到过EXC_BAD_ACCESS崩溃。
我可能误解了事情是如何运作的,或者这个改变是如何解决我的问题的。但这至少是我所做的,也是最终真正解决我问题的。任何人都有任何进一步的意见,他/她是非常欢迎分享他们,因为我很有兴趣学习什么事情,我需要考虑时,使用嵌套流,因为它是一个有点难以找到好的例子在那里。
侧记:我甚至研究了一下BLoC,希望它能帮助解决这个问题。我想对于那些已经知道如何使用它的人来说很容易。但这是相当令人生畏的,特别是如果你想在从firebase阅读数据时将其用于多个流,我找不到任何如何使用BLoC的例子。
**附录(2022年6月24日):**我仍然有这个问题,偶尔会发生。这在Android中还没有发生,但该应用程序因未知原因被关闭。在我上面提到的变化之后,这种情况并不经常发生,但问题还没有完全消失。
1cosmwyk3#
在我的情况下,它的崩溃,由于firebase推送通知。我从通知有效负载及其工作中删除了通知:
字符串
jslywgbw4#
我有一个类似的错误(EXC_BAD_ACCESS)。在我的例子中,这是一个无意义的(不确定是否正确)使用late关键字。(截图)可能是从别的地方复制粘贴过来的吧……分析器和编译器都没有警告这个错误。仅平台级崩溃日志。此外,扑干净帮助,但一段时间后错误再次发射。
的数据