본문 바로가기

공부/안드로이드

[Android] Callback?

1. 정의

(1) 1단계 정의 : Callback (Wiki)

In computer programming, a callback is a piece of executable code that is passed as an argument to other code, which is expected to call back (execute) the argument at some convenient time. The invocation may be immediate as in a synchronous callback or it might happen at later time, as in an asynchronous callback.
  • 컴퓨터 프로그래밍에서 콜백은 다른 코드에 "인수로 전달되는 실행 가능한 코드 조각"으로, 편리한 시간에 인수를 콜백 (실행)해야합니다. 호출은 동기 콜백에서와 같이 즉시 수행되거나 나중에 비동기 콜백에서 발생할 수 있습니다.

(2) 2단계 정의 : Callback (javaworld)

Developers conversant in the event-driven programming model of MS-Windows and the X Window System are accustomed to passing function pointers that are invoked (that is, "called back") when something happens. Java's object-oriented model does not currently support method pointers, and thus seems to preclude using this comfortable mechanism. But all is not lost!

Java's support of interfaces provides a mechanism by which we can get the equivalent of callbacks. The trick is to define a simple interface that declares the method we wish to be invoked.

  • MS-Windows 및 X Window System의 이벤트 중심 프로그래밍 모델에 능숙한 개발자는 무언가가 발생했을 때 호출되는 함수 포인터를 전달하는 데 익숙합니다. Java의 객체 지향 모델은 "함수 포인터"를 지원하지 않으므로이 이러한 메커니즘을 사용하지 못하지만, 그러나 방법이 없는 것은 아니다.

(3) 3단계 정의 : Callback (StackoverFlow)

The concept of callbacks is to inform a class synchronous / asynchronous if some work in another class is done. Some call it the Hollywood principle: "Don't call us, we'll call you".
  • 콜백 개념은 다른 클래스의 일부 작업이 완료되면 클래스에 동기/비동기를 알리는 것입니다. 이런 Callback의 특성을 할리우드의 원칙이라고도 부른다. "우리한테 연락하지마세요, 우리가 연락할께요."

 

(4) 그래서 Callback?

  • 함수 포인터 : 위의 정의를 참고할 때, Callback은 포인터 개념이 없는 자바에서 Interface를 이용한 함수 포인터를 흉내내는 것.
  • 동기 시점 맞추기 : 비동기 프로그래밍의 특징 중 하나는 현재 코드 실행을 기다리지 않고 바로 다음 코드로 넘어간다는 점. 즉, 코드 A를 수행하고 결과를 받기까지 기다린 후 다음 코드 B가 실행되는 것이 의도이지만, 비동기는 그러든 말든 B를 수행해버린다.이러한 문제를 해결하기 위하여, 개발자 본인이 원하는 함수(코드)를 끼워넣는 작업이 Callback이다.
  • 화면 간 통신 : Fragment간 통신하는 방법들은 여러 가지다. 그 중 하나가 Callback을 이용한 방법이다.

 

 

2. 그래서?

(1) 왜 쓰냐

 

'함수 하나로 만들어서 쓰면 되지 왜 굳이, 어디에서 어디를 호출하고 실행하는 짓을 해야하냐?'

 

  • 개인적으로는 "함수 포인터" 개념이라는 이유가 가장 크다. 클래스별로 단일의 함수를 쓰지만, 다르게 정의해서 쓴다라는 인터페이스 특성이 더해지니 이유가 되었다.
  • 하나의 클래스에서 발생한 이벤트/입력변수를 다른 클래스에게 알려주고 처리하고 싶은데 쉬운 방법이 없네.

(2) 예를 들어, BackButton

MainActivity에서의 BackButton 동작과 SomeActivity의 BackButton 동작을 다르게 구현하고 싶을 때 같은 Callback 형식을 가진다.

- MainActivity

