Skip to content
This repository was archived by the owner on Feb 25, 2025. It is now read-only.

Conversation

@matthew-carroll
Copy link
Contributor

Automatically destroy FlutterEngine when created by FlutterActivity or FlutterFragment.

I believe this resolves: flutter/flutter#43688

I did a memory profile locally and got the following results:

Memory dump before, during, and after the gallery was opened using existing engine:
130,258
287,161
282,553

Memory dump before, during, and after the gallery was opened using changes in this PR:
118,115
282,177
52,930

I'm now attempting to reproduce these results by running the actual device lab test locally. I'm waiting for a local build of profile mode to finish so that I can execute the test, but so far the evidence looks pretty strong.

@blasten
Copy link

blasten commented Oct 29, 2019

Are we able to add tests at this point?

@matthew-carroll
Copy link
Contributor Author

@blasten what did you have in mind?

@matthew-carroll
Copy link
Contributor Author

I realized after looking at the tests that the root cause is that our tests do not use the dedicated methods for configuring an Intent for FlutterActivity, which lead to the unexpected retention of the FlutterEngine. This PR essentially handles the missing Intent extra. I have now also added a test to verify that even such an illegal intent does not retain the FlutterEngine.


assertTrue(flutterActivity.shouldDestroyEngineWithHost());
}

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Have more tests where a mockito engine is passed into the activity/fragment via the cache or by subclassing and provideFlutterEngine and check that flutterEngine.destroy is not called?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Testing any lifecycle methods is outside what we seem to be able to do with our current testing infrastructure. Maybe we could assemble some e2e tests for that purpose, but even those seem to take a while to create. I did add some testing logic to cover the use-case you mentioned.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Robolectric in general can test lifecycle events. We should have the infra for this. But maybe the problem is something other than just the raw ability for the test harness to drive lifecycle events?

* removed.
*/
@VisibleForTesting
/* package */ void setDelegate(@NonNull FlutterActivityAndFragmentDelegate delegate) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Being able to put in a delegate factory was the reason why I made the FlutterInjector :D

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, I think the desired outcome here is to better support common testing mechanisms. Then we would probably have alternative approaches that don't require this or an injector.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

+1. We don't necessarily need to bring in a full DI framework (and probably shouldn't) but it would help testing a ton in general if the classes used constructor injection as a pattern instead of defaulting to instantiating everything as silent side effects of being constructed, or having certain callbacks triggered.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, the combination of problems here is that:

  • We can't constructor inject framework classes
  • The very 1st lifecycle method in FlutterActivity and FlutterFragment attempt to connect to JNI

We should eventually probably introduce a static dependency resolver for FlutterActivity and FlutterFragment. @xster had something like that in his original PR. The reason I removed it was because I think it was also being passed to plain Java classes, which I think should always utilize constructor injection unless there is an exceptional situation. But I probably should not have removed it entirely - I should have left it in specifically for FlutterActivity and FlutterFragment

At this point my recommendation is that we do what is necessary to button up the embedding for public consumption, and then it would be nice to schedule a dedicated project to resolve outstanding issues related to testing including: testing dep resolution, testing dep versioning, and embedding tools for testing.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ya, this is now tangential to this PR to some extent. But ya, that's why DI is so common on Android, because you can't construct and inject stuff generally.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's clean these up at some point in your TODO issue.

