<NME> CgeoApplication.java
<BEF> package cgeo.geocaching;

import cgeo.geocaching.network.Cookies;
import cgeo.geocaching.settings.Settings;
import cgeo.geocaching.storage.DataStore;
import cgeo.geocaching.ui.notifications.NotificationChannels;
import cgeo.geocaching.utils.ContextLogger;
import cgeo.geocaching.utils.Log;
import cgeo.geocaching.utils.OOMDumpingUncaughtExceptionHandler;

import android.annotation.SuppressLint;
import android.app.Application;
import android.content.ComponentCallbacks2;
import android.content.Context;
import android.content.res.Configuration;
import android.content.res.Resources;
import android.os.Build;
import android.os.Looper;
import android.os.UserManager;
import android.view.ViewConfiguration;

import java.lang.reflect.Field;
import java.lang.reflect.Method;

import com.squareup.leakcanary.LeakCanary;

public class CgeoApplication extends Application {

    private static CgeoApplication instance;

public class CgeoApplication extends Application {

    private static CgeoApplication instance;
    private static final AtomicInteger lowPrioNotificationCounter = new AtomicInteger(0);
    private static final AtomicBoolean hasHighPrioNotification = new AtomicBoolean(false);

    public CgeoApplication() {
        setInstance(this);
    }

    private static void setInstance(@NonNull final CgeoApplication application) {
        instance = application;
    }

    public static Application getInstance() {
        return instance;
    }

    @Override
            fixUserManagerMemoryLeak();
        }

        LeakCanary.install(this);
        showOverflowMenu();

        initApplicationLocale();

            Settings.setAppThemeAutomatically(this);

            if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.O) {
                fixUserManagerMemoryLeak();
            }

            showOverflowMenu();

            initApplicationLocale();

            // initialize cgeo notification channels
            NotificationChannels.createNotificationChannels(this);

            // ensure initialization of lists
            DataStore.getLists();

            // Restore cookies
            Cookies.restoreCookies();

            LooperLogger.startLogging(Looper.getMainLooper());
        }
    }

    /**
     * https://code.google.com/p/android/issues/detail?id=173789
     * introduced with JELLY_BEAN_MR2 / fixed in October 2016
     */
    private void fixUserManagerMemoryLeak() {
        try {
            // invoke UserManager.get() via reflection
            final Method m = UserManager.class.getMethod("get", Context.class);
            m.setAccessible(true);
            m.invoke(null, this);
        } catch (final Throwable e) {
            if (BuildConfig.DEBUG) {
                throw new IllegalStateException("Cannot fix UserManager memory leak", e);
            }
        }
    }

    @Override
    public void onConfigurationChanged(@NonNull final Configuration newConfig) {
        super.onConfigurationChanged(newConfig);

        initApplicationLocale();
    }

    private void showOverflowMenu() {
        try {
            final ViewConfiguration config = ViewConfiguration.get(this);
            final Field menuKeyField = ViewConfiguration.class.getDeclaredField("sHasPermanentMenuKey");
            menuKeyField.setAccessible(true);
            menuKeyField.setBoolean(config, false);
        } catch (IllegalArgumentException | IllegalAccessException | NoSuchFieldException ignored) {
        }
    }

    @Override
    public void onLowMemory() {
        super.onLowMemory();
        onTrimMemory(ComponentCallbacks2.TRIM_MEMORY_COMPLETE);
    }

    @SuppressLint("NewApi")
    @Override
    public void onTrimMemory(final int level) {
        super.onTrimMemory(level);
        if (level >= TRIM_MEMORY_MODERATE) {
            Log.i("Cleaning applications cache to trim memory");
            DataStore.removeAllFromCache();
        }
    }

    /**
     * Enforce a specific language if the user decided so.
     */
    private void initApplicationLocale() {
        final Configuration config = getResources().getConfiguration();
        config.locale = Settings.getApplicationLocale();
        final Resources resources = getResources();
        resources.updateConfiguration(config, resources.getDisplayMetrics());
    }

    public AtomicInteger getLowPrioNotificationCounter() {
        return lowPrioNotificationCounter;
    }

    public AtomicBoolean getHasHighPrioNotification() {
        return hasHighPrioNotification;
    }

}

<MSG> upgrade LeakCanary

Upgrade LeakCanary to version 2 (esp. 2.1).

LeakCanary 2 is a major rewrite. High level changes:

    New heap analyzer, reimplemented from scratch to use 10 times less memory (see Shark).
    APIs updated to simplify configuration and provide access to the new heap analyzer.
    Internals rewritten to 100% Kotlin.
    Multiple leaks detected in one analysis, grouped per leak type

The integration is now easier.

<DFF> @@ -22,8 +22,6 @@ import androidx.annotation.NonNull;
 import java.lang.reflect.Field;
 import java.lang.reflect.Method;
 
-import com.squareup.leakcanary.LeakCanary;
-
 public class CgeoApplication extends Application {
 
     private static CgeoApplication instance;
@@ -50,7 +48,6 @@ public class CgeoApplication extends Application {
             fixUserManagerMemoryLeak();
         }
 
-        LeakCanary.install(this);
         showOverflowMenu();
 
         initApplicationLocale();

upgrade LeakCanary