안드로이드 Native 코드 분석 : sp<> - Smart Pointer

 안드로이드 카메라 동작 원리에 대해 공부를 하면서 안드로이드 소스코드를 분석하고 있습니다. 옆에 C++ 서적을 읽어가며 보다보면 어느 정도 이해하고 넘어갈 수 있는 부분들이 많이 있었습니다만 책에서도 찾아보기 어려운 내용이 들이닥치더군요. 그 부분이 바로 sp<T> 구문이었습니다.


/framework/base/core/jni/android_hardware_Camera.cpp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
sp<Camera> get_native_camera(JNIEnv *env, jobject thiz, JNICameraContext** pContext)
{
    sp<Camera> camera;
    Mutex::Autolock _l(sLock);
    JNICameraContext* context = reinterpret_cast<JNICameraContext*>(env->GetIntField(thiz, fields.context));
    if (context != NULL) {
        camera = context->getCamera();
    }
    ALOGV("get_native_camera: context=%p, camera=%p", context, camera.get());
    if (camera == 0) {
        jniThrowRuntimeException(env, "Method called after release()");
    }
 
    if (pContext != NULL) *pContext = context;
    return camera;
}
cs


 코드 내부를 살펴보면 뜬금없이 등장하는 'sp<Camera>' 부분이 영 이해가 되지 않더군요. 이를 헤더를 뒤져가며 찾는 것도 너무나도 어려웠는데 이를 좀 더 자세히 살펴보았습니다.


/frameworks/base/core/jni/android_hardware_Camera.cpp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
#define LOG_TAG "Camera-JNI"
#include <utils/Log.h>
 
#include "jni.h"
#include "JNIHelp.h"
#include "android_runtime/AndroidRuntime.h"
#include <android_runtime/android_graphics_SurfaceTexture.h>
#include <android_runtime/android_view_Surface.h>
 
#include <cutils/properties.h>
#include <utils/Vector.h>
 
#include <gui/GLConsumer.h>
#include <gui/Surface.h>
#include <camera/Camera.h>
#include <binder/IMemory.h>
cs

위 헤더파일들 중 #include<binder/IMemory.h>로 이동해 봅니다.


/frameworks/native/include/binder/IMemory.h

1
2
3
4
5
6
7
8
9
10
#ifndef ANDROID_IMEMORY_H
#define ANDROID_IMEMORY_H
 
#include <stdint.h>
#include <sys/types.h>
#include <sys/mman.h>
 
#include <utils/RefBase.h>
#include <utils/Errors.h>
#include <binder/IInterface.h>
cs


 다음으로 안드로이드 JNI의 핵심을 이해할 수 있는 #include<utils/RefBase.h> 로 이동하신 후


/system/core/include/utils/RefBase.h

1
2
3
4
5
6
7
8
9
10
11
12
#ifndef ANDROID_REF_BASE_H
#define ANDROID_REF_BASE_H
 
#include <cutils/atomic.h>
 
#include <stdint.h>
#include <sys/types.h>
#include <stdlib.h>
#include <string.h>
 
#include <utils/StrongPointer.h>
#include <utils/TypeHelpers.h>
cs


끝으로 #include<utils/StongPointer.h> 로 이동해 주시면

/system/core/include/utils/StrongPointer.h

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
template<typename T>
class sp {
public:
    inline sp() : m_ptr(0) { }
 
    sp(T* other);
    sp(const sp<T>& other);
    template<typename U> sp(U* other);
    template<typename U> sp(const sp<U>& other);
 
    ~sp();
 
    // Assignment
 
    sp& operator = (T* other);
    sp& operator = (const sp<T>& other);
 
    template<typename U> sp& operator = (const sp<U>& other);
    template<typename U> sp& operator = (U* other);
 
    //! Special optimization for use by ProcessState (and nobody else).
    void force_set(T* other);
 
    // Reset
 
    void clear();
 
    // Accessors
 
    inline  T&      operator* () const  { return *m_ptr; }
    inline  T*      operator-> () const { return m_ptr;  }
    inline  T*      get() const         { return m_ptr; }
 
