현재 개발하고 있는 앱의 메모리 누수가 감지되고 있습니다. ㅡㅡ^ 


메모리 누수 감지는 


https://github.com/square/leakcanary


를 통해서 프로파일링을 하고 있고, 대부분 문제가 스레드가 활동하는 동안 Context 가 살아남아서 생기는 문제가 있습니다.


뭐 자바의 패러다임으로 볼 때, 메모리는 VM 이 관리해주기 때문에 딱히 개발자가 건들 필요는 없어보이지만, 안드로이드 메모리가 제한적이고 각 객체가 더이상 쓰이지 않을 때 바로바로 해제를 해주어야 한다는 측면에서 적절한 작업이 이뤄줘야하는 것으로 보입니다. 특히 액티비티 인스턴스는 용량이 크기 때문에 잘 해제가 되야 합니다. ㅡㅡ^, 


해당 문제를 해결하기 위해, 아래 포스트처럼 WeakReference 를 통해 해결을 하려 하고 있으며, 이를 적용하기 위한 일괄된 패턴이 필요했습니다.



그래서 이번에 시도해보고 있는 것은 

잔디 블로그 (http://tosslab.github.io/android/2015/03/01/01.Android-mvc-mvvm-mvp.html

에서 소개한 MVP 패턴 사례를 참고하여, 메모리 누수를 막아보려고 하고 있습니다.


제가 하려고 했던 아래 코드입니다.


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
public class SplashActivity extends Activity {
 
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_splash);
    }
 
    @Override
    protected void onResume() {
        super.onResume();
 
        Handler hd = new Handler();
        hd.postDelayed(() -> {
            // 심플하지만, 이 곳 핸들러의 역할 때문에 메모리 누수가 존재
            setResult(RESULT_OK);
            finish();
 
            overridePendingTransition(R.anim.abc_fade_in, R.anim.abc_fade_out);
        }, 1000);
    }
}
cs



핸들러 내부에서 Context 가 살아남아 있으며, 실제 프로파일러에서는 Handler 에서 메모리 릭이 감지되었다고 나오고 있습니다.


이러한 이유로, 해야할 일을 나누어 봅시다.


WeakReference 관리는 Presenter 가 담당합니다. 아래와 같이 말이죠.


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
public class SplashPresenter {
    private Handler hd;
    private WeakReference<SplashActivity> splashActivityWeakReference;
 
    public SplashPresenter(SplashActivity splashActivity) {
        splashActivityWeakReference = new WeakReference<>(splashActivity);
        hd = new Handler();
    }
 
    /**
     * 1초 후에 해당 액티비트를 종료한다.
     */
    public void delayFinished() {
        hd.postDelayed(() -> {
            SplashActivity splashActivity = splashActivityWeakReference.get();
 
            if (splashActivity != null) {
                splashActivity.smoothFinishActivity();
            }
 
        }, 1000);
    }
}
cs


잔디 블로그의 경우에는 Presenter 를 인터페이스화 시킨 후 구현화하였지만,


- 저의 경우는 Activity 와 Presenter 가 1:1 관계이고, 

- Activity 에서 View 관련 업데이트를 해야할 종류가 많을 경우 interface 에 그만큼 메소드를 정의해야하기 때문에 불편하다는 생각이 있었으므로, 


Activity 와 Presenter 를 단순 has-a 관계로 처리하고자 하였습니다.


그리하여, Activity 구현 부는 다음과 같이 View 만 업데이트하는 구조를 취하게 되었습니다. Activity 자체가 WeakReference 관리를 할 필요는 없는것이죠.


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
public class SplashActivity extends Activity {
    private SplashPresenter splashPresenter;
 
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_splash);
 
        splashPresenter = new SplashPresenter(this);
    }
 
    @Override
    protected void onResume() {
        super.onResume();
        splashPresenter.delayFinished();
    }
 
    /**
     * Activity 종료
     * <p>
     * <pre>
     *     Activity 를 페이드아웃 애니메이션을 사용하며 종료.
     * </pre>
     */
    public void smoothFinishActivity() {
        setResult(RESULT_OK);
        finish();
        overridePendingTransition(R.anim.abc_fade_in, R.anim.abc_fade_out);
    }
}
cs


다음과 같은 구조를 통해, View 를 담당하는 Activity 클래스는 WeakReference 를 고려할 필요가 없게 되었으며, Activity instance 는 GC Time에 메모리 회수가 가능하게 되었습니다.



반응형
Posted by N'