RxJava2, RxAndroid2

[RxAndroid2] 메모리 누수를 잡아보자. Disposable, RxLifeCycle, CompositeDisposable

알통몬_ 2018. 10. 10. 15:28
반응형


공감 및 댓글은 포스팅 하는데

 아주아주 큰 힘이 됩니다!!

포스팅 내용이 찾아주신 분들께 

도움이 되길 바라며

더 깔끔하고 좋은 포스팅을 

만들어 나가겠습니다^^

 


메모리 누수

일반적으로 참조가 완료되었지만 할당한 메모리를 해제하지 않아서 발생.

강한 참조의 경우 GC가 메모리에서 객체를 제거할 수 없기 때문에

라이프 사이클에 맞게 객체 참조를 끊어주어야 사용하지 않는 메모리 해제 가능.

시스템 전체 성능에 영향을 주는 요소이므로 중요하게 관리해야함.


메모리 누수 예제

package park.sunggyun.thomas.rxandroidnetworkex;

import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.widget.TextView;
import io.reactivex.Observable;
import io.reactivex.Observer;
import io.reactivex.observers.DisposableObserver;

public class MainActivity extends AppCompatActivity {

TextView textView;

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
textView = findViewById(R.id.textView);
start();
}

private void start() {
Observer<String> observer = new DisposableObserver<String>() {
@Override
public void onNext(String s) {
textView.setText(s);
}

@Override
public void onError(Throwable e) {

}

@Override
public void onComplete() {

}
};

Observable.<String>create(emitter -> {
emitter.onNext("Hello RxAndroid");
emitter.onComplete();
}).subscribe(observer);
}

}

정상적으로 동작하는 코드입니다. BUT 메모리 누수가 있는 예제입니다.

Observable은 안드로이드의 Context를 복사하여 유지하고

onComplete(), onError() 함수가 호출되면 내부에서

자동으로 unsubscribe() 를 호출합니다.


구독자가 텍스트 뷰를 참조하기 때문에, 액티비티가 비정상 종료될 경우

텍스트 뷰가 참조하는 액티비티는 종료되어도 GC의 대상이 되지 못합니다.

=> 메모리 누수 발생.


위 같은 문제를 해결하는 방법에 대해 보겠습니다.


Disposable 인터페이스를 이용한 명시적 자원해제

onCreate()에서 subscribe()를 호출하면 onDestroy()에서 메모리 참조를 해제하고

onResume()에서 호출하면 onPause()에서 해제합니다.

이 방법을 라이프 사이클에 맞에 설정해주면 됩니다.

package park.sunggyun.thomas.rxandroidnetworkex;

import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.widget.TextView;
import io.reactivex.Observable;
import io.reactivex.Observer;
import io.reactivex.disposables.Disposable;
import io.reactivex.observers.DisposableObserver;

public class MainActivity extends AppCompatActivity {

private Disposable disposable;

TextView textView;

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
textView = findViewById(R.id.textView);
start();
}

private void start() {
DisposableObserver<String> observer = new DisposableObserver<String>() {
@Override
public void onNext(String s) {
textView.setText(s);
}

@Override
public void onError(Throwable e) {

}

@Override
public void onComplete() {

}
};

disposable = Observable.<String>create(emitter -> {
emitter.onNext("Hello RxAndroid");
emitter.onComplete();
}).subscribeWith(observer);
}

@Override
protected void onDestroy() {
super.onDestroy();
if (!disposable.isDisposed()) {
disposable.dispose();
}
}
}


RxLifeCycle 라이브러리 사용

compose()를 이용해 RxLifeCycle 라이브러리 적용.

먼저 dependencies{} 에 

implementation 'com.trello.rxlifecycle2:rxlifecycle:2.2.2'
implementation 'com.trello.rxlifecycle2:rxlifecycle-android:2.2.2'
implementation 'com.trello.rxlifecycle2:rxlifecycle-components:2.2.2'

를 추가해줍니다.

그리고 activity는 RxAppCompatActivity를 상속받아주면 됩니다.

package park.sunggyun.thomas.rxandroidnetworkex;

import android.os.Bundle;
import android.widget.TextView;

import com.trello.rxlifecycle2.android.ActivityEvent;
import com.trello.rxlifecycle2.components.support.RxAppCompatActivity;

import io.reactivex.Observable;

public class MainActivity extends RxAppCompatActivity {

TextView textView;

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
textView = findViewById(R.id.textView);
start();
}

private void start() {

Observable.<String>create(emitter -> {
emitter.onNext("Hello RxAndroid");
emitter.onComplete();
}).compose(bindUntilEvent(ActivityEvent.DESTROY))
.subscribe(textView::setText);
}

}


CompositeDisposable 클래스 사용

이 클래스를 사용하면 생성된 모든 Observable을 안드로이드 라이프 사이클에 맞춰서

한 번에 모두 메모리 해제를 할 수 있습니다.

package park.sunggyun.thomas.rxandroidnetworkex;

import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.widget.TextView;
import io.reactivex.Observable;
import io.reactivex.disposables.CompositeDisposable;
import io.reactivex.disposables.Disposable;

public class MainActivity extends AppCompatActivity {

TextView textView;
CompositeDisposable cDisposable = new CompositeDisposable();

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
textView = findViewById(R.id.textView);
start();
}

private void start() {

Disposable disposable = Observable.<String>create(emitter -> {
emitter.onNext("Hello RxAndroid");
emitter.onComplete();
}).subscribe(textView::setText);
cDisposable.add(disposable);
}

@Override
protected void onDestroy() {
super.onDestroy();
if (!cDisposable.isDisposed()) {
cDisposable.dispose();
}
}
}


이상입니다.

다음 포스팅에서는 RxJava 디버깅에 대해 공부합니다.


반응형