MVP 패턴을 통한 메모리 누수 관리
현재 개발하고 있는 앱의 메모리 누수가 감지되고 있습니다. ㅡㅡ^
메모리 누수 감지는
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에 메모리 회수가 가능하게 되었습니다.
'개발이야기 > 안드로이드' 카테고리의 다른 글
RxJava2 를 이용한 Collection 데이터 처리. (0) | 2017.02.27 |
---|---|
WeakReference 테스트 (2) | 2016.11.15 |
액티비티 개수 구하기 (0) | 2016.11.15 |
안드로이드 DOZE 모드 만들기 (0) | 2016.11.15 |