마스터Q&A 안드로이드는 안드로이드 개발자들의 질문과 답변을 위한 지식 커뮤니티 사이트입니다. 안드로이드펍에서 운영하고 있습니다. [사용법, 운영진]

코루틴 launch 안에서 함수 호출 시, 병목 현상 및 오류 질문 드립니다

0 추천

2일정도 계속 수정해보고 있는데 영 답을 못찾겠어서 질문 드립니다..

job = CoroutineScope(Dispatchers.Default).launch {
    val events = mutableListOf<EventData>()
    for (no in 0..6) {
        yield()
        val startTimeMillis = calendar.timeInMillis + 86400000*no
        val endTimeMillis = startTimeMillis + 86400000
        events.addAll((activity as MainActivity).getEvents(startTimeMillis,endTimeMillis))
    }
    withContext(Dispatchers.Main) {
        binding.relativeLayoutEvents.removeAllViews()
        createEvent(events)
    }
}

뷰페이저(프래그먼트로 구성) MainActivity에서 함수를 호출하게 되는데,

fun getEvents(startTime: Long, endTime: Long): MutableList<EventData> {
    Log.d("태그","시작: ${sdf.format(startTime)}")
    val calendar = Calendar.getInstance()
    calendar.timeInMillis = startTime
    calendar.firstDayOfWeek = Calendar.SUNDAY
    val dayOfTheWeek = calendar.get(Calendar.DAY_OF_WEEK) - calendar.firstDayOfWeek
    val events = mutableSetOf<EventData>()
    val projection = arrayOf(
        CalendarContract.Events.CALENDAR_ID,
        CalendarContract.Instances.EVENT_ID,
        ~이후 생략~

Google calendar API를 통하여 이벤트를 불러오는 함수 입니다.

문제는 뷰페이저가
offscreenPageLimit = 1
로 되어있어서 동시에 3개의 프래그먼트가 생성, -> 함수 호출이 되는데

동시에 호출되면 내부 구조에 문제가 생기는 지 파라미터 값이 변형되거나
다른 기타 에러들이 생기는 데 (Dispachters.Main)이나 아예 메인스레드에서 실행할 경우 문제 없이 실행이 됩니다. (렉 빼고는...)

(제가 질문을 조리있게 못 드린 것 같아서 나름 간단하게 도식화 했습니다.)

혹시 위의 방법에서 잘못된 점이나
아니면 추천하실 만한 해결방법이 있을까요?

 

추측하는 가장 문제는

API에서 받아올 때

while (cursor!!.moveToNext()) 로 받아오고 있는데

반복문에서 간헐적으로(꽤나 자주) 탈출하게 됩니다.

서한 (330 포인트) 님이 2021년 12월 20일 질문
서한님이 2021년 12월 20일 수정

1개의 답변

0 추천
 
채택된 답변
Dispatcher.Default는 백그라운드 쓰레드를 사용합니다.  뷰에 접근할 때는 Dispathcer.Main을 사용하셔야 하구요. MainActivity는 당연히 View에 해당하므로 제 생각에는 Dispatcher.Default를 이용하여 MainActivity.getEvents를 받아오는 건 바람직하지 않다고 보여집니다.

getEvents가 함수의 이름대로라면 calendar에 보여지는 데이터를 가져오는 함수일텐데, 데이터를 데이터소스에서 가져오는 행위는 뷰가 아니라 데이터를 처리할 수 있는 레이어에서 하는 것이 바람직합니다. 예를 들어 ViewModel이 데이터소스를 통해서 백그라운드 쓰레드 안에서 UseCase나 Interactor또는 Repository 통하여 데이터를 가져오도록 하는 것이 일반적입니다.

데이터 처리하는 부분을 ViewModel 같은 데서 DataSource(DB 처리)에서 백그라운드 쓰레드를 통하여 가져오게 하시고, ViewModel에서는 뷰에 필요한 구조의 데이터로 변환하여, 이걸 화면에서 보여주시는 구조로 바꾸시길 권장합니다.
spark (227,470 포인트) 님이 2021년 12월 20일 답변
서한님이 2021년 12월 20일 채택됨
그리고 액티비티나 프레그먼트에서 사용할 수 있는 CoroutineScope이 이미 존재합니다. 따로 만들지 마시고 필요하다면 lifecycleScope을 사용하면 됩니다. 검색해 보세요. 그리고
왜  launch 안에서 굳이 yield를 호출하고 계신지 궁금하네요.
코루틴이 실행되고있는 중에 job이 cancel되는 경우가 존재해 중간에 취소처리를 잡아내기 위하여 yield를 호출했는데 상관이 없을까요??

답변 감사합니다 저번부터 계속 도움 잘 받고 있습니다.
해당 증상은 아마도 라이프싸이클 때문에 발생할 겁니다. 코루틴 실행 중에 액티비티가 destory되거나 하면 job을 취소해 주어야 하는데, 제가 말씀드린 lifecycleSccope이나 ViewModel의 viewModelScope 을 사용하면 해당 처리를 라이프사이클에 따라 자동으로 해줍니다.
...