简体   繁体   English

RxAndroid-修复活动泄漏?

[英]RxAndroid - fix Activity Leak?

I've distilled things down to the Activity shown below with the associated build.config and activity_main.xml. 我使用相关的build.config和activity_main.xml将事情简化为以下所示的Activity。

The problem is LeakCanary is reporting a leak of the Activity if you press BACK before the counter completes, whereas letting it run to the end doesn't. 问题是,如果您在计数器完成之前按BACK,则LeakCanary会报告活动的泄漏,而让计数器运行到最后则不会。

I've an idea what's going on. 我知道发生了什么事。 I know there must be an anonymous inner class created in the Activity by the Observer which survives the onPause() call because if you watch the console you can see it continuing the count. 我知道观察者必须在“活动”中创建一个匿名内部类,该类可以幸免onPause()调用,因为如果您观看控制台,您会看到它继续计数。 I know I've unsubscribed at this point, but the thread in the process is still running as an inner class of the Activity, which is the hallmark of a leak. 我知道我目前已经退订,但是进程中的线程仍作为Activity的内部类运行,这是泄漏的标志。 This isn't just an artificial corner case for me either, in my real app the count is supposed to continue after an orientation change, so I persist what's necessary in a retained fragment and pick up the count accordingly, with the same leak. 这对我来说也不只是一个虚假的案例,在我的真实应用中,计数应该在方向改变后继续,所以我坚持保留片段中的必要内容,并以相同的泄漏进行计数。 I just didn't need to show it here to simplify the example. 我只是不需要在这里显示它来简化示例。

MainAcivity.java: MainAcivity.java:

package com.otamate.rxactivityleaker;

import android.os.Bundle;
import android.os.SystemClock;
import android.support.v7.app.AppCompatActivity;
import android.util.Log;
import android.widget.TextView;

import com.squareup.leakcanary.LeakCanary;

import rx.Observable;
import rx.Subscriber;
import rx.android.schedulers.AndroidSchedulers;
import rx.schedulers.Schedulers;

public class MainActivity extends AppCompatActivity {
    private Observable<Long> mObservable;
    private Subscriber<Long> mSubscriber;

    public TextView mTextView;
    public final static int MAX_PROGRESS = 10;
    public final static int EMIT_DELAY_MS = 1000;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        LeakCanary.install(getApplication());

        setContentView(R.layout.activity_main);

        mTextView = (TextView) findViewById(R.id.textView);

        mSubscriber = createSubscriber();
        mObservable = createObservable();

        mObservable.subscribeOn(Schedulers.io())
            .observeOn(AndroidSchedulers.mainThread())
            .subscribe(mSubscriber);
    }

    private Observable createObservable() {
        return Observable.create (
            new Observable.OnSubscribe<Long>() {

                @Override
                public void call(Subscriber<? super Long> sub) {
                    for (long i = 1; i < MAX_PROGRESS + 1; i++) {

                        Log.d("Observable", "Progress: " + i);

                        sub.onNext(i);
                        SystemClock.sleep(EMIT_DELAY_MS);
                    }
                    sub.onCompleted();
                }
            }
        );
    }

    private Subscriber createSubscriber() {
        return new Subscriber<Long>() {

            @Override
            public void onNext(Long val) {
                Log.d("Subscriber", "Loop " + val);

                mTextView.setText("Progress: " + val);
            }

            @Override
            public void onCompleted() {
                Log.d("Subscriber", "Completed");

                mTextView.setText("Done!");
            }

            @Override
            public void onError(Throwable e) {
                Log.d("Subscriber", "Error: " + e);

                mTextView.setText("Error!");
            }
        };
    }

    @Override
    public void onPause() {
        super.onPause();

        if (mSubscriber != null) {
            Log.d("MainActivity", "onPause() Unsubscribed");

            mSubscriber.unsubscribe();
        }
    }
}

Here's activity_main.xml: 这是activity_main.xml:

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:paddingBottom="@dimen/activity_vertical_margin"
    android:paddingLeft="@dimen/activity_horizontal_margin"
    android:paddingRight="@dimen/activity_horizontal_margin"
    android:paddingTop="@dimen/activity_vertical_margin"
    tools:context="com.otamate.rxactivityleaker.MainActivity">

    <TextView
        android:id="@+id/textView"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Idle" />
</RelativeLayout>

build.gradle: 的build.gradle:

apply plugin: 'com.android.application'

android {
    compileSdkVersion 23
    buildToolsVersion "23.0.2"

    defaultConfig {
        applicationId "com.otamate.rxactivityleaker"
        minSdkVersion 15
        targetSdkVersion 23
        versionCode 1
        versionName "1.0"
    }
    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
        }
    }
}

dependencies {
    compile fileTree(dir: 'libs', include: ['*.jar'])
    testCompile 'junit:junit:4.12'
    compile 'com.android.support:appcompat-v7:23.1.1'
    compile 'com.android.support:appcompat-v7:23.1.1'
    compile 'io.reactivex:rxandroid:0.25.0'
    debugCompile 'com.squareup.leakcanary:leakcanary-android:1.3.1'
    releaseCompile 'com.squareup.leakcanary:leakcanary-android-no-op:1.3.1'
}

Observable.create makes it easy for you to shoot yourself in the foot . Observable.create使您轻松射击自己的脚

Your problem is here: 您的问题在这里:

private Observable createObservable() {
    return Observable.create (
        new Observable.OnSubscribe<Long>() {

            @Override
            public void call(Subscriber<? super Long> sub) {
                for (long i = 1; i < MAX_PROGRESS + 1; i++) {

                    Log.d("Observable", "Progress: " + i);

                    sub.onNext(i);
                    SystemClock.sleep(EMIT_DELAY_MS);
                }
                sub.onCompleted();
            }
        }
    );
}

When creating your Observable you are NOT checking if the Subscriber unsubscribed before calling onNext/onCompleted . 在创建Observable时,您不会在调用onNext/onCompleted之前检查Subscriber是否已取消订阅。 You are also not handling back-pressure. 您也没有处理背压。

Check out AbstractOnSubscribe it can help you create your Observable easier. 查看AbstractOnSubscribe,它可以帮助您更轻松地创建Observable

EDIT: AbstractOnSubscribe was removed in 1.1 but maybe SyncOnSubscribe will work. 编辑: AbstractOnSubscribe在1.1中已删除,但SyncOnSubscribe可能会起作用。

Try this 尝试这个

Subscription subscription;
@Override 
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);

    LeakCanary.install(getApplication()); 

    setContentView(R.layout.activity_main);

    mTextView = (TextView) findViewById(R.id.textView);

    mSubscriber = createSubscriber();
    mObservable = createObservable(); 

    subscription = mObservable.subscribeOn(Schedulers.io()) 
        .observeOn(AndroidSchedulers.mainThread()) 
        .subscribe(mSubscriber);
} 

Than in onPause() 比onPause()

@Override 
public void onPause() { 
    super.onPause(); 

    if (subscription != null) {

        subscription.unsubscribe();
    } 
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM