2

I am trying to use the WeakReference class to prevent from having memory leaks in Android.

I have designed a Bluetooth API that allows two Android devices to communicate (a client and a server) using the Android Bluetooth SDK.

This API is available whithin a shared library (so both apps have access to the same code).

The problem here is that the reference inside my weakreferences gets destroyed in matters of seconds. I tried to use a custom ReferenceQueue but none of its methods are called!

Looking at the RAM usage of both Android devices I don't see anything scary : very low, very stable, because I have a single Activity with a few views.

I thought the WeakReference is supposed to keep a reference of my Activities as long as they don't get garbage collected (meaning, there is no strong reference anymore). Am I wrong ?


[EDIT 1]

To provide more clarifications, I will post some theoretical code that should represent what I am attempting to do.

Class BluetoothAPI

 private WeakReference<BluetoothStateListener> stateListener;

 public final void setProgressListener(@Nullable BluetoothProgressListener progressListener) {

        this.progressListener = new WeakReference<>(progressListener, new Ref<>(context));
    }

private synchronized void notifyProgress(final float percent) {
        if (this.progressListener != null) {
            final BluetoothProgressListener listener = this.progressListener.get();
            if (listener != null) {
                UIAccess.runOnUiThread(new Runnable() {
                    public final void run() {
                        listener.onProgress(percent);
                    }
                });
            } else {
                Log.d(TAG, "Progress Listener is null!");
            }
        }
    }

Class Activity A, inside onCreate()

 this.btAPI.setProgressListener(new BluetoothProgressListener() {
            @UiThread
            public final void onProgress(final float percent) {
                final int progress = Math.round(percent * 10);
                progressBar.setProgress(progress);
                progressLabel.setText(getString(R.string.main_act_progress_status_tv_incoming, progress / 10));
            }
        });

When receiving data, my progressbar is never updated because the BluetoothProgressListener is always null. What does justify this ?

Cœur
  • 37,241
  • 25
  • 195
  • 267
Mackovich
  • 3,319
  • 6
  • 35
  • 73
  • Please show some code. – Fildor Feb 08 '17 at 08:27
  • 1
    There is no strong ref to the anonymous listener. So it will be GC'd. Expected behavior. – Fildor Feb 08 '17 at 08:43
  • What about `SoftReference` then ? If the Activity gets destroyed, will GC collect my anonymous listeners references whithin the softreferences ? – Mackovich Feb 08 '17 at 08:45
  • Edit: it does not work ... SoftReference holds onto the reference for a bit longer. I guess I did not get how the WeakReferences/SoftReferences really work. I'll have to revert back to manually destroy the strong references myself! – Mackovich Feb 08 '17 at 09:15
  • Maybe you can find inspiration here: http://blog.nimbledroid.com/2016/09/06/stop-memory-leaks.html – Fildor Feb 08 '17 at 09:21

1 Answers1

1

WeakReference in Android does not wait for next garbage collection cycle and destroy itself the moment no active and hard reference is holding onto them.

So you need to refactor your code and avoid using WeakReference as cache.

Nayan Srivastava
  • 3,655
  • 3
  • 27
  • 49
  • 2
    What is the reference for the claim that they destroy themselves without GC cycle? – Lucero Feb 08 '17 at 08:26
  • In my case, my Bluetooth API allows callback registration to be notified of state changes (connected, disconnected etc.) and for progress (when data is being sent or received). So in all these cases, my Activities register themselves with an anonymous class implementation. When the callbacks are triggered, the UI is updated to reflect the data events. So these implementations are saved inside the WeakReferences. I'd like to understand why these implementations get destroyed that quickly. Am I doing something wrong ? – Mackovich Feb 08 '17 at 08:26
  • "So these implementations are saved inside the WeakReferences." - How do you do that? – Fildor Feb 08 '17 at 08:27
  • you have to share some code, but the gist is it will destroy itself if no active references are available – Nayan Srivastava Feb 08 '17 at 08:27
  • @Lucero It is actually a java thing that does not work similarly with android – Nayan Srivastava Feb 08 '17 at 08:28
  • And actually u cannot rely on your described solution, you must always define a way to re-initialize your objects if weak refs are destroyed – Nayan Srivastava Feb 08 '17 at 08:30
  • @NayanSrivastava We understand that you claim "Android" works differently here. Lucero is asking for some source link to **backup** your statement. And I am with him on that. – GhostCat Feb 08 '17 at 08:31
  • Cannot really find that behavior in Docs: https://developer.android.com/reference/java/lang/ref/WeakReference.html Do you have other references? @Lucero – Fildor Feb 08 '17 at 08:31
  • @NayanSrivastava I'm not saying otherwise but I'm surprised so please post information (a link to an authoritative source) which explains how the WeakReference work in Android – Lucero Feb 08 '17 at 08:31
  • NP @Lucero but can't really quote an authoritative source. Just witnessed this with my experience on several devices and API levels – Nayan Srivastava Feb 08 '17 at 08:36
  • I have updated my post with some code. I hope you will better understand what I am trying to achieve. Truth be told, I am starting using `WeakReference` after doing some reading on Medium (and elsewhere) where the authors explain how to use this class to avoid memory leak because in Android & Java it is too easy to have memory leaks. Beforehand I used to manage my strong references (of the anonymous class implementations) by sending **null** to my event registerers, but Android lifecycle can be too tricky so I'd rather let the GC do its work. – Mackovich Feb 08 '17 at 08:37
  • you should not be creating anonymous class within context or any other resource that can be leaked. Why not concrete public class or static sub class – Nayan Srivastava Feb 08 '17 at 08:40
  • @NayanSrivastava then for the life of me, how not to leak your Activity/Context when you want to register a callback to update the UI when the data changes from an API manipulating said data ? The API cannot be a static inner class ... it's too big and not supposed to be linked to any other class. It's a pure API within a library. – Mackovich Feb 08 '17 at 08:46
  • You are actually leaking your context/activity by creating anonymous classes as they secretly hold strong ref of outer class and do not allow GC to clear them easily http://stackoverflow.com/questions/10864853/when-exactly-is-it-leak-safe-to-use-anonymous-inner-classes – Nayan Srivastava Feb 08 '17 at 08:48
  • @NayanSrivastava thank you but I already knew that, hence why I want to use WeakReference... in a previous comment I said I used to manage my strong references by sending **null** to replace the anonymous classes (which should clear out any previous strong references). However, in order to do that, I need to watch out for Android Lifecycle, which can be a bit too tricky... in some cases, my activities leaked even though I sent **null** because I did not manage all lifecycle cases! – Mackovich Feb 08 '17 at 08:52
  • Let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/135152/discussion-between-nayan-srivastava-and-mackovich). – Nayan Srivastava Feb 08 '17 at 08:54