Skip to content

Commit e6e217d

Browse files
dnfieldcg021
authored andcommitted
Call Shell::NotifyLowMemory when backgrounded/memory pressure occurs on Android (flutter#19026)
* Reland "Call Shell::NotifyLowMemoryWarning on Android Trim and LowMemory events (flutter#18979)" (flutter#19023)" This reverts commit 0a852d8.
1 parent b027d5a commit e6e217d

File tree

9 files changed

+73
-3
lines changed

9 files changed

+73
-3
lines changed

shell/platform/android/android_shell_holder.cc

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -181,4 +181,8 @@ fml::WeakPtr<PlatformViewAndroid> AndroidShellHolder::GetPlatformView() {
181181
return platform_view_;
182182
}
183183

184+
void AndroidShellHolder::NotifyLowMemoryWarning() {
185+
FML_DCHECK(shell_);
186+
shell_->NotifyLowMemoryWarning();
187+
}
184188
} // namespace flutter

shell/platform/android/android_shell_holder.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,8 @@ class AndroidShellHolder {
4040

4141
void UpdateAssetManager(fml::RefPtr<flutter::AssetManager> asset_manager);
4242

43+
void NotifyLowMemoryWarning();
44+
4345
private:
4446
const flutter::Settings settings_;
4547
const std::shared_ptr<PlatformViewAndroidJNI> jni_facade_;

shell/platform/android/io/flutter/embedding/android/FlutterActivityAndFragmentDelegate.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -629,6 +629,9 @@ void onUserLeaveHint() {
629629
void onTrimMemory(int level) {
630630
ensureAlive();
631631
if (flutterEngine != null) {
632+
// This is always an indication that the Dart VM should collect memory
633+
// and free any unneeded resources.
634+
flutterEngine.getDartExecutor().notifyLowMemoryWarning();
632635
// Use a trim level delivered while the application is running so the
633636
// framework has a chance to react to the notification.
634637
if (level == TRIM_MEMORY_RUNNING_LOW) {
@@ -651,6 +654,7 @@ void onTrimMemory(int level) {
651654
void onLowMemory() {
652655
Log.v(TAG, "Forwarding onLowMemory() to FlutterEngine.");
653656
ensureAlive();
657+
flutterEngine.getDartExecutor().notifyLowMemoryWarning();
654658
flutterEngine.getSystemChannel().sendMemoryPressureWarning();
655659
}
656660

shell/platform/android/io/flutter/embedding/engine/FlutterJNI.java

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -856,6 +856,22 @@ public Bitmap getBitmap() {
856856
// TODO(mattcarroll): determine if this is nonull or nullable
857857
private native Bitmap nativeGetBitmap(long nativePlatformViewId);
858858

859+
/**
860+
* Notifies the Dart VM of a low memory event, or that the application is in a state such that now
861+
* is an appropriate time to free resources, such as going to the background.
862+
*
863+
* <p>This is distinct from sending a SystemChannel message about low memory, which only notifies
864+
* the running Flutter application.
865+
*/
866+
@UiThread
867+
public void notifyLowMemoryWarning() {
868+
ensureRunningOnMainThread();
869+
ensureAttachedToNative();
870+
nativeNotifyLowMemoryWarning(nativePlatformViewId);
871+
}
872+
873+
private native void nativeNotifyLowMemoryWarning(long nativePlatformViewId);
874+
859875
private void ensureRunningOnMainThread() {
860876
if (Looper.myLooper() != mainLooper) {
861877
throw new RuntimeException(

shell/platform/android/io/flutter/embedding/engine/dart/DartExecutor.java

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -232,6 +232,19 @@ public void setIsolateServiceIdListener(@Nullable IsolateServiceIdListener liste
232232
}
233233
}
234234

235+
/**
236+
* Notify the Dart VM of a low memory event, or that the application is in a state such that now
237+
* is an appropriate time to free resources, such as going to the background.
238+
*
239+
* <p>This does not notify a Flutter application about memory pressure. For that, use the {@link
240+
* SystemChannel#sendMemoryPressureWarning}.
241+
*/
242+
public void notifyLowMemoryWarning() {
243+
if (flutterJNI.isAttached()) {
244+
flutterJNI.notifyLowMemoryWarning();
245+
}
246+
}
247+
235248
/**
236249
* Configuration options that specify which Dart entrypoint function is executed and where to find
237250
* that entrypoint and other assets required for Dart execution.

shell/platform/android/io/flutter/view/FlutterView.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -320,6 +320,7 @@ public void onStop() {
320320
}
321321

322322
public void onMemoryPressure() {
323+
mNativeView.getFlutterJNI().notifyLowMemoryWarning();
323324
systemChannel.sendMemoryPressureWarning();
324325
}
325326

shell/platform/android/platform_view_android_jni_impl.cc

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -464,6 +464,12 @@ static void InvokePlatformMessageEmptyResponseCallback(JNIEnv* env,
464464
);
465465
}
466466

467+
static void NotifyLowMemoryWarning(JNIEnv* env,
468+
jobject obj,
469+
jlong shell_holder) {
470+
ANDROID_SHELL_HOLDER->NotifyLowMemoryWarning();
471+
}
472+
467473
static jboolean FlutterTextUtilsIsEmoji(JNIEnv* env,
468474
jobject obj,
469475
jint codePoint) {
@@ -534,6 +540,11 @@ bool RegisterApi(JNIEnv* env) {
534540
.fnPtr = reinterpret_cast<void*>(
535541
&InvokePlatformMessageEmptyResponseCallback),
536542
},
543+
{
544+
.name = "nativeNotifyLowMemoryWarning",
545+
.signature = "(J)V",
546+
.fnPtr = reinterpret_cast<void*>(&NotifyLowMemoryWarning),
547+
},
537548

538549
// Start of methods from FlutterView
539550
{

shell/platform/android/test/io/flutter/embedding/android/FlutterActivityAndFragmentDelegateTest.java

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
package io.flutter.embedding.android;
22

3-
import static android.content.ComponentCallbacks2.TRIM_MEMORY_RUNNING_LOW;
3+
import static android.content.ComponentCallbacks2.*;
44
import static org.junit.Assert.assertEquals;
55
import static org.junit.Assert.assertNull;
66
import static org.mockito.Matchers.any;
@@ -442,7 +442,7 @@ public void itForwardsOnUserLeaveHintToFlutterEngine() {
442442
}
443443

444444
@Test
445-
public void itSendsMessageOverSystemChannelWhenToldToTrimMemory() {
445+
public void itNotifiesDartExecutorAndSendsMessageOverSystemChannelWhenToldToTrimMemory() {
446446
// Create the real object that we're testing.
447447
FlutterActivityAndFragmentDelegate delegate = new FlutterActivityAndFragmentDelegate(mockHost);
448448

@@ -451,14 +451,21 @@ public void itSendsMessageOverSystemChannelWhenToldToTrimMemory() {
451451
delegate.onAttach(RuntimeEnvironment.application);
452452

453453
// Emulate the host and call the method that we expect to be forwarded.
454+
delegate.onTrimMemory(TRIM_MEMORY_RUNNING_MODERATE);
454455
delegate.onTrimMemory(TRIM_MEMORY_RUNNING_LOW);
456+
delegate.onTrimMemory(TRIM_MEMORY_RUNNING_CRITICAL);
457+
delegate.onTrimMemory(TRIM_MEMORY_BACKGROUND);
458+
delegate.onTrimMemory(TRIM_MEMORY_COMPLETE);
459+
delegate.onTrimMemory(TRIM_MEMORY_MODERATE);
460+
delegate.onTrimMemory(TRIM_MEMORY_UI_HIDDEN);
455461

456462
// Verify that the call was forwarded to the engine.
463+
verify(mockFlutterEngine.getDartExecutor(), times(7)).notifyLowMemoryWarning();
457464
verify(mockFlutterEngine.getSystemChannel(), times(1)).sendMemoryPressureWarning();
458465
}
459466

460467
@Test
461-
public void itSendsMessageOverSystemChannelWhenInformedOfLowMemory() {
468+
public void itNotifiesDartExecutorAndSendsMessageOverSystemChannelWhenInformedOfLowMemory() {
462469
// Create the real object that we're testing.
463470
FlutterActivityAndFragmentDelegate delegate = new FlutterActivityAndFragmentDelegate(mockHost);
464471

@@ -470,6 +477,7 @@ public void itSendsMessageOverSystemChannelWhenInformedOfLowMemory() {
470477
delegate.onLowMemory();
471478

472479
// Verify that the call was forwarded to the engine.
480+
verify(mockFlutterEngine.getDartExecutor(), times(1)).notifyLowMemoryWarning();
473481
verify(mockFlutterEngine.getSystemChannel(), times(1)).sendMemoryPressureWarning();
474482
}
475483

shell/platform/android/test/io/flutter/embedding/engine/dart/DartExecutorTest.java

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
import static org.mockito.Mockito.mock;
77
import static org.mockito.Mockito.times;
88
import static org.mockito.Mockito.verify;
9+
import static org.mockito.Mockito.when;
910

1011
import android.content.res.AssetManager;
1112
import io.flutter.embedding.engine.FlutterJNI;
@@ -38,4 +39,14 @@ public void itSendsBinaryMessages() {
3839
verify(fakeFlutterJni, times(1))
3940
.dispatchPlatformMessage(eq("fake_channel"), eq(fakeMessage), anyInt(), anyInt());
4041
}
42+
43+
@Test
44+
public void itNotifiesLowMemoryWarning() {
45+
FlutterJNI mockFlutterJNI = mock(FlutterJNI.class);
46+
when(mockFlutterJNI.isAttached()).thenReturn(true);
47+
48+
DartExecutor dartExecutor = new DartExecutor(mockFlutterJNI, mock(AssetManager.class));
49+
dartExecutor.notifyLowMemoryWarning();
50+
verify(mockFlutterJNI, times(1)).notifyLowMemoryWarning();
51+
}
4152
}

0 commit comments

Comments
 (0)