지난 시간에 이미지 데이터를 서버에서 받아 왔는데
이제 동영상 정보도 가져와 줄거다
우선은 데이터를 받을 모델을 하나더 만들어야한다
HTTP/1.1 200 OK
Content-Type: application/json;charset=UTF-8
{
"meta": {
"total_count": 6033,
"pageable_count" : 800,
"is_end": false
},
"documents": [
{
"title": "AOA 지민·김용만, 돼지꼬리 맛에 정신혼미 ‘극찬세례’",
"play_time": 185,
"thumbnail": "https://search2.kakaocdn.net/argon/138x78_80_pr/FRkbdWEKr4F",
"url": "http://tv.kakao.com/channel/2653417/cliplink/304487728?playlistId=87634",
"datetime": "2017-05-06T00:36:45+09:00",
"author": "_SBS"
},
...
]
}
동영상은 이런식으로 데이터를 받아오는데, 이전에 만든 이미지 뷰의 데이터 클래스가
data class ImageModel(
val meta: Meta,
val documents: List<Document>
)
data class Meta(
val total_count: Int,
val pageable_count: Int,
val is_end: Boolean
)
data class Document(
val collection: String,
val thumbnail_url: String?,
val image_url: String,
val width: Int,
val height: Int,
val display_sitename: String,
val doc_url: String,
val datetime: String
)
이렇게 되어있다.
이걸 이용해서 만들순없을까
data class ApiResponse<T>(
val meta: Meta?,
val documents: List<T>?
)
data class Meta(
@SerializedName("total_count")
val totalCount: Int?,
@SerializedName("pageable_count")
val pageableCount: Int?,
@SerializedName("is_end")
val isEnd: Boolean?
)
data class ImageDocument(
val collection: String?,
@SerializedName("thumbnail_url")
val thumbnailUrl: String?,
@SerializedName("image_url")
val imageUrl: String?,
val width: Int?,
val height: Int?,
@SerializedName("display_sitename")
val displaySitename: String?,
@SerializedName("doc_url")
val docUrl: String?,
val datetime: String?
)
data class VclipDocument(
val title: String?,
val url: String?,
val datetime: String?,
@SerializedName("play_time")
val playTime: Int?,
val thumbnail: String?,
val author: String?
)
제네릭 클래스를 이용해서 이런식으로 만들어 줬다
제네릭 클래스는 메소드에서 사용할 데이터 타입을 나중에 확정하는 기법인데
이를 이용해서 다양한 타입의 문서 목록(documents)을 갖을 수 있도록 설계했다.
이렇게 함으로써 이미지 검색 API 응답과 비디오 클립 검색 API 응답을 동일한 클래스로 처리할 수 있다
이제 데이터를 불러오는 레포지토리를 수정 해보자
interface ApiRepository{
suspend fun searchData(query: String, sort: String, page: Int): MutableList<SearchModel>
}
일단 저번에 만든 레포지토리를 파사드 패턴으로 바꿔주고
if (imageApi.isSuccessful) {
imageApi.body()?.documents?.map { document ->
SearchModel(
image = Uri.parse(document.image_url),
title = document.collection,
site = document.display_sitename,
date = dateTimeFormatter(document.datetime),
doc_url = document.doc_url
)
}?.let { searchList.addAll(it) }
}
이미지 데이터를 불러올때 만든 이 로직을 수정해 줄거다
이런느낌으로 동영상 부분도 똑같이 만들고 리스트를 정렬 해주면될까 싶었는데
if (imageApi.isSuccessful) {
imageApi.body()?.documents?.map { document ->
SearchModel(
thumnail = document.imageUrl.toString(),
title = document.collection,
siteName = document.collection.toString(),
date = dateTimeFormatter(document.datetime),
docUrl = document.docUrl.toString()
)
}?.let { imageApiResults.addAll(it) }
}
if (vclipApi.isSuccessful) {
vclipApi.body()?.documents?.map {
...
}?.let { vclipApiResults.addAll(it) }
}
val combinedResults = imageApiResults + vclipApiResults
combinedResults.sortedByDescending { LocalDateTime.parse(it.date, formatter) }.toMutableList()
searchList = combinedResults
return searchList
}
뷰에선 이미지 부분이 먼저 불러와지고 비디오 부분이 불러와지면서 정렬이 안됐다
데이터가 불러와질때 마다 데이터를 처리를 해주고 리턴해줘서 정렬이 안되는거 같은데
예상만할뿐 정확한 이유는 자세히 정의하진 못하겠다. //
return withContext(Dispatchers.IO) {
val imageResults = searchImage(query, sort, page).documents?.map { convertImageSearchModel(it) } ?: listOf()
val vclipResults = searchVclip(query, sort, page).documents?.map { convertVideoSearchModel(it) } ?: listOf()
val combinedResults = imageResults + vclipResults
combinedResults.sortedByDescending { LocalDateTime.parse(it.date, formatter) }.toMutableList()
}
이전 코드에서 다른 실수가 있었던것 같다 글 을 쓰면서 비슷하게 새로 만들어 봤는데
정렬이 잘 된다...
하지만 단점으로는 이미지와 동영상 데이터가 순차적으로 처리된다.
이미지 데이터와 동영상데이터를 병렬로 함께 처리하는게 더 효율 적이다
어쨋든 이 상황을 해결 하려면 결국 비동기적인 데이터를 묶어주고 데이터가 전부 불러와질때까지 기다리게 해줘야 하는데
async와 await를 사용해서 구현했다.
override suspend fun searchData(query: String, sort: String, page: Int): MutableList<SearchModel> {
if (page == 1) searchList.clear()
searchList.addAll(loadApiData(query,sort,page))
return searchList
}
private suspend fun loadApiData(query: String, sort: String, page: Int): MutableList<SearchModel> {
return withContext(Dispatchers.IO) {
val imageResultsDeferred = async { searchImage(query, sort, page)
.documents?.map { convertImageSearchModel(it) } }
val vclipResultsDeferred = async { searchVclip(query, sort, page)
.documents?.map { convertVideoSearchModel(it) } }
val imageResults = imageResultsDeferred.await() ?: listOf()
val vclipResults = vclipResultsDeferred.await() ?: listOf()
val combinedResults = imageResults + vclipResults
combinedResults.sortedByDescending { LocalDateTime.parse(it.date, formatter) }.toMutableList()
}
}
private fun convertImageSearchModel(document: ImageDocument): SearchModel {
return SearchModel(
thumnail = document.imageUrl.toString(),
title = document.displaySitename.toString(),
siteName = document.collection.toString(),
postType = "Image",
date = dateTimeFormatter(document.datetime.toString()),
docUrl = document.docUrl.toString()
)
}
private fun convertVideoSearchModel(document: VclipDocument): SearchModel {
return SearchModel(
thumnail = document.thumbnail.toString(),
title = document.title.toString(),
siteName = document.author.toString(),
postType = "Video",
date = dateTimeFormatter(document.datetime.toString()),
docUrl = document.url.toString()
)
}
async로 이미지 데이터와 , 동영상 데이터를 병렬처리해서 받은뒤 await해서 다 받을때까지 기다려준뒤 정렬 해준다.
이렇게 되면 병렬적으로 데이터를 받기 때문에 복수데이터를 처리 할때 훨씬 효율적으로 받을 수 있고, 완전한 데이터가 생기고 난 다음 로직이 수행되기 때문에 불필요한 작업을 줄일수 있다.