콜백함수

 

콜백함수란?

  • 비동기 개념이 선행되어야 함
    • 메인 스레드외에 다른 스레드를 사용해서 작업을 병렬적으로 (동시수행) 처리하는 것
  • 다른 스레드가 진행중이던 작업이 끝났을 경우 호출하는 함수가 콜백함수
    • 메인 스레드는 이 콜백함수를 이용해서 의도한 결과가 나올 수 있도록 다른 스레드에서 완료한 작업에 뒤이어서 진행할 수 있음

구체적인 예시

  • 콜백함수가 없어서 에러 나는 코드
recyclerView_notices.addOnScrollListener(object : RecyclerView.OnScrollListener() {
    override fun onScrolled(recyclerView: RecyclerView, dx: Int, dy: Int) {
        super.onScrolled(recyclerView, dx, dy)
 
        if (!recyclerView_notices.canScrollVertically(1){
            공지사항을 불러옴 // (네트워크 작업)
            리스트에 공지사항을 추가하고 새로고침 // (UI 작업)
        }
})
  • 콜백함수 사용
class MainActivity : AppCompatActivity(), PostListener { // PostListener를 구현한다.
 
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        
        recyclerView_notices.addOnScrollListener(object : RecyclerView.OnScrollListener() {
            override fun onScrolled(recyclerView: RecyclerView, dx: Int, dy: Int) {
                super.onScrolled(recyclerView, dx, dy)
 
                if (!recyclerView_notices.canScrollVertically(1){
                    네트워크 작업(this) // this는 listener를 뜻한다.
                    // 여기서 했던 UI 작업을 loadPage 함수에서 한다.
                }
            }
        })
    }
    
    override fun loadPage(notices: ArrayList<NoticeList>) { // override 함
        UI 작업
    }
}

class 네트워크 작업(listener: PostListener){
    var mCallback = listener
    ...
    ...
    ...
    mCallback.loadPage(notices) // 작업이 다 끝나고 loadPage를 호출한다.
}
  • 위 그림의 네트워크 작업은 비동기 함수
    • 즉 네트워크 작업이 완벽히 끝났는지 여부는 상관없이 메인 쓰레드는 바로 네트워크 작업 호출 이후 UI 작업 호출함
    • 네트워크 작업은 완료하는데 까지 상대적으로 오래걸리기 때문에 UI 작업을 하기 위해 필요한 리스트가 Null로 넘어오는 에러 발생
      • 네트워크 작업이 끝난 이후 UI 작업이 진행되야 함
      • 이때 필요한 것이 콜백함수

간단한 코드 예시

자바

private interface OnStringCallback {
    void onCallback(String string);
}

private void testCallback(OnStringCallback callback) {
    callback.onCallback("hello callback");
}
testCallback(new OnStringCallback() {
    @Override
    public void onCallback(String string) {
        Log.d("Test","Called : "+string);
    }
});

코틀린

private fun testCallback(callback: ((String)->Unit)) {
    // String 값을 콜백 받을것인데 return의 기본값은 Unit으로 설정한다

    callback.invoke("hello callback")
}
testCallback {
    Log.d("Test","Called : "+it)
}

좀더 자세한 코드 (자바)

// 선언과 정의의 단어 차이
//// 선언은 메모리에 안 올라감, 정의는 메모리에 올라감

// 콜백 클래스 선언
// 나머지 값을 뱉어주는 콜백 클래스 작성할 것임

class CallClass{

    // 인터페이스 선언 (뼈대만. 기능 구현은 메인클래스에서)
    interface onRestNumberCb{
        void onRestNumber(int Number, int Rest);
    }

    // Number, DivNumber, CallBack 선언
    private int Number = 0;
    private int DivNumber = 0;
    private onRestNumberCb myCallBack;      // 위에서 정의한 인터페이스의 타입임을 주목

    public void setOnRestNumberCb(onRestNumberCb callBack){

        // 객체지향적 코드를 만들기 위해 CallBack 함수를 위한 set함수 선언
        myCallBack = callBack;      
    }

    public void setDivNumber(int div){

        // 역시나 객체지향적 코드를 위한 set
        DivNumber = div;
    }

    public void addNumber(int adder){
        Number = adder;

        if(myCallBack != null){
            myCallBack.onRestNumber(Number, Number % DivNumber);
        }
    }
    
}
public class study{
       public static void main(String[] args){
           CallClass total = new CallClass();

            // 인터페이스 선언을 해놓은 상태임
            // 구체적 기능은 여기 Main 클래스에서 아래와 같이 재정의할 수 있음
            // 인터페이스는 의도한 대로 구조만 형성해주고, 그 구조에서 돌아가는 "기능"을 사용자가 직접
            // 정의해준다고 생각하면 편할 듯
           CallClass.onRestNumberCb callBack = new CallClass.onRestNumberCb(){

               @Override
               public void onRestNumber(int Number, int Rest){
                   System.out.println(Number + "를 5로 나눈 나머지는 " + Rest + " 입니다.");
               }
           };

           // 나눌값 셋팅
           total.setDivNumber(5);
           
           // callback 셋팅
           // Main에서 정의해준 callback 함수의 기능을 살려서 이제 "객체" 만들어주는 것임
           total.setOnRestNumberCb(callBack);

           for(int i = 1; i <= 100; i++){
               total.addNumber(i);
           }
       }
}

안드로이드에서의 활용

registerForActivityResult

  • 핸드폰내의 이미지 가져오는 코드
val getContent = registerForActivityResult(GetContent()) { uri: Uri? ->
    // Handle the returned Uri
    
    // 여기서 갤러리의 이미지 Uri를 어떻게 처리할 지 정할 수 있음
}

override fun onCreate(savedInstanceState: Bundle?) {
    // ...

    val selectButton = findViewById<Button>(R.id.select_button)

    selectButton.setOnClickListener {
        // Pass in the mime type you'd like to allow the user to select
        // as the input
        getContent.launch("image/*")
    }
}

참고한 블로그
developer.android.com