일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | 5 | 6 | 7 |
8 | 9 | 10 | 11 | 12 | 13 | 14 |
15 | 16 | 17 | 18 | 19 | 20 | 21 |
22 | 23 | 24 | 25 | 26 | 27 | 28 |
29 | 30 | 31 |
- Swift Package Manager
- convert base64
- SwiftUI
- 스크롤 탭
- 상단 탭바
- Tuist
- oberve url
- notifychanged
- 개발자 면접
- swift
- ViewBuilder
- scrolling tab
- pod install
- url 추적
- 기존 앱
- swift #swift keychain #keychain 사용법
- base64 변환
- transformation.map
- development language
- DevelopmentRegion
- detect url
- DataBinding
- List
- url 관찰
- UIPresentationController
- Android
- GeometryReader
- ios
- UIViewControllerTransitioningDelegate
- Side Menu
- Today
- Total
버그 잡이
Retrofit+liveData+moshi 로 네트워크 통신하기 본문
다음과 같은 클래스가 필요하다.
- data 클래스
- network api 인터페이스
- viewmodel
- fragment
1. data 클래스
- 기본적으로 Article 클래스만 있어면 된다.
- 나는 넘어오는 json이 json object가 3번 중첩되는 구조라 이를 처리하기 위해서 class를 추가로 만들었다.
data class ResponseData(
@Json(name = "tistory")
val tistory: Tistory
)
data class Tistory(
@Json(name="item")
val item: Item
)
data class Item(
@field:Json(name = "posts")
val posts: List<Article>
)
@Parcelize
data class Article(
val id: String,
val title: String,
val postUrl: String,
val visibility: String,
val categoryId: String,
val comment: String,
val trackbacks: String,
val date: String
):Parcelable{}
2. networkAPI
- 여기가 핵심이다.
1) 레트로핏 객체를 생성해준다. -> moshi를 converterFactory로 넣어준다.
2) ApiService를 인터페이스로 구현해준다.(여기서 각종 쿼리문을 작성하면 레트로핏이 알아서 처리해준다.)
-> getArticles() 에서 deffered를 활용했는데 이는 코루틴을 활용하기 위함이다.
3) BlogApi라는 object를 만들어서 어디서든지 사용할 수 있게 한다.
private const val BASE_URL = "https://www.tistory.com/apis/post/"
private val retrofit = Retrofit.Builder()
.addConverterFactory(MoshiConverterFactory.create())
.addCallAdapterFactory(CoroutineCallAdapterFactory())
.baseUrl(BASE_URL)
.build()
interface BlogApiService {
@GET("list?access_token=[티스토리API 토큰값]&output=json&blogName=jinsangjin&page=1")
fun getArticles(): Deferred<ResponseData>
}
object BlogApi{
val retrofitService: BlogApiService by lazy{
retrofit.create(BlogApiService::class.java)
}
}
3. ViewModel
- 기본 retrofit 콜백이 아니라 코루틴 콜백을 사용하였다.
-> 코루틴을 활용하면 스레드를 제어할 수 있기 때문에 viewmodel이 소멸되면 레트로핏 작업을 멈출 수 있다.
- 코루틴 관련
1) coroutineScope를 뷰모델+Main로 했다.
- viewmodel은 뷰모델 생명주기에 맞추기 위함이고
- Main 스레드로 한 이유는 레트로핏 자체가 백그라운드에서 작업해주기 때문에 다른 새로운 스레드를 사용할 필요가 없다.
2) await()
- (await이 실행될때까지 해당 코루틴이 완료되지 않았다면) 해당 항목이 완료될때까지 작업을 기다리는 메서드이다.
- 즉 여기서는 listResult를 반환할때까지 기다리고 이후 작업을 진행하는 것이다.
- why?) 그래야 결과값에 따라 에러 처리를 하거나 특정 결과를 나타낼 수 있다
class BlogViewModel : ViewModel(){
private val _response = MutableLiveData<List<Article>>()
val response: MutableLiveData<List<Article>>
get() = _response
enum class BlogApiStatus{LOADING, ERROR, DONE}
private val _status = MutableLiveData<BlogApiStatus>()
val status: LiveData<BlogApiStatus>
get() = _status
private var viewModelJob = Job()
private val coroutineScope = CoroutineScope(viewModelJob + Dispatchers.Main)
init {
getArticleList()
}
private fun getArticleList(){
coroutineScope.launch {
var getArticlesDeferred = BlogApi.retrofitService.getArticles()
try{
_status.value = BlogApiStatus.LOADING //기본 로딩 상태
var listResult = getArticlesDeferred.await()
_response.value = listResult.tistory.item.posts
_status.value = BlogApiStatus.DONE //성공시 완료 상태
}catch (t: Throwable){
//_response.value = "Failure: " + t.message
_status.value = BlogApiStatus.ERROR //에러시 에러 상태
}
}
}
}
4. Fragment
- observe로 관찰하면서 데이터를 최신화 한다.
class BlogFragment : Fragment() {
//lazy로 늦게 선언. 최초 사용될때 괄호 안과 같이 정의됨
private val viewModel: BlogViewModel by lazy{
ViewModelProviders.of(this).get(BlogViewModel::class.java)
}
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
val binding = FragmentBlogBinding.inflate(inflater)
binding.setLifecycleOwner(this)
binding.viewModel = viewModel
val viewAdapter = BlogListAdapter()
binding.blogRecyclerView.adapter = viewAdapter
viewAdapter.onItemClickListener = {
//Toast.makeText(requireContext(), "${articleData.title}", Toast.LENGTH_SHORT).show()
}
viewModel.response.observe(this, Observer {
viewAdapter.submitList(it)
})
viewModel.status.observe(this, Observer {
when(it){
BlogViewModel.BlogApiStatus.LOADING -> {
binding.statusText.setText("로딩중..")
binding.statusText.visibility = View.VISIBLE
}
BlogViewModel.BlogApiStatus.ERROR -> {
binding.statusText.setText("네트워크 에러")
binding.statusText.visibility = View.VISIBLE
}
BlogViewModel.BlogApiStatus.DONE -> {
binding.statusText.visibility = View.GONE
}
}
})
return binding.root
}
}
*gradle
//retrofit
implementation "com.squareup.retrofit2:retrofit: $version_retrofit"
implementation "com.squareup.retrofit2:converter-scalars:$version_retrofit"
//moshi - json 처리해주는 라이브러리
implementation "com.squareup.moshi:moshi:$version_moshi"
implementation "com.squareup.moshi:moshi-kotlin:$version_moshi"
implementation "com.squareup.retrofit2:converter-moshi:$version_retrofit"
//retrofit & coroutine
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:$version_kotlin_coroutines"
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:$version_kotlin_coroutines"
implementation "com.jakewharton.retrofit:retrofit2-kotlin-coroutines-adapter:$version_retrofit_coroutines_adapter"
'모던 안드로이드 > Udacity Android with kotlin' 카테고리의 다른 글
[Udacity android with kotlin] 5. Architecture - viewmodel (0) | 2020.06.15 |
---|---|
[advanced android] android에서 Test란? TDD란? #Test와 AAC (1) | 2020.04.21 |
(ViewModel+LiveData)Bottom Navigation 수직/수평 전환시 fragment 초기화 문제 해결 #configuration change (0) | 2020.04.16 |
(AAC 응용) LiveData+Room+RecyclerView #DiffUtil (0) | 2020.04.15 |
(AAC 응용)ViewModel+LiveData+Room으로 ToDoList 앱 만들기 (0) | 2020.04.14 |