Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

一个比较隐蔽的内存泄漏的Bug,并引发了奇怪的行为。 #512

Open
aitsuki opened this issue Aug 6, 2021 · 0 comments · May be fixed by #513
Open

一个比较隐蔽的内存泄漏的Bug,并引发了奇怪的行为。 #512

aitsuki opened this issue Aug 6, 2021 · 0 comments · May be fixed by #513

Comments

@aitsuki
Copy link

aitsuki commented Aug 6, 2021

先来看一个测试用例

/// Rebuild the widget after 'refresh' or 'loadmore' is completed, check
/// their callbaks can get a correct timestamp(defined in 'builder').
/// If get a wrong timestamp, consider memory leak.
testWidgets("test memory leak", (tester) async {
  final controller = RefreshController();
  int currentTimestamp = 0;
  await tester.pumpWidget(MaterialApp(
    home: Directionality(
      textDirection: TextDirection.ltr,
      child: Builder(
          key: Key('builder'),
          builder: (context) {
            int timestamp = DateTime.now().millisecondsSinceEpoch;
            currentTimestamp = timestamp;
            print('$timestamp ---- build');
            return SmartRefresher(
              controller: controller,
              enablePullUp: true,
              onLoading: () {
                controller.loadComplete();
                print('$timestamp --- onLoading');
                expect(timestamp, currentTimestamp);
                tester
                    .firstElement(find.byKey(Key('builder')))
                    .markNeedsBuild();
              },
              onRefresh: () {
                controller.refreshCompleted();
                print('$timestamp --- onRefresh');
                expect(timestamp, currentTimestamp);
                tester
                    .firstElement(find.byKey(Key('builder')))
                    .markNeedsBuild();
              },
              child: ListView(
                children: List<Widget>.generate(20, (index) {
                  return Container(height: 50, child: Text('Item: $index'));
                }),
              ),
            );
          }),
    ),
  ));

  // test pull refresh
  await tester.drag(find.byType(Scrollable), const Offset(0, 100.0),
      touchSlopY: 0.0);
  await tester.pumpAndSettle();
  await tester.drag(find.byType(Scrollable), const Offset(0, 100.0),
      touchSlopY: 0.0);
  await tester.pumpAndSettle();

  // test load more
  controller.position!.jumpTo(controller.position!.maxScrollExtent - 30.0);
  await tester.drag(find.byType(Scrollable), const Offset(0, -30.0));
  await tester.pumpAndSettle();
  await tester.drag(find.byType(Scrollable), const Offset(0, -30.0));
  await tester.pumpAndSettle();
});
1628278954679 ---- build
1628278954679 --- onRefresh
1628278955294 ---- build
1628278955294 --- onRefresh
1628278955405 ---- build
1628278955405 --- onLoading
1628278955452 ---- build
1628278955405 --- onLoading
══╡ EXCEPTION CAUGHT BY FOUNDATION LIBRARY ╞════════════════════════════════════════════════════════
The following TestFailure object was thrown while dispatching notifications for
RefreshNotifier<LoadStatus>:
  Expected: <1628278955452>
  Actual: <1628278955405>

在加载更多的onLoading回调中,打印的时间戳是永远是首次回调onLoading得到的那个,初步考虑是内存泄漏了,onLoading持有了应该要被销毁的Builder。通过devtools检查,发现确实存在两个SmartRefresher实例,gc无法回收。

初步判断问题可能出在这里,因为我没有设置header和footer,使用的是默认的:

class SmartRefresherState extends State<SmartRefresher> {

  final RefreshIndicator defaultHeader =
      defaultTargetPlatform == TargetPlatform.iOS
          ? ClassicHeader()
          : MaterialClassicHeader();

  final LoadIndicator defaultFooter = ClassicFooter();
}

这里header实例和footer实例保存在了State中,所以父节点重建时,这两个widget不会被重建,而且他们应该还持有了SmartRefresher对象但是没有被正确释放。

@aitsuki aitsuki linked a pull request Aug 7, 2021 that will close this issue
lauglam pushed a commit to lauglam/pull_to_refresh that referenced this issue Jun 17, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging a pull request may close this issue.

1 participant