핀수로그
  • android.view.WindowManagerGlobal: findViewLocked / android memory leak 해결하기 (근데 이제 fragment 를 곁들인)
    2022년 08월 06일 00시 22분 40초에 업로드 된 글입니다.
    작성자: 핀수
    728x90
    반응형

     

       

      android.view.WindowManagerGlobal: findViewLocked

      🥺 activity 가 finish 상태일 때 dialog 를 dismiss() 하려고 할 때 생기는 오류

       

      에러 발생 상황

      • 로그인 화면에서 로그인 저장 체크박스에 체크를 하고 로그인 버튼을 바로 누르는 경우
        1. 체크를 하면 CustomToast 가 뜬다.
        2. 로그인 버튼을 누르면 홈 화면으로 이동한다. (LoginActivity.finish())
      • 로그아웃하는 경우
        1. 로그아웃을 클릭하면 한번 더 확인하는 CustomConfirmDialog 가 뜬다.
        2. ‘네’ 를 선택하면 로그아웃 되어 로그인 화면으로 이동한다.

       

      해결 방안

      💡 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

      아래 글을 참고하여 작성 되었습니다.

      https://dhna.tistory.com/382

      https://square.github.io/leakcanary/

      728x90
      반응형
      댓글