    // Operators
 
    COMPARE(==)
    COMPARE(!=)
    COMPARE(>)
    COMPARE(<)
    COMPARE(<=)
    COMPARE(>=)
 
private:    
    template<typename Y> friend class sp;
    template<typename Y> friend class wp;
    void set_pointer(T* ptr);
    T* m_ptr;
};
cs


드디어 우리들이 찾고 있던 sp<T>가 구현된 StrongPointer를 찾아내실 수 있습니다. 과연 이 녀석은 무슨 역할을 하는 것일까요?


 안드로이드 소스코드에 사용되고 있는 sp<T>는 스마트포인터(Smart Pointer)를 말하는 것이며 보통 Struct나 int같이 C에서 기본적으로 제공되고 있는 것들은 *을 통해 간단하게 포인터로 사용하는 것이 가능합니다. sp<T>는 이를 객체인 Class를 포인터처럼 사용할 수 있도록 하기 위해 사용되는 도구라고 설명드릴 수 있겠습니다. 이를 사용하면 사용자는 포인터로 지정된 객체를 실제 객체를 다루듯이 사용하실 수 있다는 장점을 가지고 있습니다.


 Strong Pointer를 쓰는 가장 중요한 이유는 포인터 객체의 할당과 삭제를 직접 제어할 수 있다는 것입니다. Java와 같이 Gabage Collector 기능이 있는 경우 객체를 할당 해준 후 사용하지 않으면 알아서 할당에서 사라지지만 C/C++의 경우 해당 기능이 존재하지 않습니다. 그렇기 때문에 sp<T>를 활용해 메모리를 관리해 준다면 좀 더 쾌적한 프로그램을 만들 수 있을 것입니다.


 위 코드에서 47번째 줄의 내용을 주목해 봅시다. m_ptr이라는 이름의 변수가 있는데 이 변수의 타입은 T, 즉 sp에 전달되는 클래스 자신의 주소값을 저장하는 상황임을 알 수 있습니다.


 그렇다면 여기서 잠시 코드의 내부롤 살짝 살펴보도록 하겠습니다.


1
2
3
4
5
6
template<typename T>
sp<T>::sp(T* other)
        : m_ptr(other) {
    if (other)
        other->incStrong(this);
}
cs

객체의 스마트포인터가 할당된 경우 입니다. 기존에 해당 객체가 존재할 경우 incStrong()를 통해 참조 갯수를 늘립니다.


1
2
3
4
5
template<typename T>
sp<T>::~sp() {
    if (m_ptr)
        m_ptr->decStrong(this);
}
cs

객체의 스마트포인터 하나가 할당이 해제된 경우입니다. decString()를 통해 참조 갯수를 줄입니다.



1
2
3
4
5
6
7
template<typename T>
void sp<T>::clear() {
    if (m_ptr) {
        m_ptr->decStrong(this);
        m_ptr = 0;
    }
}
cs

객체 자체의 할당을 해제합니다. 해당 포인터를 0으로 하여 할당을 해제합니다.


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
template <typename T>
class sp
{
public:
    inline sp() : m_ptr(0) { }
....
    // Accessors
 
    inline  T&      operator* () const  { return *m_ptr; }
    inline  T*      operator-> () const { return m_ptr;  }
    inline  T*      get() const         { return m_ptr; }
....
private:
    template<typename Y> friend class sp;
    template<typename Y> friend class wp;
    void set_pointer(T* ptr);
    T* m_ptr;
};
 
 
cs

 앞에서도 제시되었었던 Smart Pointer인 sp 클래스 내부의 모습입니다. Accessors 주석 아래에 작성된 부분은 Smart Pointer에 연결된 포인터에 접근에 관하여 다루고 있는 모습입니다.


 안드로이드 소스코드를 이해하는 데에는 이 정도의 지식만 알고 넘어가시면 될 듯 합니다. 좀 더 자세한 내용을 알고 싶으신 분들은 위에서 언급한 코드를 직접 살펴보시면 좋을 것입니다.