question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

Leak resulting from RxBleClient.scanBleDevices()

See original GitHub issue

I’ve been pulling my hair out trying to debug a leak in my app when using RxBleClient.scanBleDevices(). My subscribe onNext lambda holds a reference to the Activity, however I am unsubscribing when the Activity is destroyed, so I shouldn’t be leaking. The leak’s GC root is a thread, which references com.polidea.rxandroidble.internal.radio.FIFORunnableEntry.emitter.

Maybe I’m just missing something here? In that case, any insight is greatly appreciated.

I’ve created an example that demonstrates the issue:

import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;

import com.google.common.base.Strings;
import com.polidea.rxandroidble.RxBleClient;
import com.polidea.rxandroidble.scan.ScanSettings;

import com.example.R;
import com.example.MyApplication;
import rx.Observable;
import rx.Subscription;
import timber.log.Timber;

public class DebugLeakActivity extends AppCompatActivity {

    RxBleClient bleClient;
    Subscription subscription = null;
    String someActivityField;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_debug_leak);

        // in my app, RxBleClient is provided by dependency injection and has the lifetime of the app.
        // simulating here.
        bleClient = ((MyApplication)getApplication()).bleClient;

    }

    @Override
    protected void onStart() {
        super.onStart();

        subscription = scan()
                .subscribe(
                        // reference a field in the activity to create a reference to the Activity from the lambda
                        deviceString -> someActivityField = deviceString,
                        throwable -> Timber.e(throwable));
    }

    @Override
    protected void onDestroy() {

        if (subscription != null) {
            subscription.unsubscribe();
            subscription = null;
        }
        super.onDestroy();
    }

    public Observable<String> scan() {
        ScanSettings scanSettings = new ScanSettings.Builder()
                .setScanMode(ScanSettings.SCAN_MODE_LOW_LATENCY)
                .build();

        return bleClient.scanBleDevices(scanSettings)
                .doOnUnsubscribe(() -> Timber.d("scanBleDevices() unsubscribed"))
                .distinct()
                .doOnNext(scanResult -> Timber.d(String.format("Found BLE device: %s, %s", scanResult.getBleDevice().getMacAddress(), Strings.nullToEmpty(scanResult.getBleDevice().getName()))))
                .map(rxBleScanResult -> rxBleScanResult.toString());
    }
}

Here is the reference chain from LeakCanary:

In com.example:0.20.0-SNAPSHOT:200000.
* com.example.presentation.DebugLeakActivity has leaked:
* GC ROOT thread java.lang.Thread.<Java Local> (named 'Thread-8')
* references com.polidea.rxandroidble.internal.radio.FIFORunnableEntry.emitter
* references rx.internal.operators.OnSubscribeCreate$NoneEmitter.actual
* references rx.internal.operators.OperatorUnsubscribeOn$1.val$subscriber (anonymous subclass of rx.Subscriber)
* references rx.internal.operators.OnSubscribeMap$MapSubscriber.actual
* references rx.internal.operators.OperatorMerge$InnerSubscriber.parent
* references rx.internal.operators.OperatorMerge$MergeSubscriber.child
* references rx.observers.Subscribers$5.val$subscriber (anonymous subclass of rx.Subscriber)
* references rx.observers.Subscribers$5.val$subscriber (anonymous subclass of rx.Subscriber)
* references rx.observers.Subscribers$5.val$subscriber (anonymous subclass of rx.Subscriber)
* references rx.internal.operators.OperatorDistinct$1.val$child (anonymous subclass of rx.Subscriber)
* references rx.internal.operators.OnSubscribeDoOnEach$DoOnEachSubscriber.subscriber
* references rx.internal.operators.OnSubscribeMap$MapSubscriber.actual
* references rx.observers.SafeSubscriber.actual
* references rx.internal.util.ActionSubscriber.onNext
* references com.example.presentation.DebugLeakActivity$$Lambda$1.arg$1 (anonymous implementation of rx.functions.Action1)
* leaks com.example.presentation.DebugLeakActivity instance

I’ve attached the full LeakCanary details for more information: In 2.txt

Issue Analytics

  • State:closed
  • Created 6 years ago
  • Comments:6 (4 by maintainers)

github_iconTop GitHub Comments

1reaction
streetsofbostoncommented, Oct 12, 2017

Looking at this code snippet:

    @Override
    protected void onStart() {
        super.onStart();

        subscription = scan()
                .subscribe(
                        // reference a field in the activity to create a reference to the Activity from the lambda
                        deviceString -> someActivityField = deviceString,
                        throwable -> Timber.e(throwable));
    }

    @Override
    protected void onDestroy() {

        if (subscription != null) {
            subscription.unsubscribe();
            subscription = null;
        }
        super.onDestroy();
    }

The onStart can be called multiple times before the onDestroy is finally called.

Instead of unsubscribing in the onDestroy, override the onStop instead and unsubscribe there.

0reactions
dariuszseweryncommented, Oct 13, 2017

I think that @streetsofboston comment may pinpoint the cause of the observed leak. Closing for now. If new information will be available—feel free to reopen.

Read more comments on GitHub >

github_iconTop Results From Across the Web

Leak resulting from RxBleClient.scanBleDevices() #288 - GitHub
I've been pulling my hair out trying to debug a leak in my app when using RxBleClient.scanBleDevices() . My subscribe onNext lambda holds...
Read more >
android - RxAndroidBle with java 7 - Stack Overflow
I am trying to use RxAndroidBle to scan for devices. All the examples I found seem to use java lambda expression. Do I...
Read more >
Bachelorarbeit
In chapter 5, we talk about the results we have achieved during our thesis: ... They are used in gas leakage detecting equipment...
Read more >
RxAndroidBLE — your most powerful tool for Bluetooth Low ...
Your entry point to the library is the RxBleClient class. ... scanBleDevices() .subscribe(rxBleScanResult -> { // Process scan result here. }); ...
Read more >
bd3242b7cac19704fa66d226f13...
01-20 18:07:18.002 6621 7096 D BluetoothGatt: readRssi() - device: ... lowi_close_record:Scan done in 25297295ms, 11 APs in scan results 01-20 18:07:18.085 ...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found