/**
* This method exists so that JVM tests can ensure that a delegate exists without
* putting this Activity through any lifecycle events, because JVM tests cannot handle
* executing any lifecycle methods, at the time of writing this.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should be possible. Does http://robolectric.org/androidx_test/ help any?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure, but we don't have support for the latest testing infra. I think we're still on robolectric v1, while robolectric is now on v3 or v4. If Flutter brings this testing infra completely up to date with the ecosystem then we can probably write a lot of better tests.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should be able to put activities through lifecycle events on our current Robolectric version (3.8). What's missing in 3 that you need in 4 that we don't have here?

@xster
Copy link
Member

xster commented Oct 30, 2019

I think you probably don't need the setter by calling activitycontroller.create etc.

Otherwise LGTM

@matthew-carroll
Copy link
Contributor Author

@xster the first thing I tried was calling create(). That crashes right off the bat by trying to attach to JNI. So then I tried to subclass and override onCreate() to only instantiate the delegate but then it blew up because it couldn't find some version of appcompat. That means there's probably some transitive dependency that we're missing in our testing infra, but every time I've tried to solve those problems it costs me a number of days to track it down. So I opted for exposing a setter. Is it OK if we continue as-is?

@xster
Copy link
Member

xster commented Oct 30, 2019

@mklim looks like https://chrome-infra-packages.appspot.com/p/flutter/android/robolectric_bundle/+/last_updated:2019-09-09T16:47:38-0700 is on robolectric 3.8 right?

This is fine. Though we should leave an issue to clean this up at some point.

@matthew-carroll
Copy link
Contributor Author

Maybe I'm thinking of some other library version then. I think one of these major pieces is on 1 instead of 3. Oh, maybe I'm thinking of mockito? In any event, I think if we could write tests the way that tests are exemplified in standard Android docs, then we'd probably be in a position to write better tests.

@matthew-carroll
Copy link
Contributor Author

Added a TODO with a github issue in source code to remove the setter.

@xster
Copy link
Member

xster commented Oct 30, 2019

Mockito doesn't affect this right? It's a purely robolectric issue.

@matthew-carroll
Copy link
Contributor Author

Yeah, that's correct. But really it's that we haven't included every possible transitive dependency that would typically be loaded. I'm also not sure how to even discover that set of dependencies. The dependency tree is both very broad and very deep. My overarching point is that pretty much every new test that's written seems to lead to some missing piece, so each new test can end up with days of plumbing work. If we could reach the status-quo, which is Gradle resolved dependencies with the latest and most convenient APIs, then we could probably do a lot better with tests in general, including this specific case, too.

@xster
Copy link
Member

xster commented Oct 30, 2019

https://github.com/flutter/engine/pull/12069/files#diff-f928557f2d60773a8435366400fa42edR93 was meant to have a universally test accessible way of not crashing on JNI. Is this somewhere else now?

This PR is fine but if the only issue left was the crash on ActivityController.create, it would be nice to spend some time-bound effort to fix it since it seems fixable.

@matthew-carroll
Copy link
Contributor Author

That sounds good to me. Can we wait until after the code freeze since these are package private, visible for testing exposures?

@xster
Copy link
Member

xster commented Oct 30, 2019

ya, that's fine

@mklim
Copy link
Contributor

mklim commented Oct 30, 2019

This is fine. Though we should leave an issue to clean this up at some point.

Yeah, tracked at flutter/flutter#38471. We were blocked for a little while on a Robolectric issue but the latest release should fix it. The upgrade is a little complicated because it also involves switching CI to use Java 9.

@matthew-carroll
Copy link
Contributor Author

I finally got the device lab test running locally with the patch. Here is the comparison of the results. It looks good to me.

Before patch:

Task result:
{
"success": true,
"data": {
"start-min": 37628,
"start-max": 43147,
"start-median": 37677,
"end-min": 126056,
"end-max": 131671,
"end-median": 126819,
"diff-min": 88313,
"diff-max": 89793,
"diff-median": 88524
},
"benchmarkScoreKeys": [
"start-min",
"start-max",
"start-median",
"end-min",
"end-max",
"end-median",
"diff-min",
"diff-max",
"diff-median"
]
}

After patch:

Task result:
{
"success": true,
"data": {
"start-min": 51088,
"start-max": 57177,
"start-median": 51593,
"end-min": 56913,
"end-max": 60748,
"end-median": 57558,
"diff-min": 3571,
"diff-max": 6470,
"diff-median": 5727
},
"benchmarkScoreKeys": [
"start-min",
"start-max",
"start-median",
"end-min",
"end-max",
"end-median",
"diff-min",
"diff-max",
"diff-median"
]
}

@matthew-carroll
Copy link
Contributor Author

Merging on red LUCI due to message in chat that says it's not really broken.

@matthew-carroll matthew-carroll merged commit 97807b5 into flutter:master Oct 30, 2019
engine-flutter-autoroll added a commit to engine-flutter-autoroll/flutter that referenced this pull request Oct 30, 2019
engine-flutter-autoroll added a commit to flutter/flutter that referenced this pull request Oct 31, 2019
git@github.com:flutter/engine.git/compare/e609577d1201...97807b5

git log e609577..97807b5 --no-merges --oneline
2019-10-30 matthew-carroll@users.noreply.github.com Automatically destroy FlutterEngine when created by FlutterActivity or FlutterFragment. (flutter/engine#13423)
2019-10-30 bkonyi@google.com Roll src/third_party/dart f30b494035..d50c158d42 (5 commits) (flutter/engine#13443)
2019-10-30 skia-flutter-autoroll@skia.org Roll src/third_party/skia 7da048b5e8f1..7df14d055703 (7 commits) (flutter/engine#13441)


If this roll has caused a breakage, revert this CL and stop the roller
using the controls here:
https://autoroll.skia.org/r/flutter-engine-flutter-autoroll
Please CC cbracken@google.com on the revert to ensure that a human
is aware of the problem.

To report a problem with the AutoRoller itself, please file a bug:
https://bugs.chromium.org/p/skia/issues/entry?template=Autoroller+Bug

Documentation for the AutoRoller is here:
https://skia.googlesource.com/buildbot/+/master/autoroll/README.md
Inconnu08 pushed a commit to Inconnu08/flutter that referenced this pull request Nov 26, 2019
git@github.com:flutter/engine.git/compare/e609577d1201...97807b5

git log e609577..97807b5 --no-merges --oneline
2019-10-30 matthew-carroll@users.noreply.github.com Automatically destroy FlutterEngine when created by FlutterActivity or FlutterFragment. (flutter/engine#13423)
2019-10-30 bkonyi@google.com Roll src/third_party/dart f30b494035..d50c158d42 (5 commits) (flutter/engine#13443)
2019-10-30 skia-flutter-autoroll@skia.org Roll src/third_party/skia 7da048b5e8f1..7df14d055703 (7 commits) (flutter/engine#13441)


If this roll has caused a breakage, revert this CL and stop the roller
using the controls here:
https://autoroll.skia.org/r/flutter-engine-flutter-autoroll
Please CC cbracken@google.com on the revert to ensure that a human
is aware of the problem.

To report a problem with the AutoRoller itself, please file a bug:
https://bugs.chromium.org/p/skia/issues/entry?template=Autoroller+Bug

Documentation for the AutoRoller is here:
https://skia.googlesource.com/buildbot/+/master/autoroll/README.md
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Flutter Gallery Benchmark Regressions

6 participants