// Copyright 2004-present Facebook. All Rights Reserved.
package com.facebook.react;
import java.util.Collections;
import java.util.LinkedHashSet;
import java.util.Set;
import android.annotation.TargetApi;
import android.app.Application;
import android.content.ComponentCallbacks2;
import android.content.Context;
import android.content.res.Configuration;
import android.os.Build;
import com.facebook.react.bridge.MemoryPressure;
import com.facebook.react.bridge.MemoryPressureListener;
import static android.content.ComponentCallbacks2.TRIM_MEMORY_BACKGROUND;
import static android.content.ComponentCallbacks2.TRIM_MEMORY_COMPLETE;
import static android.content.ComponentCallbacks2.TRIM_MEMORY_MODERATE;
import static android.content.ComponentCallbacks2.TRIM_MEMORY_RUNNING_CRITICAL;
import static android.content.ComponentCallbacks2.TRIM_MEMORY_UI_HIDDEN;
/**
* Translates and routes memory pressure events to the current catalyst instance.
*/
public class MemoryPressureRouter {
// Trigger this by sending an intent to your activity with adb shell:
// am broadcast -a com.facebook.catalyst.ACTION_TRIM_MEMORY_MODERATE
private static final String ACTION_TRIM_MEMORY_UI_HIDDEN =
"com.facebook.rnfeed.ACTION_TRIM_MEMORY_UI_HIDDEN";
private static final String ACTION_TRIM_MEMORY_MODERATE =
"com.facebook.rnfeed.ACTION_TRIM_MEMORY_MODERATE";
private static final String ACTION_TRIM_MEMORY_CRITICAL =
"com.facebook.rnfeed.ACTION_TRIM_MEMORY_CRITICAL";
private final Set<MemoryPressureListener> mListeners =
Collections.synchronizedSet(new LinkedHashSet<MemoryPressureListener>());
private final ComponentCallbacks2 mCallbacks = new ComponentCallbacks2() {
@Override
public void onTrimMemory(int level) {
trimMemory(level);
}
@Override
public void onConfigurationChanged(Configuration newConfig) {
}
@Override
public void onLowMemory() {
}
};
@TargetApi(Build.VERSION_CODES.JELLY_BEAN)
public static boolean handleDebugIntent(Application application, String action) {
switch (action) {
case ACTION_TRIM_MEMORY_UI_HIDDEN:
simulateTrimMemory(application, ComponentCallbacks2.TRIM_MEMORY_UI_HIDDEN);
break;
case ACTION_TRIM_MEMORY_MODERATE:
simulateTrimMemory(application, TRIM_MEMORY_MODERATE);
break;
case ACTION_TRIM_MEMORY_CRITICAL:
simulateTrimMemory(application, TRIM_MEMORY_COMPLETE);
default:
return false;
}
return true;
}
MemoryPressureRouter(Context context) {
context.getApplicationContext().registerComponentCallbacks(mCallbacks);
}
/**
* Add a listener to be notified of memory pressure events.
*/
public void addMemoryPressureListener(MemoryPressureListener listener) {
mListeners.add(listener);
}
/**
* Remove a listener previously added with {@link #addMemoryPressureListener}.
*/
public void removeMemoryPressureListener(MemoryPressureListener listener) {
mListeners.remove(listener);
}
public void destroy(Context context) {
context.getApplicationContext().unregisterComponentCallbacks(mCallbacks);
}
private void trimMemory(int level) {
if (level >= TRIM_MEMORY_COMPLETE) {
dispatchMemoryPressure(MemoryPressure.CRITICAL);
} else if (level >= TRIM_MEMORY_BACKGROUND || level == TRIM_MEMORY_RUNNING_CRITICAL) {
dispatchMemoryPressure(MemoryPressure.MODERATE);
} else if (level == TRIM_MEMORY_UI_HIDDEN) {
dispatchMemoryPressure(MemoryPressure.UI_HIDDEN);
}
}
private void dispatchMemoryPressure(MemoryPressure level) {
// copy listeners array to avoid ConcurrentModificationException if any of the listeners remove
// themselves in handleMemoryPressure()
MemoryPressureListener[] listeners =
mListeners.toArray(new MemoryPressureListener[mListeners.size()]);
for (MemoryPressureListener listener : listeners) {
listener.handleMemoryPressure(level);
}
}
private static void simulateTrimMemory(Application application, int level) {
application.onTrimMemory(level);
}
}