Skip to content

Commit

Permalink
fix: relay onLowMemory events to client Observers so they appear in r…
Browse files Browse the repository at this point in the history
…eports from the NDK layer
  • Loading branch information
lemnik committed May 24, 2021
1 parent b68d828 commit b59149a
Show file tree
Hide file tree
Showing 7 changed files with 99 additions and 1 deletion.
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
package com.bugsnag.android;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;

import com.bugsnag.android.ObserverInterfaceTest.BugsnagTestObserver;

import android.content.ComponentCallbacks;
import android.content.Context;
import androidx.test.core.app.ApplicationProvider;

import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
import org.mockito.Captor;
import org.mockito.Spy;
import org.mockito.junit.MockitoJUnitRunner;

/**
* Heavily mocked test to ensure that onLowMemory events are distributed to Client Observers
*/
@RunWith(MockitoJUnitRunner.class)
public class MemoryTrimTest {

@Spy
Context context = ApplicationProvider.getApplicationContext();

@Captor
ArgumentCaptor<ComponentCallbacks> componentCallbacksCaptor;

@Test
public void onLowMemoryEvent() {
when(context.getApplicationContext()).thenReturn(context);
Client client = new Client(context, BugsnagTestUtils.generateConfiguration());

// capture the registered ComponentCallbacks
verify(context, times(1)).registerComponentCallbacks(componentCallbacksCaptor.capture());

BugsnagTestObserver observer = new BugsnagTestObserver();
client.registerObserver(observer);

ComponentCallbacks callbacks = componentCallbacksCaptor.getValue();
callbacks.onLowMemory();

assertEquals(1, observer.observed.size());
Object observedEvent = observer.observed.get(0);

assertTrue(
"observed event should be UpdateMemoryTrimEvent",
observedEvent instanceof StateEvent.UpdateMemoryTrimEvent
);

assertTrue(
"observed event should be marked isLowMemory",
((StateEvent.UpdateMemoryTrimEvent) observedEvent).isLowMemory()
);
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -207,7 +207,7 @@ private <T extends StateEvent> T findMessageInQueue(Class<T> argClass) {
}

static class BugsnagTestObserver implements Observer {
private final ArrayList<Object> observed;
final ArrayList<Object> observed;

BugsnagTestObserver() {
observed = new ArrayList<>(4);
Expand Down
14 changes: 14 additions & 0 deletions bugsnag-android-core/src/main/java/com/bugsnag/android/Client.java
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
import androidx.annotation.VisibleForTesting;

import kotlin.Unit;
import kotlin.jvm.functions.Function1;
import kotlin.jvm.functions.Function2;

import java.util.ArrayList;
Expand Down Expand Up @@ -224,6 +225,7 @@ public Unit invoke(String activity, Map<String, ?> metadata) {
systemBroadcastReceiver = SystemBroadcastReceiver.register(this, logger, bgTaskService);

registerOrientationChangeListener();
registerMemoryTrimListener();

// load last run info
lastRunInfoStore = new LastRunInfoStore(immutableConfig);
Expand Down Expand Up @@ -357,6 +359,18 @@ public Unit invoke(String oldOrientation, String newOrientation) {
ContextExtensionsKt.registerReceiverSafe(appContext, receiver, configFilter, logger);
}

private void registerMemoryTrimListener() {
appContext.registerComponentCallbacks(new ClientComponentCallbacks(
new Function1<Boolean, Unit>() {
@Override
public Unit invoke(Boolean isLowMemory) {
clientObservable.postMemoryTrimEvent(isLowMemory);
return null;
}
}
));
}

void setupNdkPlugin() {
String lastRunInfoPath = lastRunInfoStore.getFile().getAbsolutePath();
int crashes = (lastRunInfo != null) ? lastRunInfo.getConsecutiveLaunchCrashes() : 0;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package com.bugsnag.android

import android.content.ComponentCallbacks
import android.content.res.Configuration

internal class ClientComponentCallbacks(
val callback: (Boolean) -> Unit
) : ComponentCallbacks {
override fun onConfigurationChanged(newConfig: Configuration) {}

override fun onLowMemory() {
callback(true)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,10 @@ internal class ClientObservable : BaseObservable() {
notifyObservers(StateEvent.UpdateOrientation(orientation))
}

fun postMemoryTrimEvent(isLowMemory: Boolean) {
notifyObservers(StateEvent.UpdateMemoryTrimEvent(isLowMemory))
}

fun postNdkInstall(conf: ImmutableConfig, lastRunInfoPath: String, consecutiveLaunchCrashes: Int) {
notifyObservers(
StateEvent.Install(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,4 +42,6 @@ sealed class StateEvent {
class UpdateOrientation(val orientation: String?) : StateEvent()

class UpdateUser(val user: User) : StateEvent()

class UpdateMemoryTrimEvent(val isLowMemory: Boolean) : StateEvent()
}
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,7 @@ class NativeBridge : Observer {
external fun updateUserEmail(newValue: String)
external fun updateUserName(newValue: String)
external fun getUnwindStackFunction(): Long
external fun updateLowMemory(newValue: Boolean)

/**
* Creates a new native bridge for interacting with native components.
Expand Down Expand Up @@ -134,6 +135,7 @@ class NativeBridge : Observer {
updateUserName(makeSafe(msg.user.name ?: ""))
updateUserEmail(makeSafe(msg.user.email ?: ""))
}
is StateEvent.UpdateMemoryTrimEvent -> updateLowMemory(msg.isLowMemory)
}
}

Expand Down

0 comments on commit b59149a

Please sign in to comment.