ListAdapter submitList 후 로직 수행하기

berry·2023년 11월 1일
1

문제 상황

현재 프로젝트에서 댓글창에서 사용하는 RecyclerView 의 Adapter 로 ListAdapter 를 사용하고 있다.
뷰모델에서 서버로부터 댓글들을 받아와서 ListAdapter 의 submitList 함수를 이용하여 리사이클러뷰에 새로운 아이템을 반영해준다.
그리고 만약 새 댓글이 정상적으로 전송이 되면 smoothScrollToPosition(0) 함수를 호출하여 리사이클러뷰의 맨 위로 스크롤이 되게 했다.

그러나 위의 코드를 실제로 실행해보면 최상단으로 스크롤이 되지 않는다.

뭐가 문제일까?
일단 ListAdapter 가 뭔지 그 작동 방식을 살펴보자.

ListAdapter 가 뭐야?

내용이 너무 많아져서 따로 글을 썼다. 읽고 오자!
https://velog.io/@woonyumnyum/ListAdapter-%ED%8C%8C%ED%97%A4%EC%B9%98%EA%B8%B0

핑구의 글도 읽고 오자!
https://velog.io/@pingu244/DiffUtil%EC%9D%B4%EB%9E%80-%EB%AC%B4%EC%97%87%EC%9D%B4%EB%83%90

문제 해결

이제 어떤 문제인지 감이 온다.
ListAdapter 의 submitList 함수는 내부적으로 background thread 에서 비동기적으로 동작하기 때문에, 새 아이템이 반영되기 전 smoothScrollToPosition 함수가 실행된 것이다.
따라서 새 아이템이 반영된 후 최상단으로 스크롤 되도록 구현해야 한다.

방법1: ListAdapter 의 submitList 함수에 callback 함수를 넘긴다.

그러면 새로운 List 의 반영이 완료되면 callback 함수가 실행될 것이다.

recyclerView.apply {
            val commentAdapter: CommentAdapter = adapter as? CommentAdapter ?: return
            commentAdapter.submitList(
                succeedCommentResponseUiState.comments
            ) { smoothScrollToPosition(0) }
	}

방법2: View.post 메소드를 이용한다.

View.post 메소드를 살펴보자

View.post 메소드의 정의는 다음과 같다.

 public boolean post(Runnable action)
  • Runnable 을 메시지 큐에 추가한다. Runnable은 ui thread 에서 실행된다.
  • Runnable 메시지 큐에 성공적으로 배치되면 true를 반환한다. 실패하면 false 를 반환하는데, 그 이유는 일반적으로 메시지 큐를 처리하는 Looper 가 종료 중이기 때문이다.

함수가 어떻게 동작하는지 구현부를 살펴보자

public boolean post(Runnable action) {
        final AttachInfo attachInfo = mAttachInfo;
        if (attachInfo != null) {
            return attachInfo.mHandler.post(action);
        }

        // Postpone the runnable until we know on which thread it needs to run.
        // Assume that the runnable will be successfully placed after attach.
        getRunQueue().post(action);
        return true;
    }
  • 만약 AttachInfo 가 null 이 아니라면 Handler.Post 함수를 실행한다.

    Handler.post 함수는 실행 가능 Runnable 을 메시지 큐에 추가한다. 이때 핸들러는 ui thread 에 연결되어 있기 때문에 Runnable 은 ui thread 에서 실행된다.

  • 만약 AttachInfo 가 null 이라면 실행이 필요한 스레드를 알 때까지 Runnable 을 연기한다.

    뷰가 Attach 된 후 Runnable이 성공적으로 배치되었다는 가정 하에 Run Queue 를 얻어서 Runnable 에 대해 Queue의 post 함수를 실행한다.
    이때 getRunQueue() 로 얻어지는 객체는 핸들러가 연결되지 않은 경우 뷰에서 보류 중인 작업을 큐에 대기열에 추가하는 데 사용되는 클래스이다.
    AttachInfo: 뷰가 parent window 에 연결될 때 뷰에 제공되는 정보 집합

해결하기

post 메소드로 넘겨진 람다 smoothScrollToPosition 메소드가 메시지큐에 추가된다.
그리고 해당 메시지는 submitList 가 완료된 후 실행될 것이다!
그럼 List 가 반영된 후 최상단으로 스크롤 하게 되니까 정상적으로 실행된다!

recyclerView.apply {
            val commentAdapter: CommentAdapter = adapter as? CommentAdapter ?: return
            commentAdapter.submitList(
                succeedCommentResponseUiState.comments
            )
            post { smoothScrollToPosition(0) }
        }

결론

submitList 에 콜백을 넘겨서 실행시키는 방법과 View.post 함수를 이용하는 방법 둘다 Message Queue 에 메시지를 넣고 Handler 의 post 함수를 사용하는 방법이다.

해결!

댓글 전송에 성공하면 리사이클러뷰의 최상단으로 자동 스크롤이 잘 되는 것을 볼 수 있다!


참고

profile
공부 내용 기록

0개의 댓글