검색결과 리스트
글
Handler와 Message를 활용하여 콜백함수 구현하기
안드로이드 프레임워크를 분석하던 도중 흥미로운 부분을 발견하게 되어 이를 소개하고자 합니다. 물론 이는 안드로이드의 Application 단계에서도 쉽게 구현될 수 있는 기능이기에 안드로이드 애프리케이션 제작에 어느 정도 경험이 있으신 분들이라면 쉽게 이해하실 수 있으시리라 생각합니다.
이번에 다루고자 하는 핵심적인 개념은 바로 Callback 입니다. 그럼 여기서 Callback 이란 무엇인지 간단하게 설명하도록 하겠습니다.
Callback이란?
일반적으로 우리들이 프로그래밍을 설계할 때 Method와 같은 함수를 구현합니다. 특히 API와 같이 원하는 기능이 미리 구현되어 있어 해당 함수를 호출하는 것으로 원하는 기능을 실행하기도 하지요. Callback 또한 일반적인 함수들과 비슷하게 구성되어 있습니다. Android의 경우 프로그래머가 구현하고자 하는 기능을 Listener Interface를 통해 Callback 기능을 등록해줍니다.
Callback 함수가 일반 함수와 가장 큰 차이점으로 호출되는 시점에 있습니다. 일반적인 함수의 경우 프로세스가 해당 함수를 호출하면 호출되는 즉시 해당 기능을 수행하게 됩니다. 반면 Callback 함수의 경우 프로세서가 호출을 요청할 경우 일반 함수처럼 즉시 호출될 수도 있지만 프로세스의 동작과는 독립적으로 동작하는 것이 가능하여 해당 프로세스가 수행을 종료한 후에 Callback 함수를 실행시킬 수 있습니다.
안드로이드 Framework에서 Runnable Interface를 통해 Callback 기능을 구현한 방법이 있어 해당 기능을 분석해 보았습니다. 특이하게도 안드로이드에서 지원하는 Looper와 Message를 활용해서 구현하였다는 점인데요 쉽게 설명을 드리자면 실행하고자 하는 함수를 Runnable Interface를 통해 구현한 후 이를 Mssage를 통해 해당 함수를 예약해 두었다가 이후 프로세스가 작업을 종료하게 되었을 때 해당 함수를 호출하는 방식입니다.
자세한 구현 내용을 실제 소스코드를 통해 확인해보도록 합시다.
/framework/base/core/java/android/view/Choreographer.java
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 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 | public final class Choreographer { .... // Choreographer를 초기와 합니다. 인자로 Choreographer의 Looper를 넘겨줍니다. // Thread local storage for the choreographer. private static final ThreadLocal<Choreographer> sThreadInstance = new ThreadLocal<Choreographer>() { @Override protected Choreographer initialValue() { Looper looper = Looper.myLooper(); if (looper == null) { throw new IllegalStateException("The current thread must have a looper!"); } return new Choreographer(looper); } }; .... private final Looper mLooper; private final FrameHandler mHandler; // The display event receiver can only be accessed by the looper thread to which // it is attached. We take care to ensure that we post message to the looper // if appropriate when interacting with the display event receiver. private final FrameDisplayEventReceiver mDisplayEventReceiver; .... private Choreographer(Looper looper) { //Constructor를 통해 Looper를 받는다. mLooper = looper; //Constructor를 통하여 얻게 된 Looper를 FrameHadler에 등록한다. mHandler = new FrameHandler(looper); mDisplayEventReceiver = USE_VSYNC ? new FrameDisplayEventReceiver(looper) : null; .... } } .... } //Callback 기능을 수행할 Hndler입니다. //Callback 기능 구현시 handlerMessage(Message msg) 함수는 실행되지 않습니다. private final class FrameHandler extends Handler { public FrameHandler(Looper looper) { super(looper); } @Override public void handleMessage(Message msg) { switch (msg.what) { case MSG_DO_FRAME: doFrame(System.nanoTime(), 0); break; case MSG_DO_SCHEDULE_VSYNC: doScheduleVsync(); break; case MSG_DO_SCHEDULE_CALLBACK: doScheduleCallback(msg.arg1); break; } } } //Callback 기능을 구현하고자 하는 부분입니다. //Runnable Interface로 구현하고자 하는 Callback 함수를 run() 함수로 구성합니다. private final class FrameDisplayEventReceiver extends DisplayEventReceiver implements Runnable { private boolean mHavePendingVsync; private long mTimestampNanos; private int mFrame; public FrameDisplayEventReceiver(Looper looper) { super(looper); } @Override public void onVsync(long timestampNanos, int builtInDisplayId, int frame) { .... mTimestampNanos = timestampNanos; mFrame = frame; //Message를 통해 Callback 기능 구현 //Callback 함수를 Message에 등록합니다. //mHandler : Message를 받을 Looper를 갖고 있는 Handler //this : 등록하고자 하는 함수. 해당 기능은 Runnable Interface를 통하여 run() 함수로 구성됨 Message msg = Message.obtain(mHandler, this); msg.setAsynchronous(true); //Message를 Handler에 전송합니다. Handler는 Message를 받은 후 현재 프로세스가 작업을 // 종료하면 이후 해당 Callback 기능을 실행합니다. mHandler.sendMessageAtTime(msg, timestampNanos / NANOS_PER_MS); } //실행하고자 하는 Callback 함수 @Override public void run() { mHavePendingVsync = false; doFrame(mTimestampNanos, mFrame); } } | cs |
위에서 구현된 함수를 이미지로 나타내면 다음과 같습니다.
'안드로이드 > 애플리케이션 제작' 카테고리의 다른 글
저장된 사진및 파일이 보이지 않을 때 미디어스캐닝(Media Scanning) 방법 [Kitkat 이후의 버전에서 적용방법] (0) | 2015.10.04 |
---|---|
byte[] 바이트 배열을 socket을 통해 쉽게 전송하는 방법 (0) | 2015.10.03 |
안드로이드 기기 간에 Wifi Socket 통신하기 (14) | 2015.02.08 |
USB를 연결한 후 Logcat이 바로 보이지 않을 때 해결방법 (0) | 2015.02.07 |
xml graphical layout가 정상적으로 동작하지 않을 때 (0) | 2015.02.05 |