- android.view.WindowManagerGlobal: findViewLocked / android memory leak 해결하기 (근데 이제 fragment 를 곁들인)2022년 08월 06일 00시 22분 40초에 업로드 된 글입니다.작성자: 핀수728x90반응형
android.view.WindowManagerGlobal: findViewLocked
🥺 activity 가 finish 상태일 때 dialog 를 dismiss() 하려고 할 때 생기는 오류
에러 발생 상황
- 로그인 화면에서 로그인 저장 체크박스에 체크를 하고 로그인 버튼을 바로 누르는 경우
- 체크를 하면 CustomToast 가 뜬다.
- 로그인 버튼을 누르면 홈 화면으로 이동한다. (LoginActivity.finish())
- 로그아웃하는 경우
- 로그아웃을 클릭하면 한번 더 확인하는 CustomConfirmDialog 가 뜬다.
- ‘네’ 를 선택하면 로그아웃 되어 로그인 화면으로 이동한다.
해결 방안
💡 activity 가 finish 되기 전에 dialog 를 dismiss 해야한다.
첫번째 해결책
activity 가 destroy 될 때 다이얼로그들을 dismiss() 했다.
(내 커스텀 다이얼로그들은 싱글톤 패턴으로 구현이 되어있다.)
CustomToast.getInstance().dismiss();
결과 : 실패
android.app.ActivityThread: performDestroyActivity 에러가 발생했다.
두번째 해결책
다이얼로그 액션 인터페이스를 정의할 때 안드로이드에서 제공하는 다이얼로그 인터페이스처럼 alertDialog 를 인자로 넘겨준다.
binding.logoutTextView.setOnClickListener( v -> CustomConfirmDialogTest.getInstance().showDialog(mContext, "로그아웃하시겠습니까?", new CustomConfirmDialogTest.ConfirmDialogAction() { @Override public void setPositiveAction(AlertDialog alertDialog) { alertDialog.dismiss(); ... } @Override public void setNegativeAction() {/**/} }));
결과 : CustomToast 는 해결할 수 없다. → 커스텀 토스트에서도 같은 문제가 발생할 확률이 있는데, 해결할 수 없다는 의미 ⇒ 실패
세번째 해결책
각 다이얼로그 클래스들이 싱글톤으로 구현이 되어있기 때문에
각 객체들에 대해서 alertDialog 가 null 인지 아닌지 판별이 가능하다.
(예전의 로직은 계속 new 되는 상태였기 때문에 판별 불가능, 다이얼로그 중복 호출 가능성 있었음)
public void dismissDialog(){ if (alertDialog != null){ alertDialog.dismiss(); } }
해당 메소드를 activity 가 destroy 될 때 호출한다.
@Override protected void onDestroy() { super.onDestroy(); CustomConfirmDialog.getInstance().dismissDialog(); }
결과 : 성공
fragment memory leak
MVVM 패턴을 적용하고 나서 profiler 로 memory leak 을 확인했는데 몇몇 fragment 들이 제때 수거되지 않고 남아서 메모리 릭을 일으키는 것을 확인했다.
왜...?
MVVM 패턴을 적용하기 전에는 메모리 누수를 찾아볼 수 없었는데..
ViewModel 이 fragment 보다 생명주기가 길기 때문에 context 를 참조하지 않는것이 좋다고 해서 참조하지 않았고,
ViewBinding 도 오랫동안 참조될 가능성이 있다고 해서 context, viewBinding 도
생명주기에 맞춰서 모두 명시적으로 해제해주었는데 자꾸만 누수가 일어나서 답답했다.
특정 fragment 가 수거가 되질 않으니..fragment context인 activity context 도 GC 에게 수거되지 않았다.
그렇게 답답해 하다가 LeakCanary 를 알게 되었고
적용해 보았다. (LeakCanary 에 대한 것은 따로 글을 작성해보도록 하겠다.)
프로파일러로 확인하는 것보다 훨씬 직관적이고 알아보기 쉽게 되어있어서 편했다.
그래서 하나하나 살펴보는데 ConstraintLayout 과 Button 이 누수를 일으킨다고 하니까
이게 도대체 무슨 소리일까 미치고 팔짝 뛸 것 같았는데 자세히 보니까..
Dialog 가 떴었던 화면들이 계속해서 참조가 되고 있는 것을 알 수 있었다.
-> 특정 fragment 가 계속 남아있었던 이유도 그 화면에서는 무조건 dialog 가 뜨게 되어있기 때문이었다.
그렇다면 문제의 원인은 CustomDialog 에 있다는 것인데..
다이얼로그를 띄우려고 컨텍스트를 참조하긴 하는데…(이 컨텍스트는 FragmentActivity context 이고 아주 강한 참조라고 볼 수 있다.)
근데 인자를 통해서 받아오는데 뭐가 문제일까? 하다가
다이얼로그는 싱글톤으로 구현되어있고..alertDialog 가 static 으로 선언되어 있는 것을 확인했다.
AlertDialog.Builder builder = new AlertDialog.Builder(mContext); alertDialog = builder.create();
이때 참조되는 context 를.. static으로 선언된 alertDialog 가 계속 참조하고 있으니까
다이얼로그가 떴었던 화면들(fragment)이 제대로 수거될 수 없는 것이 아닐까 하는 생각을 했다.
그래서 아래와 같이 alertDialog 를 null 로 만들어 주었다.
public void dismissDialog(){ if (alertDialog != null){ alertDialog.dismiss(); alertDialog = null; } }
All retained objects have been garbage collected
해결이 됐다!
내가 생각한 것이 맞았던 건지는.. 정확히 알 수는 없지만 일단 현재까지 메모리 누수를 확인할 수 없었다.
alertDialog 에 대해 좀 더 정확히 공부해야겠다고 느꼈고
그동안 말로만 들어왔던 context 공포..(제대로 관리하지 못하면 어떻게 되는지를)
를 몸소 깨닫게 되었다.
References
아래 글을 참고하여 작성 되었습니다.
728x90반응형'pinslog > Log.daily()' 카테고리의 다른 글
다른 글로 대체합니다 (0) 2022.08.10 git remote 주소 변경하기 / ReactNative 빌드 오류 해결하기 (0) 2022.08.08 figma asset export / 여러개의 Vector Asset 한번에 import 하기 how to multi svg import in android studio (0) 2022.08.07 LeakCanary (0) 2022.08.07 commit 복구하기 / java list 자르기 (0) 2022.08.04 다음글이 없습니다.이전글이 없습니다.댓글 - 로그인 화면에서 로그인 저장 체크박스에 체크를 하고 로그인 버튼을 바로 누르는 경우