Hello guys,
Every second I send the event to the server and save to the realm after a long time without turning off the application will be crash. ( about 1 or 2 days)
How to solve this problem?
Thanks.
P.s:
I think this might be due to running out of RAM or Storage device ?
But after a day, I will call the function to delete the event successfully.
Realm version(s): 4.0.0
Realm sync feature enabled: no
Android Studio version: 3.0.1
Which Android version and device: Android 5.0, Jacs/ Zenfone 3 Max
Code :
Background Event
public class BackgroundEventService extends Service {
public Context context = this;
public Handler handler = null;
public static Runnable runnable = null;
public final int timeDelayed = 5000; // default
// Config.getInstance().getTransmissionFrequencyToSecond() = 1s
public static boolean isServiceRunning = false;
@Override
public IBinder onBind(Intent intent) {
return null;
}
@Override
public void onCreate() {
handler = new Handler();
runnable = new Runnable() {
public void run() {
if (hasConfig()) {
EventRepository.getInstance().addRawEvent();
handler.postDelayed(runnable, Config.getInstance().getTransmissionFrequencyToSecond());
} else {
handler.postDelayed(runnable, timeDelayed);
}
}
};
if (hasConfig()) {
handler.postDelayed(runnable, Config.getInstance().getTransmissionFrequencyToSecond());
} else {
handler.postDelayed(runnable, timeDelayed);
}
}
public boolean hasConfig() {
if (Config.getInstance().getTransmissionFrequencyToSecond() != 0)
return true;
return false;
}
@Override
public void onDestroy() {
if (handler!=null && runnable!=null) {
handler.removeCallbacks(runnable);
}
isServiceRunning = false;
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
isServiceRunning = true;
return START_NOT_STICKY;
}
}
Func of this line : EventRepository.getInstance().addRawEvent();
public void addRawEvent() {
final RawEvent event = new RawEvent();
event.setDevId(Integer.valueOf(DeviceModeDB.getInstance().getDevID()));
event.setBusId(BusManager.getInstance().getCurrentDriver().getBusID());
event.setLatitude(BusManager.getInstance().getLatitude());
event.setLongitude(BusManager.getInstance().getLongitude());
event.setEventName(EventName.RAW_EVENT.name());
event.setEventTime(TimeUtils.generateTime());
EventDB.saveRawEvent(event, new Realm.Transaction.OnSuccess() {
@Override
public void onSuccess() {
addRawEvent(event, false);
}
});
}
private void addRawEvent(final RawEvent event, final boolean inBackground) {
if (event == null) return;
event.setEventSend(TimeUtils.generateTime());
mService.addRawEvent(event).enqueue(new Callback<EventResult>() {
@Override
public void onResponse(Call<EventResult> call, Response<EventResult> response) {
if (response.isSuccessful() && response.body() != null && response.body().onSuccess()) {
if (inBackground) {
EventDB.saveRawEventBackground(event);
} else {
EventDB.saveRawEvent(event, null);
}
}
}
@Override
public void onFailure(Call<EventResult> call, Throwable t) {
}
});
}
EventDB : Save to Realm
public static void saveRawEvent(final RawEvent item, final Realm.Transaction.OnSuccess onSuccess) {
if (item == null) return;
Realm.getDefaultInstance().executeTransactionAsync(new Realm.Transaction() {
@Override
public void execute(Realm realm) {
realm.insertOrUpdate(item);
}
}, new Realm.Transaction.OnSuccess() {
@Override
public void onSuccess() {
if (onSuccess != null) {
onSuccess.onSuccess();
}
}
});
}
public static void saveRawEventBackground(final RawEvent item) {
if (item == null) return;
Realm.getDefaultInstance().executeTransaction(new Realm.Transaction() {
@Override
public void execute(Realm realm) {
realm.insertOrUpdate(item);
}
});
}
Init Realm
public class ApplicationController extends MultiDexApplication {
private static ApplicationController mInstance;
public ApplicationController() {
}
public static ApplicationController getInstance() {
return mInstance;
}
@Override
public void onCreate() {
super.onCreate();
mInstance = this;
init();
}
private void init() {
if (!BuildConfig.DEBUG) {
Fabric.with(this, new Crashlytics());
}
Realm.init(this);
RealmConfiguration config = new RealmConfiguration.Builder()
.compactOnLaunch()
.build();
Realm.compactRealm(config);
}
}
Gradle :
buildscript {
repositories {
jcenter()
maven { url 'https://maven.google.com' }
maven {
url "https://jitpack.io"
}
}
dependencies {
classpath 'com.android.tools.build:gradle:2.3.3'
classpath "io.realm:realm-gradle-plugin:4.0.0"
classpath 'com.google.gms:google-services:3.1.1' // google-services plugin
// NOTE: Do not place your application dependencies here; they belong
// in the individual module build.gradle files
}
}
allprojects {
repositories {
jcenter()
maven {
url 'https://maven.google.com'
}
maven {
url "https://jitpack.io"
}
}
}
task clean(type: Delete) {
delete rootProject.buildDir
}
project.ext {
appcompat = "26.0.1"
retrofit = "2.3.0"
butterknife = "8.8.1"
rxjava = "2.1.3"
rxandroid = "2.0.1"
}
buildscript {
repositories {
maven { url 'https://maven.fabric.io/public' }
}
dependencies {
// These docs use an open ended version so that our plugin
// can be updated quickly in response to Android tooling updates
// We recommend changing it to the latest version from our changelog:
// https://docs.fabric.io/android/changelog.html#fabric-gradle-plugin
classpath 'io.fabric.tools:gradle:1.+'
}
}
apply plugin: 'com.android.application'
apply plugin: 'io.fabric'
apply plugin: 'realm-android'
repositories {
maven { url 'https://maven.fabric.io/public' }
maven { url 'https://maven.google.com' }
}
android {
compileSdkVersion 26
buildToolsVersion "26.0.2"
defaultConfig {
applicationId "com.myapp.event"
minSdkVersion 21
targetSdkVersion 26
versionCode 86
versionName "6.2"
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
multiDexEnabled true
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
realm {
syncEnabled = true
}
configurations.all {
resolutionStrategy.force 'com.google.code.findbugs:jsr305:3.0.2'
}
}
dependencies {
compile fileTree(include: ['*.jar'], dir: 'libs')
compile "com.android.support:appcompat-v7:$project.appcompat"
compile "com.squareup.retrofit2:retrofit:$project.retrofit"
compile "com.squareup.retrofit2:converter-gson:$project.retrofit"
compile "com.squareup.retrofit2:adapter-rxjava2:$project.retrofit"
compile 'com.squareup.okhttp3:logging-interceptor:3.8.1'
androidTestCompile('com.android.support.test.espresso:espresso-core:2.2.2', {
exclude group: 'com.android.support', module: 'support-annotations'
})
testCompile 'junit:junit:4.12'
compile 'com.google.code.gson:gson:2.8.2'
compile "io.reactivex.rxjava2:rxjava:$project.rxjava"
compile "io.reactivex.rxjava2:rxandroid:$project.rxandroid"
compile('com.crashlytics.sdk.android:crashlytics:2.6.8@aar') {
transitive = true
}
compile "com.jakewharton:butterknife:$project.butterknife"
annotationProcessor "com.jakewharton:butterknife-compiler:$project.butterknife"
compile 'com.jakewharton:butterknife:8.8.1'
annotationProcessor 'com.jakewharton:butterknife-compiler:8.8.1'
compile 'com.tbruyelle.rxpermissions2:rxpermissions:0.9.5@aar'
testCompile 'junit:junit:4.12'
compile 'de.hdodenhof:circleimageview:2.1.0'
compile 'me.relex:circleindicator:1.2.2@aar'
compile 'com.github.bumptech.glide:glide:4.3.1'
annotationProcessor 'com.github.bumptech.glide:compiler:4.3.1'
compile 'org.greenrobot:eventbus:3.1.1'
compile "com.jakewharton:butterknife:$project.butterknife"
annotationProcessor "com.jakewharton:butterknife-compiler:$project.butterknife"
compile 'pl.bclogic:pulsator4droid:1.0.3'
compile 'net.zetetic:android-database-sqlcipher:3.5.9@aar'
compile 'com.github.pwittchen:reactivenetwork-rx2:0.12.1'
compile 'com.yqritc:recyclerview-flexibledivider:1.4.0'
compile files('libs/gpx-parser-1.2.jar')
compile 'com.google.android.gms:play-services-maps:11.8.0'
compile 'com.google.firebase:firebase-core:11.8.0'
compile 'com.google.firebase:firebase-database:11.8.0'
compile 'com.android.support:multidex:1.0.2'
compile 'com.github.safetysystemtechnology:location-tracker-background:v1.3'
compile 'com.jenzz.appstate:appstate:3.0.1'
}
apply plugin: 'com.google.gms.google-services'
Log:
E/AndroidRuntime(21138): io.realm.exceptions.RealmError: Unrecoverable error. mmap() failed: Out of memory size: 1342177280 offset: 0 in /Users/cm/Realm/realm-java/realm/realm-library/src/main/cpp/io_realm_internal_SharedRealm.cpp line 101
E/AndroidRuntime(21138): at io.realm.internal.SharedRealm.nativeGetSharedRealm(Native Method)
E/AndroidRuntime(21138): at io.realm.internal.SharedRealm.<init>(SharedRealm.java:194)
E/AndroidRuntime(21138): at io.realm.internal.SharedRealm.getInstance(SharedRealm.java:241)
E/AndroidRuntime(21138): at io.realm.internal.SharedRealm.getInstance(SharedRealm.java:231)
E/AndroidRuntime(21138): at io.realm.BaseRealm.compactRealm(BaseRealm.java:636)
E/AndroidRuntime(21138): at io.realm.Realm.compactRealm(Realm.java:1689)
E/AndroidRuntime(21138): at com.myapp.event.ApplicationController.init(ApplicationController.java:103)
E/AndroidRuntime(21138): at com.myapp.event.ApplicationController.onCreate(ApplicationController.java:87)
E/AndroidRuntime(21138): at android.app.Instrumentation.callApplicationOnCreate(Instrumentation.java:1012)
E/AndroidRuntime(21138): at android.app.ActivityThread.handleBindApplication(ActivityThread.java:4557)
E/AndroidRuntime(21138): at android.app.ActivityThread.access$1500(ActivityThread.java:151)
E/AndroidRuntime(21138): at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1364)
E/AndroidRuntime(21138): at android.os.Handler.dispatchMessage(Handler.java:102)
E/AndroidRuntime(21138): at android.os.Looper.loop(Looper.java:135)
E/AndroidRuntime(21138): at android.app.ActivityThread.main(ActivityThread.java:5258)
E/AndroidRuntime(21138): at java.lang.reflect.Method.invoke(Native Method)
E/AndroidRuntime(21138): at java.lang.reflect.Method.invoke(Method.java:372)
E/AndroidRuntime(21138): at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:940)
E/AndroidRuntime(21138): at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:735)
Because you open a new Realm instance for every single thread that ever calls saveRawEventBackground or saveRawEvent, and you NEVER close them.
@Zhuinden
1.The current solution is to use eventBus to send the event to the main thread and then use the saveRawEventBackground or saveRawEvent to save it.
Is that solve this problem without close realm?
2.If I delete the old data every 12 hours, the realm automatically reduce the size?
Thanks.
No, deleted objects would still take up empty space because the old versions that are left unclosed would still retain them.
Also "unclaimed" empty state is reused by future writes, but only gets shrunk down if Realm is compacted (which requires that there are 0 open Realms on any thread)
Realm.getDefaultInstance().blah
This construct doesn't allow you to close the Realm.
You can verify number of open global instances and number of open thread-local (on current thread) instances with Realm.getGlobal/LocalInstanceCount.
Thanks @Zhuinden .
@Zhuinden Let say I have globalInstanceCount = 5; then how can I close all realm?
@Zhuinden :
Realm realm = Realm.getDefaultInstance();
// do something
realm.close();
I prefer
try(Realm realm = Realm.getDefaultInstance()) {
// do something
}
or
Realm.getDefaultInstance().use { realm ->
// do something
}
Most helpful comment
I prefer
or