샘플: native-activity
컬렉션을 사용해 정리하기
내 환경설정을 기준으로 콘텐츠를 저장하고 분류하세요.
native-activity 샘플은 NDK 샘플 루트 아래의 native-activity
폴더에 있습니다. 자바 소스 코드가 포함되지 않은 순수 네이티브 애플리케이션의 아주 간단한 예입니다. 자바 소스가 없어도 자바 컴파일러는 가상 머신이 실행할 실행 가능 스터브를 만듭니다.
이 스텁은 실제 네이티브 프로그램의 래퍼 역할을 하며 .so
파일에 있습니다.
앱 자체는 단순히 전체 화면에 하나의 색상을 렌더링하고 감지된 움직임에 반응하여 부분적으로 색상을 변경합니다.
AndroidManifest.xml
네이티브 코드만 있는 앱은 NativeActivity
프레임워크 클래스를 도입한 Android API 수준 9 이전 버전을 지정해서는 안 됩니다.
<uses-sdk android:minSdkVersion="9" />
이 앱에는 자바 없이 네이티브 코드만 있으므로 다음 행은 android:hasCode
를 false
로 선언합니다.
<application android:label="@string/app_name"
android:hasCode="false">
다음 줄은 NativeActivity
클래스를 선언합니다.
<activity android:name="android.app.NativeActivity"
마지막으로 매니페스트는 빌드될 공유 라이브러리의 이름으로 android:value
를 지정하되 맨 처음의 lib
와 .so
확장자는 뺍니다. 이 값은 Android.mk
에 있는 LOCAL_MODULE
의 이름과 동일해야 합니다.
<meta-data android:name="android.app.lib_name"
android:value="native-activity" />
Android.mk
이 파일은 생성할 공유 라이브러리 이름으로 시작합니다.
LOCAL_MODULE := native-activity
다음에는 네이티브 소스 코드 파일 이름을 선언합니다.
LOCAL_SRC_FILES := main.c
그다음에는 바이너리 빌드에 사용할 빌드 시스템의 외부 라이브러리 목록을 표시합니다. -l
(link-against) 옵션은 각 라이브러리 이름 앞에 옵니다.
log
는 로깅 라이브러리입니다.
android
에는 NDK용 표준 Android 지원 API가 포함됩니다. Android 및 NDK에서 지원하는 API에 관한 자세한 내용은 Android NDK 네이티브 API를 참조하세요.
EGL
은 그래픽 API의 플랫폼별 부분에 해당합니다.
GLESv1_CM
은 Android용 OpenGL 버전인 OpenGL ES에 해당합니다. 이 라이브러리는 EGL에 따라 달라집니다.
각 라이브러리의 경우:
- 실제 파일 이름은
lib
로 시작하고 .so
확장자로 끝납니다. 예를 들어 log
라이브러리의 실제 파일 이름은 liblog.so
입니다.
- 라이브러리는 NDK 루트인
<ndk>/platforms/android-<sdk_version>/arch-<abi>/usr/lib/
디렉터리에 있습니다.
LOCAL_LDLIBS := -llog -landroid -lEGL -lGLESv1_CM
다음 행은 정적 라이브러리의 이름인 android_native_app_glue
를 제공합니다. 이 이름은 애플리케이션이 NativeActivity
수명 주기 이벤트와 터치 입력을 관리하는 데 사용됩니다.
LOCAL_STATIC_LIBRARIES := android_native_app_glue
마지막 행은 빌드 시스템에 이 정적 라이브러리를 빌드하라고 지시합니다.
ndk-build
스크립트는 빌드된 라이브러리(libandroid_native_app_glue.a
)를 빌드 프로세스 중 생성된 obj
디렉터리에 배치합니다. android_native_app_glue
라이브러리에 관한 자세한 내용은 android_native_app_glue.h
헤더 및 상응하는 .c
소스 파일을 참조하세요.
$(call import-module,android/native_app_glue)
Android.mk
파일에 관한 자세한 내용은 Android.mk를 참조하세요.
main.c
기본적으로 이 파일에는 프로그램 전체가 포함됩니다.
다음 include는 Android.mk
에 열거된 공유 라이브러리 및 정적 라이브러리에 모두 상응합니다.
#include <EGL/egl.h>
#include <GLES/gl.h>
#include <android/sensor.h>
#include <android/log.h>
#include <android_native_app_glue>
android_native_app_glue
라이브러리는 다음 함수를 호출하여 사전 정의된 상태 구조를 전달합니다. NativeActivity
콜백 처리를 간소화하는 래퍼의 역할도 합니다.
void android_main(struct android_app* state) {
다음으로 이 프로그램은 글루 라이브러리에서 대기하는 이벤트를 처리합니다. 이 이벤트 핸들러는 상태 구조 뒤에 나옵니다.
struct engine engine;
// Suppress link-time optimization that removes unreferenced code
// to make sure glue isn't stripped.
app_dummy();
memset(&engine, 0, sizeof(engine));
state->userData = &engine;
state->onAppCmd = engine_handle_cmd;
state->onInputEvent = engine_handle_input;
engine.app = state;
애플리케이션은 sensor.h
의 API를 사용하여 센서 모니터링을 시작할 준비를 합니다.
engine.sensorManager = ASensorManager_getInstance();
engine.accelerometerSensor =
ASensorManager_getDefaultSensor(engine.sensorManager,
ASENSOR_TYPE_ACCELEROMETER);
engine.sensorEventQueue =
ASensorManager_createEventQueue(engine.sensorManager,
state->looper, LOOPER_ID_USER, NULL, NULL);
이어서 루프가 시작됩니다. 여기서 애플리케이션이 시스템에 메시지(센서 이벤트)를 폴링합니다. android_native_app_glue
에 메시지를 전송하면 android_main
에 정의된 onAppCmd
이벤트와 일치하는지 확인합니다. 일치하면 실행할 핸들러로 메시지가 전송됩니다.
while (1) {
// Read all pending events.
int ident;
int events;
struct android_poll_source* source;
// If not animating, we will block forever waiting for events.
// If animating, we loop until all events are read, then continue
// to draw the next frame of animation.
while ((ident=ALooper_pollAll(engine.animating ? 0 : -1, NULL,
&events,
(void**)&source)) >= 0) {
// Process this event.
if (source != NULL) {
source->process(state, source);
}
// If a sensor has data, process it now.
if (ident == LOOPER_ID_USER) {
if (engine.accelerometerSensor != NULL) {
ASensorEvent event;
while (ASensorEventQueue_getEvents(engine.sensorEventQueue,
&event, 1) > 0) {
LOGI("accelerometer: x=%f y=%f z=%f",
event.acceleration.x, event.acceleration.y,
event.acceleration.z);
}
}
}
// Check if we are exiting.
if (state->destroyRequested != 0) {
engine_term_display(&engine);
return;
}
}
큐가 비워지고 프로그램이 폴링 루프를 종료하면 프로그램은 OpenGL을 호출하여 화면을 그리도록 합니다.
if (engine.animating) {
// Done with events; draw next animation frame.
engine.state.angle += .01f;
if (engine.state.angle > 1) {
engine.state.angle = 0;
}
// Drawing is throttled to the screen update rate, so there
// is no need to do timing here.
engine_draw_frame(&engine);
}
}
이 페이지에 나와 있는 콘텐츠와 코드 샘플에는 콘텐츠 라이선스에서 설명하는 라이선스가 적용됩니다. 자바 및 OpenJDK는 Oracle 및 Oracle 계열사의 상표 또는 등록 상표입니다.
최종 업데이트: 2025-07-26(UTC)
[[["이해하기 쉬움","easyToUnderstand","thumb-up"],["문제가 해결됨","solvedMyProblem","thumb-up"],["기타","otherUp","thumb-up"]],[["필요한 정보가 없음","missingTheInformationINeed","thumb-down"],["너무 복잡함/단계 수가 너무 많음","tooComplicatedTooManySteps","thumb-down"],["오래됨","outOfDate","thumb-down"],["번역 문제","translationIssue","thumb-down"],["샘플/코드 문제","samplesCodeIssue","thumb-down"],["기타","otherDown","thumb-down"]],["최종 업데이트: 2025-07-26(UTC)"],[],[],null,["# Sample: native-activity\n\nThe native-activity sample resides under the\n[NDK samples root](https://github.com/android/ndk-samples/), in folder\n`native-activity`. It is a very simple example of a purely native\napplication, with no Java source code. In the absence of any Java source, the\nJava compiler still creates an executable stub for the virtual machine to run.\nThe stub serves as a wrapper for the actual, native program, which is located in the `.so`\nfile.\n\nThe app itself simply renders a color onto the entire screen, and\nthen changes the color partly in response to movement that it detects.\n\nAndroidManifest.xml\n-------------------\n\nAn app with only native code must not specify an Android API level lower than 9, which introduced\nthe [`NativeActivity`](/ndk/guides/concepts#naa) framework class. \n\n```xml\n\u003cuses-sdk android:minSdkVersion=\"9\" /\u003e\n```\n\nThe following line declares `android:hasCode` as `false`, as this app has only\nnative code--no Java. \n\n```xml\n\u003capplication android:label=\"@string/app_name\"\nandroid:hasCode=\"false\"\u003e\n```\n\nThe next line declares the `NativeActivity` class. \n\n```xml\n\u003cactivity android:name=\"android.app.NativeActivity\"\n```\n\nFinally, the manifest specifies `android:value` as the name of the shared library to be\nbuilt, minus the initial `lib` and the `.so` extension. This value must be the same as\nthe name of `LOCAL_MODULE` in `Android.mk`. \n\n```xml\n\u003cmeta-data android:name=\"android.app.lib_name\"\n android:value=\"native-activity\" /\u003e\n```\n\nAndroid.mk\n----------\n\nThis file begins by providing the name of the shared library to generate. \n\n```\nLOCAL_MODULE := native-activity\n```\n\nNext, it declares the name of the native source-code file. \n\n```\nLOCAL_SRC_FILES := main.c\n```\n\nNext, it lists the external libraries for the build system to use in building the binary. The\n`-l` (link-against) option precedes each library name.\n\n- `log` is a logging library.\n- `android` encompasses the standard Android support APIs for NDK. For more information about the APIs that Android and the NDK support, see [Android NDK Native\n APIs](/ndk/guides/stable_apis).\n- `EGL` corresponds to the platform-specific portion of the graphics API.\n- `GLESv1_CM` corresponds to OpenGL ES, the version of OpenGL for Android. This library depends on EGL.\n\nFor each library:\n\n- The actual file name starts with `lib`, and ends with the `.so` extension. For example, the actual file name for the `log` library is `liblog.so`.\n- The library resides in the following directory, NDK root: `\u003cndk\u003e/platforms/android-\u003csdk_version\u003e/arch-\u003cabi\u003e/usr/lib/`.\n\n```\nLOCAL_LDLIBS := -llog -landroid -lEGL -lGLESv1_CM\n```\n\nThe next line provides the name of the static library, `android_native_app_glue`, which the\napplication uses to manage `NativeActivity` lifecycle events and touch input. \n\n```\nLOCAL_STATIC_LIBRARIES := android_native_app_glue\n```\n\nThe final line tells the build system to build this static library.\nThe `ndk-build` script places the built library\n(`libandroid_native_app_glue.a`) into the `obj` directory\ngenerated during the build process. For more information about the `android_native_app_glue`\nlibrary, see its `android_native_app_glue.h` header and corresponding `.c`source file. \n\n```\n$(call import-module,android/native_app_glue)\n```\n\nFor more information about the `Android.mk` file, see\n[Android.mk](/ndk/guides/android_mk).\n\nmain.c\n------\n\nThis file essentially contains the entire progam.\n\nThe following includes correspond to the libraries, both shared and static,\nenumerated in `Android.mk`. \n\n```c++\n#include \u003cEGL/egl.h\u003e\n#include \u003cGLES/gl.h\u003e\n\n\n#include \u003candroid/sensor.h\u003e\n#include \u003candroid/log.h\u003e\n#include \u003candroid_native_app_glue\u003e\n```\n\nThe `android_native_app_glue` library calls the following function,\npassing it a predefined state structure. It also serves as a wrapper that\nsimplifies handling of `NativeActivity` callbacks. \n\n```c++\nvoid android_main(struct android_app* state) {\n```\n\nNext, the program handles events queued by the glue library. The event\nhandler follows the state structure. \n\n```c++\nstruct engine engine;\n\n\n\n// Suppress link-time optimization that removes unreferenced code\n// to make sure glue isn't stripped.\napp_dummy();\n\n\nmemset(&engine, 0, sizeof(engine));\nstate-\u003euserData = &engine;\nstate-\u003eonAppCmd = engine_handle_cmd;\nstate-\u003eonInputEvent = engine_handle_input;\nengine.app = state;\n```\n\nThe application prepares to start monitoring the sensors, using the\nAPIs in `sensor.h`. \n\n```c++\n engine.sensorManager = ASensorManager_getInstance();\n engine.accelerometerSensor =\n ASensorManager_getDefaultSensor(engine.sensorManager,\n ASENSOR_TYPE_ACCELEROMETER);\n engine.sensorEventQueue =\n ASensorManager_createEventQueue(engine.sensorManager,\n state-\u003elooper, LOOPER_ID_USER, NULL, NULL);\n```\n\nNext, a loop begins, in which the application polls the system for\nmessages (sensor events). It sends messages to\n`android_native_app_glue`, which checks to see whether they match\nany `onAppCmd` events defined in `android_main`. When a\nmatch occurs, the message is sent to the handler for execution. \n\n```c++\nwhile (1) {\n // Read all pending events.\n int ident;\n int events;\n struct android_poll_source* source;\n\n\n // If not animating, we will block forever waiting for events.\n // If animating, we loop until all events are read, then continue\n // to draw the next frame of animation.\n while ((ident=ALooper_pollAll(engine.animating ? 0 : -1, NULL,\n &events,\n (void**)&source)) \u003e= 0) {\n\n\n // Process this event.\n if (source != NULL) {\n source-\u003eprocess(state, source);\n }\n\n\n // If a sensor has data, process it now.\n if (ident == LOOPER_ID_USER) {\n if (engine.accelerometerSensor != NULL) {\n ASensorEvent event;\n while (ASensorEventQueue_getEvents(engine.sensorEventQueue,\n &event, 1) \u003e 0) {\n LOGI(\"accelerometer: x=%f y=%f z=%f\",\n event.acceleration.x, event.acceleration.y,\n event.acceleration.z);\n }\n }\n }\n\n\n // Check if we are exiting.\n if (state-\u003edestroyRequested != 0) {\n engine_term_display(&engine);\n return;\n }\n }\n```\n\nOnce the queue is empty, and the program exits the polling loop, the\nprogram calls OpenGL to draw the screen. \n\n```c++\n if (engine.animating) {\n // Done with events; draw next animation frame.\n engine.state.angle += .01f;\n if (engine.state.angle \u003e 1) {\n engine.state.angle = 0;\n }\n\n\n // Drawing is throttled to the screen update rate, so there\n // is no need to do timing here.\n engine_draw_frame(&engine);\n }\n}\n```"]]