// 뒤로가기 버튼 입력시간이 담길 long 객체
    private long pressedTime = 0;
 
    // 리스너 생성
    public interface OnBackPressedListener {
        public void onBack();
    }
 
    // 리스너 객체 생성
    private OnBackPressedListener mBackListener;
 
    // 리스너 설정 메소드
    public void setOnBackPressedListener(OnBackPressedListener listener) {
        mBackListener = listener;
    }
 
    // 뒤로가기 버튼을 눌렀을 때의 오버라이드 메소드
    @Override
    public void onBackPressed() {
 
        // 다른 Fragment 에서 리스너를 설정했을 때 처리됩니다.
        if(mBackListener != null) {
            mBackListener.onBack();
            Log.e("!!!", "Listener is not null");
        // 리스너가 설정되지 않은 상태(예를들어 메인Fragment)라면
        // 뒤로가기 버튼을 연속적으로 두번 눌렀을 때 앱이 종료됩니다.
        } else {
            Log.e("!!!", "Listener is null");
            if ( pressedTime == 0 ) {
                Snackbar.make(findViewById(R.id.main_layout),
                     " 한 번 더 누르면 종료됩니다." , Snackbar.LENGTH_LONG).show();
                pressedTime = System.currentTimeMillis();
            }
            else {
                int seconds = (int) (System.currentTimeMillis() - pressedTime);
 
                if ( seconds > 2000 ) {
                    Snackbar.make(findViewById(R.id.main_layout), 
                        " 한 번 더 누르면 종료됩니다." , Snackbar.LENGTH_LONG).show();
                    pressedTime = 0 ;
                }
                else {
                    super.onBackPressed();
                    Log.e("!!!", "onBackPressed : finish, killProcess");
                    finish();
                    android.os.Process.killProcess(android.os.Process.myPid());
                }
            }
        }
    }

MainActivity 내부에 Interface OnBackPressedListener를 먼저 선언하고, 각 클래스로부터 Callback을 받을 OnBackPressedListener 객체를 멤버함수로 선언을 함. 그리고 onBackPressed 함수에 필요 기능을 정의함. 

 

- SomeActivity

public class SomeFragment extends Fragment 
			implements MainActivity.OnBackPressedListener{

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
                             Bundle savedInstanceState) {
        View view = inflater.inflate(R.layout.fragment_help, container, false);
        mainFragment = new MainFragment();
        return view;
    }
 
    @Override
    public void onBack() {
        Log.e("Some", "onBack()");
        
        // 리스너를 설정하기 위해 Activity 를 받아옵니다.
        MainActivity activity = (MainActivity)getActivity();
        
        // 한번 뒤로가기 버튼을 눌렀다면 Listener 를 null 로 해제해줍니다.
        activity.setOnBackPressedListener(null);
        
        // MainFragment 로 교체
        getActivity().getFragmentManager().beginTransaction()
                .replace(R.id.fragment_container, mainFragment).commit();
        // Activity 에서도 뭔가 처리하고 싶은 내용이 있다면 하단 문장처럼 호출해주면 됩니다.
        // activity.onBackPressed();
    }
 
    // Fragment 호출 시 반드시 호출되는 오버라이드 메소드입니다.
    @Override
    //혹시 Context 로 안되시는분은 Activity 로 바꿔보시기 바랍니다.
    public void onAttach(Context context) {
        super.onAttach(context);
        Log.e("Other", "onAttach()");
        ((MainActivity)context).setOnBackPressedListener(this);
    }
}

호출을 하는 외부 클래스에서 MainActivity의 Callback 함수를 Implement를  하게됨. 이렇게 되면, SomeFragment에서 back버튼을 처리를 해줄 수 있게 된다. 중요한 것은, 이 Callback 함수를 통하여 원하는 이벤트 처리를 하고 변수 처리도 같이 할 수 있다.

 

(3) 다른 예제

https://brunch.co.kr/@kimkm4726/1

 

소프트웨어 개발자를 위한 Callback의 정석.

콜백이라는 주제로 첫 게시글을 작성하기 이전에, 브런치의 깔끔하고 세련된 디자인과 UI에 감탄하며... (하준호 멘토님 감사합니당 ♥) 콜백이란것을 공부하면서. 구글링을 통해 찾은 예제를 수도 없이 보았다. 그러나 콜백이 무엇인지에 대해 뚜렷한 정의를 내리지 못한 채 아몰랑. 답답함만을 느끼던 중 결국, 콜백은 'A가 B를 호출하여 B가 작업을 수

brunch.co.kr

3. 코드 참조

https://jinunthing.tistory.com/22

 

Fragment 에서 뒤로가기 버튼 입력 처리

안녕하세요! Ji-nun입니다! 주제 : Fragment 에서 뒤로가기 버튼 입력 처리 원래 Fragment 상에서 뒤로가기 버튼을 눌렀을 때는 그 입력이 Fragment 를 품고있는 Activity 가 처리합니다. 하지만 Fragment 에서 뒤..

jinunthing.tistory.com