지금까지는 일시 중단 코드 작성을 launch, async와 같은 코루틴 빌더를 이용하는 것으로 제한해 왔지만, 코틀린은 더 많은 방법을 제공
일시 중단 함수를 배워보고 지금까지 사용했던 비동기 함수와 비교
코루틴 컨텍스트와 그 사용법
URL뿐만 아니라 이름으로도 피드를 식별할 수 있기 원하기 때문에,
새 모델 파일 안에 Feed라는 이름의 데이터 클래스를 만듦
클래스는 각 피드에 대한 name과 url을 쌍으로 갖음
MainActivity에 있는 feeds의 내용을 Feed 유형이 되도록 업데이트 가능👌
목록의 마지막 요소는 잘못된 피드라는 점에 유의💣
private val feeds = listOf( Feed("npr","https://www.npr.org/rss/rss.php?id=1001"), Feed("fox","https://feeds.foxnews.com/foxnews/politics?format=xml"), //"https://rss.cnn.com/rss/cnn_topstories.rss", //<- 사이트 변경으로 인하여 오류 발생때문에 주석처리 Feed("inv","htt://myNewsFeed") )
Deferred<List<Article>>
을 반환asyncLoadNews() 에서의 요청 목록을 적절하게 업데이트해야 함
RecyclerView의 기본 개념은 비용이 많이 드는 뷰 생성 프로세스를 최대한 피한다는 것
대신 작은 뷰 세트가 생성돼 재사용되며, RecyclerView라는 이름을 통해 사용자가 스크롤할 때 정보를 표시
이 작업을 위해서 ViewHolder라는 것이 필요,
ViewHolder 는 나중에 재사용될 수 있는 뷰의 레이아웃 요소를 갖는 객체
각 항목에 대해 정의한 레이아웃을 기반으로 어댑터 안에 ViewHolder를 생성
↪ ViewHolder 클래스는 RecylerView.ViewHolder를 확장하고
레이아웃 자체를 super 생성자에 전달한다는 점을 주목⚡
어댑터가 작동하려면 어떻게 ViewHolder를 생성하고 내용을 대체할 수 있는지 알아야하고, 현재 갖고 있는 요소의 양을 반환할 수 있어야 함
첫 번째 단계로 ViewHolder 타입을 사용하는 RecyclerView.Adapter를 확장
작업이 끝나면 세 가지 함수를 구현해야 함
onCreateViewHolder()
새로운 ViewHolder를 생성할 때마다 호출된다. 필요에 따라 모든 뷰를 인플레이트하고 ViewHolder를 사용할 준비가 된 상태로 반환한다.
onBindViewHolder()
ViewHolder의 내용을 지정된 위치에 요소의 내용을 로드/치환하기 위해
호출된다. 뷰의 내용을 업데이트하기 위해 필요하다.
getItemCount()
어댑터가 갖고 있는 요소의 수량을 반환해야 한다.
inflate는 사전적으로 '부풀리다'라는 뜻인데,
View를 정의한 XML이나 자바 파일을 기반으로 View 객체 만드는 것을
의미한다.
개별 기사에 대해 정의한 XML 레이아웃을 인플레이트하고 정보를 표시하는 데 사용될 뷰를 찾을 것이다.
새로운 ViewHolder가 필요할 때마다 함수가 호출
여기서 전달 받은 위치(position)에 따라 기사를 검색하고, 그에 따라서 뷰의 텍스트를 로드해야 함.
함수는 첫 번째 그룹의 기사를 표시하기 위해 처음 호출되며 나중에 사용자가 기사의 내용을 변경하기 위해 스크롤하는 시점에 호출
article의 사이즈를 반환
현재는 어댑터에 기사를 추가하는 외부 클라이언트를 허용하는 어떠한 함수도 노출하지 않음
우리가 구현한 어댑터는 외부 클라이언트가 기사를 추가할 수 있는 어떤 함수도 노출하지 않고 있음
기사 그룹을 추가할 수 있도록 간단한 함수를 추가
List<Article>
를 뷰에 매핑할 수 있는 어댑터가 만들어졌음
-> 메인 액티비티에 추가한 RecyclerView와 함께 이 어댑터를 사용
OnCreate() 함수에서 이것들 모두를 인스턴스 화할 것
UI에서 기사를 실제로 표시할 수 있으려면 asyncLoadNews()를 업데이트해서 어뎁터에 검색된 요소를 추가
-> 이전에 남겨뒀던 TODO를 변경
프로그레스바를 숨기고 새 기사를 viewAdapter에 추가하는 코드를 추가
UI 테스트를 하기 전에, 데이터를 표시하기 전 일정 시간 동안 프로그레스바를 실제로 볼 수 있도록 asyncFetchArticles()에 약간의 지연 시간 추가
기사 중 일부는 summary 부분에 HTML 태그가 표시된다.
보통 이런 태그들은 summary가 HTML을 지원하는 뷰어에 표시되기 때문에 문제가 되지 않지만, 여기에서는 뷰어를 사용하지 않기 때문에 제거하는 것이 좋음
div 요소가 나오면 설명 부분을 잘라내기 위해 asyncFetchArticles()를 수정 필요!
위 코드에서는 <div 요소 전 까지의 내용을 summary로 가져온다.
summary의 내용이 <div로 시작한다면 가져오는 summary의 내용이 모두 없어진다.
launch(), async(), runBlocking()과 같은 코루틴 빌더를 사용해서 일시 중단 알고리즘의 대부분을 작성
코루틴 빌더를 호출할 때 전달하는 코드는 일시 중단 람다임
2장에서 동시성 코드를 구현할 때 코루틴 빌더 대신 비동기 함수를 사용하는 쪽이 더 편리한지에 대해 설명했음
이제 일시 중단 함수를 추가해 이 주제를 확장할 차례
우리는 비동기 함수를 구현한 Job을 (Deferred를 포함해서) 반환하는 함수라고 했다. 이러한 함수는 보통 launch() 또는 async() 빌더로 감싸인 함수이지만, 구현한 잡이 반환될 때만 비동기 함수로 본다.
잡 구현을 반환하는 함수가 있으면 어떤 시나리오에서는 편리할 수 있지만,
코루틴이 실행되는 동안에 일시 중단을 위해서
join() 이나 await() 를 사용하는 코드가 필요하다는 단점💣이 생김
기본 동작으로 일시 중지를 하고 싶으면 어떻게 해야 할까?
비동기 함수를 사용해 레파지토리를 설계하고 구현하는 방법을 살펴보자.
다음과 같이 데이터 클래스에서부터 시작
함수 이름이 이해하기 편리하게 돼 있음
-> 함수가 비동기라는 점을 명시하는 것이 중요
이러한 클라이언트의 성질로 인해, 호출자는 항상 요청이 완료될 때까지 일시 정지해야 하므로 보통 함수 호출 직후에 await() 호출이 있게 됨
f
구현은 Deferred와 엮이게 될 것
다른 유형의 퓨처(future)로 ProfileServiceRepository 인터페이스를
깔끔하게 구현하기 위한 방법은 없음
유연함
인터페이스의 상세 구현 내용은 노출되지 않기 때문에 퓨처를 지원하지 모든 라이브러리를 구현에서 사용할 수 있음
현재 스레드를 차단하지 않고 예상된 Profile을 반환하는 구현이라면 어떤 퓨처 운영도 동작할 것
간단함
순차적으로 수행하려는 작업에 비동기 함수를 사용하면 항상 await() 를 호출해야 하는 번거로움이 생기고, 명시적으로 async가 포함된 함수의 이름을 지정해야 함
일시 중단 함수를 사용하면 레파지토리를 사용할 때마다 이름을 변경하지 않아도 되고 await() 를 호출할 필요가 없어짐
다음 목록은 Deferred<T>
를 포함하는 구현과 관련된 Job을 사용
일반적으로 구현에 Job이 엮이는 것을 피하기 위해서는 일시 중단 함수를 사용하는 것이 좋음
인터페이스를 정의할 때는 항상 일시 중단 함수를 사용
비동기 함수를 사용하면 Job을 반환하기 위한 구현을 해야 함
마찬가지로 추상함수를 정의할 때는 항상 일시 중단 함수를 사용
가시성이 높은 함수일수록 일시 중단 함수를 사용해야 함
비동기 함수는 private / internal 함수로 제한돼야 함
비동기 함수를 사용하는 코드를 적은 범위로 제한하는 것이
Job을 더 이상 사용할 수 없을 때 리팩토리의 영향을 줄임