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

하악...아래 질문드린 스레드 대기 관련하여 추가 질문 드립니다 ㅠ

0 추천
안녕하세요

메인코드 실행중 volley요청하여 응답받은 후

메인코드의 나머지를 실행하려 합니다.

근데 volley가 비동기식이라서 응답받기전에 메인코드가 실행되는 문제가 있었습니다.

 

어제 답변해주신거 참고해서 volley요청을 별도의 스레드로 분리시키고

메인스레드는 join으로 기다리게 한 후 다음코드를 실행하게 하려 했습니다..

 

그런데 이렇게 해도 volley에서

queue.add(request) 까지 한 후 스레드가 종료되고 메인스레드는 join을 통과해서 다음코드를 실행해버립니다.

제 의도는 volley에서 response까지 받은 후 join이 이루어지는 것인데 참 어렵네요 ㅜㅜ

 

따로 시도해본 방법1:

-volley요청부분을 함수로 만들고 / response받으면 전역 flag를 true로 바꿈

-메인부분은 volley요청 후 스레드 실행 > 스레드는 while문으로 flag를 검사.. true로 바뀌면 join이 이루어지고 다음코드 실행

->while문에서 진행이 안됩니다..

 

따로 시도해본 방법2:

-volley를 동기식으로(future?) 실행

-동기식으로 하니까 메인스레드가 기다리긴 하는데 /  volley요청이 timeout으로 실패합니다..

-검색결과 volley를 동기식 요청하려면 스레드를 따로 잡아야 한다고 해서 잡았더니

-메인에서 join이 없으면 이전과 같이 다음코드 바로 진행해버림 / join이 있으면 여전히 timeout으로 실패함..

 

ㅠㅠ정말 어렵네요.. 새벽까지 삽질해보다가 안돼서 질문드려봅니다 ㅜㅜ도와주세요..
하다 (640 포인트) 님이 5월 2일 질문

2개의 답변

0 추천
 
채택된 답변

제가 Volley를 사용해 본 적이 없는 관계로 Volley에 해당하는 코드는 생략할게요. 대신 콜백을 사용할 때 일반적인 접근방법을 설명드릴게요. 결론적으로 AsyncTask를 콜백을 이용해 사용해 보신 적이 있다면, 이것과 동일한 패턴을 사용하시면 됩니다. 콜백을 사용하는 Retrofit 같은 경우도 마찬가지이구요. 아주 일반적인 pattern입니다.

// 서버의  API를 호출할 클래스(클래스 이름은 님의 API가 어떤 처리를 하는지 몰라 임의로 주었습니다. 적절한 이름을 사용하시길0
class MyUseCase() {
    iinterface Listener {
          fun onMyUseCaseFetched(data: MyResponse)
          fun onMyUseCaseError(error: VolleyError)
    }

    private val listeners = hashSetOf<MyUseCase.Listener>()
    fun registerListener(listener: MyUseCase.Listener) {
         liseteners.add(listener)
    }

    fun unregiserListener(listener: MyUseCase.Listener) {
        liseteners.remove(listener)
    }

    
    fun execute() {
          val queue = Volley.newRequestQueue(applicationContext)
            var url = "https://..."
            var req = object: StringRequest(Method.POST, url, Response.Listener{
              response -> 
                 handleSuccess(response)
              }, Response.ErrorListener{error -> handleError(error) }
            ){
                override fun getParams(): Map<String, String>{
                    val params: MutableMap<String, String> = HashMap()
                    params["sql"] = sql문..
                    return params
                }
            }
            queue.add(req)
    }

    privae fun handleSuccess(response: MyResponse) {
         for (listener : listeners) {
             listener.onMyUseCaseFetched(response)
        }
   }


   privte fun handleError(error: VolleyError) {
        for (listener : listeners) {
             listener.onMyUseCaseError(error)
        }
   }

}

class MainActivity : AppCompatActivity(), MyUseCase.Linstener {
 
private val myUseCase by lazy { MyUseCase() }
   
  override fun onCreate(savedInstanceState: Bundle?) {
     super.onCreate(savedInstanceState)
}


public void onStart() {
   super.onStart()
    myUseCase.registerListener(this)
    fetchData()
}

public void onStop(...) {
   super.onStop(..)
   myUseCase.unregisterListener(this)
}

private fun fetchData() {
    myUseCase.execute()
}

override fun onMyUseCaseFetched(data: MyResponse) {
   // TODO : 여기에 원하는 처리를 하세요.
}
         
override fun onMyUseCaseError(error: VolleyError) {
   // TODO : 에러 처리
}


}

 

 API 호출 후의 처리는 콜백 안에서 하셔야 합니다.  서버 API를 비동기로 호출할 때의 처리는 대체로 위와 같이 동일합니다. 그리고 Volley 자체가 이미 백그라운드 쓰레드 처리와 메인쓰레드로의 전환을 구현해놓은 상태이기 때문에 님이 중복적으로 쓰레드를 다시 생성하려 하시는 것은 오버킬이라고 생각됩니다.

spark (47,900 포인트) 님이 5월 2일 답변
하다님이 5월 2일 채택됨
헉 감사합니다 열심히 공부해서 적용해볼게요!
0 추천
어떻게 처리하셨는지 peudo 코드만 보여주셔서는 뭐라 답을 드릴 수가 없네요. 그리고 대부분의 경우는 콜백만 제대로 처리를 하면 비동기라도 콜백이 완료된 후에 다음 코드를 진행하는 것은 문제가 없어야 하는데... 님의 이전 질문을 보면 콜백만으로도 별 문제없이 처리가 가능해야 할 것으로 보이는데.

콜백은 아주 일반적인 패턴입니다. 어차피 많은 곳에서 사용할 수 밖에 없어요. 그리고 Volley 사용법을 보면 그다지 복잡해 보이지는 않아 보이는데요. 제 개인적인 생각으로는 API 호출을 하고나서 화면에 데이터를 보여주려고 하는 경우라면, 콜백 만으로도 전혀 문제가 없어야 하고, 또 그렇게 구현하시는게 best practice 입니다.  꼭 동기식으로 처리를 하시고 싶다면 Kotlin Coroutine 같은 걸 도입하시면 랭귀지 레벨에서 쉽게 가능하구요.
spark (47,900 포인트) 님이 5월 2일 답변
대략적인 코드는 아래와 같습니다..

class MainActivity : AppCompatActivity() {
  override fun onCreate(savedInstanceState: Bundle?) {
    var t = thread()
        
    t.start()
    t.join()

    //Log.d(이후실행) //여기가 후에 실행돼야 함

inner class thread: Thread() {
        override fun run() {
            val queue = Volley.newRequestQueue(applicationContext)
            var url = "https://..."
            var req = object: StringRequest(Method.POST, url, Response.Listener{
              response -> Log.d(1번) //여기가 먼저 실행돼야 함
              }, Response.ErrorListener{error... }
            ){
                override fun getParams(): Map<String, String>{
                    val params: MutableMap<String, String> = HashMap()
                    params["sql"] = sql문..
                    return params
                }
            }
            queue.add(req)
           //여기까지 실행하고 스레드 종료되어 버리는듯함
        }
    }
...