검색결과 리스트
글
SurfaceView에서 SurfaceHolder의 동작원리(Principle of SurfaceHolder in SurfaceView)
짫은 시간에 화면에 다양한 변화를 나타내는 데에 사용되는 SurfaceView에서 사용되는 SurfaceHolder이 어떻게 동작하는 지에 대해 궁금해서 안드로이드 소스를 이리저리 살펴보게 되었습니다. 본 포스팅에서는 실행 방법을 제 나름대로 탐색한 결과에 대해 작성하였습니다.
시작하기에 앞서 SurfaceView에서 가장 많이 사용되는 Camera 애플리케이션에 대해 알아봅시다. 제가 이전에 작성한 카메라 작동에 관한 포스팅은 아래 링크를 참조해 주시길 바랍니다.
SurfaceView를 사용하기 위해서는 SurfaceHolder를 사용해야 합니다. SurfaceHolder에 사용하고자 하는 기능을 넣은 후 이를 등록하면 SurfaceView에서는 이를 토대로 동작을 수행하게 됩니다.
1 2 3 4 5 6 7 8 9 10 | protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); requestWindowFeature(Window.FEATURE_NO_TITLE); setContentView(R.layout.activity_main); surfaceView = (SurfaceView)findViewById(R.id.surfaceView); surfaceHolder = surfaceView.getHolder(); surfaceHolder.addCallback(surfaceListener); } | cs |
위 코드에서 확인하는 바와 같이 SurfaceView를 XML로 생성한 SurfaceView와 연결한 후
해당 SurfaceView에 SurfaceHolder를 연결한 후 해당 Holder에 surfaceListener를 추가하는 과정입니다.
여기서 surfaceListener는 다음과 같이 선언됩니다.
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 | private SurfaceHolder.Callback surfaceListener = new SurfaceHolder.Callback() { @Override public void surfaceDestroyed(SurfaceHolder holder) { // TODO Auto-generated method stub camera.release(); } @Override public void surfaceCreated(SurfaceHolder holder) { // TODO Auto-generated method stub camera = Camera.open(); try { camera.setPreviewDisplay(holder); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } } @Override public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) { // TODO Auto-generated method stub Camera.Parameters parameters = camera.getParameters(); parameters.setPreviewSize(width, height); camera.startPreview(); } }; | cs |
위에서 보시는 것과 같이 SurfaceHolder 내에 있는 Callback 인터페이스 내의 3개의 Method를 선언한 것을 보실 수 있습니다. 이 내용이 SurfaceView 내의 Holder에 적용되는 것이지요.
이번에는 SurfaceView에서 getHolder()가 동작하는 방식에 대해 보도록 하겠습니다.
/frameworks/base/core/java/android/view/SurfaceView.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 | public class SurfaceView extends View { .... private final SurfaceHolder mSurfaceHolder = new SurfaceHolder() { private static final String LOG_TAG = "SurfaceHolder"; @Override public boolean isCreating() { return mIsCreating; } @Override public void addCallback(Callback callback) { synchronized (mCallbacks) { // This is a linear search, but in practice we'll // have only a couple callbacks, so it doesn't matter. if (mCallbacks.contains(callback) == false) { mCallbacks.add(callback); } } } .... } .... /** * Return the SurfaceHolder providing access and control over this * SurfaceView's underlying surface. * * @return SurfaceHolder The holder of the surface. */ public SurfaceHolder getHolder() { return mSurfaceHolder; } } | cs |
SurfaceView 클래스를 선언한 후 getHolder() 함수를 호출하게 되면 위의 소스에서 보는 바와 같이 SurfaceView 클래스 내에 있는 mSurfaceHolder 객체를 return 해주는 구조임을 알 수 있습니다.
mSurfaceHolder는 SurfaceView 클래스 내부에 Interface인 SurafceHolder.Callback을 정의하여 선언하였음을 확인하실 수 있습니다. 해당 Holder 내에 addCallbacks()가 정의되어있어 Application 단계에서 설정하였던 surfaceListener를 등록하고 있는 모습을 보실 수 있습니다.
그렇다면 SurfaceView 클래스 내부에 있는 mCallbacks 객체에 등록된 surfaceListener는 어떤 방식으로 동작하는지 살펴보도록 하겠습니다. mCallbacks 또한 SurfaceView.java 내부에서 동작되는 모습을 확인하실 수 있습니다.
아래 코드의 내용들이 상당히 많습니다만 한 줄 한 줄이 상당히 중요한 관계로 관련 코드를 모두 올려봅니다. 물론 핵심 코드는 볼드체 및 밑줄로 눈에 띄도록 표기하였습니다.
/frameworks/base/core/java/android/view/SurfaceView.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 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 | public class SurfaceView extends View { static private final String TAG = "SurfaceView"; static private final boolean DEBUG = false; final ArrayList<SurfaceHolder.Callback> mCallbacks = new ArrayList<SurfaceHolder.Callback>(); .... private void updateWindow(boolean force, boolean redrawNeeded) { if (!mHaveFrame) { return; } ViewRootImpl viewRoot = getViewRootImpl(); if (viewRoot != null) { mTranslator = viewRoot.mTranslator; } if (mTranslator != null) { mSurface.setCompatibilityTranslator(mTranslator); } int myWidth = mRequestedWidth; if (myWidth <= 0) myWidth = getWidth(); int myHeight = mRequestedHeight; if (myHeight <= 0) myHeight = getHeight(); getLocationInWindow(mLocation); final boolean creating = mWindow == null; final boolean formatChanged = mFormat != mRequestedFormat; final boolean sizeChanged = mWidth != myWidth || mHeight != myHeight; final boolean visibleChanged = mVisible != mRequestedVisible; if (force || creating || formatChanged || sizeChanged || visibleChanged || mLeft != mLocation[0] || mTop != mLocation[1] || mUpdateWindowNeeded || mReportDrawNeeded || redrawNeeded) { if (DEBUG) Log.i(TAG, "Changes: creating=" + creating + " format=" + formatChanged + " size=" + sizeChanged + " visible=" + visibleChanged + " left=" + (mLeft != mLocation[0]) + " top=" + (mTop != mLocation[1])); .... try { final boolean visible = mVisible = mRequestedVisible; mLeft = mLocation[0]; mTop = mLocation[1]; mWidth = myWidth; mHeight = myHeight; mFormat = mRequestedFormat; .... mSurfaceLock.lock(); try { redrawNeeded |= creating | reportDrawNeeded; SurfaceHolder.Callback callbacks[] = null; final boolean surfaceChanged = (relayoutResult & WindowManagerGlobal.RELAYOUT_RES_SURFACE_CHANGED) != 0; if (mSurfaceCreated && (surfaceChanged || (!visible && visibleChanged))) { mSurfaceCreated = false; if (mSurface.isValid()) { if (DEBUG) Log.i(TAG, "visibleChanged -- surfaceDestroyed"); callbacks = getSurfaceCallbacks(); for (SurfaceHolder.Callback c : callbacks) { c.surfaceDestroyed(mSurfaceHolder); } } } mSurface.transferFrom(mNewSurface); if (visible && mSurface.isValid()) { if (!mSurfaceCreated && (surfaceChanged || visibleChanged)) { mSurfaceCreated = true; mIsCreating = true; if (DEBUG) Log.i(TAG, "visibleChanged -- surfaceCreated"); if (callbacks == null) { callbacks = getSurfaceCallbacks(); } for (SurfaceHolder.Callback c : callbacks) { c.surfaceCreated(mSurfaceHolder); } } if (creating || formatChanged || sizeChanged || visibleChanged || realSizeChanged) { if (DEBUG) Log.i(TAG, "surfaceChanged -- format=" + mFormat + " w=" + myWidth + " h=" + myHeight); if (callbacks == null) { callbacks = getSurfaceCallbacks(); } for (SurfaceHolder.Callback c : callbacks) { c.surfaceChanged(mSurfaceHolder, mFormat, myWidth, myHeight); } } if (redrawNeeded) { if (DEBUG) Log.i(TAG, "surfaceRedrawNeeded"); if (callbacks == null) { callbacks = getSurfaceCallbacks(); } for (SurfaceHolder.Callback c : callbacks) { if (c instanceof SurfaceHolder.Callback2) { ((SurfaceHolder.Callback2)c).surfaceRedrawNeeded( mSurfaceHolder); } } } } } finally { mIsCreating = false; if (redrawNeeded) { if (DEBUG) Log.i(TAG, "finishedDrawing"); mSession.finishDrawing(mWindow); } mSession.performDeferredDestroy(mWindow); } } catch (RemoteException ex) { } if (DEBUG) Log.v( TAG, "Layout: x=" + mLayout.x + " y=" + mLayout.y + " w=" + mLayout.width + " h=" + mLayout.height + ", frame=" + mSurfaceFrame); } .... private SurfaceHolder.Callback[] getSurfaceCallbacks() { SurfaceHolder.Callback callbacks[]; synchronized (mCallbacks) { callbacks = new SurfaceHolder.Callback[mCallbacks.size()]; mCallbacks.toArray(callbacks); } return callbacks; } .... } | cs |
위 코드를 처음 보는 사람이라면 코드의 양이 다소 방대하여 이해하기 힘드실 수 있습니다. 일단 위에서 강조한 부분에 대해 좀 더 자세히 설명드리도록 하겠습니다.
final ArrayList<SurfaceHolder.Callback> mCallbacks = new ArrayList<SurfaceHolder.Callback>();
mCallBacks는 Java에서 제공하는 ArrayList로 구성된 Object입니다. 이 ArrayList 안에 Application에서 호출했던 addCallbacks()를 통해 Listener가 등록됩니다.
private void updateWindow(boolean force, boolean redrawNeeded)
본 함수를 통해 SurfaceView의 화면이 변경될 수 있도록 할 수 있습니다. 해당 함수를 잘 보시면 SurfaceHolder.Callback 인터페이스를 통해 선언한 함수들이 실행되는 것을 보실 수 있습니다.
'안드로이드 > 프레임워크' 카테고리의 다른 글
안드로이드 프레임워크 프로그래밍(11) [JAVA 프레임워크와 Native 프레임워크 연결 원리] (2) | 2015.03.18 |
---|---|
안드로이드 Native 코드 분석 : sp<> - Smart Pointer (0) | 2015.03.02 |
안드로이드 프레임워크 프로그래밍(10) [Native C/C++ 코드에서 Java 호출] (0) | 2015.02.18 |
안드로이드 프레임워크 프로그래밍(9) [프레임워크에 JNI를 활용해 C/C++ 코드 추가하기] (0) | 2015.02.17 |
안드로이드 프레임워크 프로그래밍(8) [JNI에서 작성된 C++ 코드에서 C 코드 함수 호출하기] (0) | 2015.02.16 |