| page.title=Sample: native-activity | |
| @jd:body | |
| <div id="qv-wrapper"> | |
| <div id="qv"> | |
| <h2>On this page</h2> | |
| <ol> | |
| <li><a href="#am">AndroidManifest.xml</a></li> | |
| <li><a href="#anm">Android.mk</a></li> | |
| <li><a href="#apm">Application.mk</a></li> | |
| <li><a href="#mac">main.c</a></li> | |
| </ol> | |
| </li> | |
| </ol> | |
| </div> | |
| </div> | |
| <p>The native-activity sample resides under the NDK installation root, in | |
| {@code samples/native-activity}. It is a very simple example of a purely native | |
| application, with no Java source code. In the absence of any Java source, the | |
| Java compiler still creates an executable stub for the virtual machine to run. | |
| The stub serves as a wrapper for the actual, native program, which is located in the {@code .so} | |
| file.</p> | |
| <p>The app itself simply renders a color onto the entire screen, and | |
| then changes the color partly in response to movement that it detects.</p> | |
| <h2 id="am">AndroidManifest.xml</h2> | |
| <p>An app with only native code must not specify an Android API level lower than 9, which introduced | |
| the <a href="{@docRoot}ndk/guides/concepts.html#naa">{@code NativeActivity}</a> framework class.</p> | |
| <pre class="no-pretty-print"> | |
| <uses-sdk android:minSdkVersion="9" /> | |
| </pre> | |
| <p>The following line declares {@code android:hasCode} as {@code false}, as this app has only | |
| native code–no Java. | |
| </p> | |
| <pre class="no-pretty-print"> | |
| <application android:label="@string/app_name" | |
| android:hasCode="false"> | |
| </pre> | |
| <p>The next line declares the {@code NativeActivity} class.</p> | |
| <pre class="no-pretty-print"> | |
| <activity android:name="android.app.NativeActivity" | |
| </pre> | |
| <p>Finally, the manifest specifies {@code android:value} as the name of the shared library to be | |
| built, minus the initial {@code lib} and the {@code .so} extension. This value must be the same as | |
| the name of {@code LOCAL_MODULE} in {@code Android.mk}.</p> | |
| <pre class="no-pretty-print"> | |
| <meta-data android:name="android.app.lib_name" | |
| android:value="native-activity" /> | |
| </pre> | |
| <h2 id="anm">Android.mk</h2> | |
| <p>This file begins by providing the name of the shared library to generate.</p> | |
| <pre class="no-pretty-print"> | |
| LOCAL_MODULE := native-activity | |
| </pre> | |
| <p>Next, it declares the name of the native source-code file.</p> | |
| <pre class="no-pretty-print"> | |
| LOCAL_SRC_FILES := main.c | |
| </pre> | |
| <p>Next, it lists the external libraries for the build system to use in building the binary. The | |
| {@code -l} (link-against) option precedes each library name.</p> | |
| <ul> | |
| <li>{@code log} is a logging library.</li> | |
| <li>{@code android} encompasses the standard Android support APIs for NDK. For more information about | |
| the APIs that Android and the NDK support, see <a href="stable_apis.html">Android NDK Native | |
| APIs</a>.</li> | |
| <li>{@code EGL} corresponds to the platform-specific portion of the graphics API.</li> | |
| <li>{@code GLESv1_CM} corresponds to OpenGL ES, the version of OpenGL for Android. This library | |
| depends on EGL.</li> | |
| </ul> | |
| <p>For each library:</p> | |
| <ul> | |
| <li>The actual file name starts with {@code lib}, and ends with the | |
| {@code .so} extension. For example, the actual file name for the | |
| {@code log} library is {@code liblog.so}.</li> | |
| <li>The library resides in the following directory, NDK root: | |
| {@code <ndk>/platforms/android-<sdk_version>/arch-<abi>/usr/lib/}.</li> | |
| </ul> | |
| <pre class="no-pretty-print"> | |
| LOCAL_LDLIBS := -llog -landroid -lEGL -lGLESv1_CM | |
| </pre> | |
| <p>The next line provides the name of the static library, {@code android_native_app_glue}, which the | |
| application uses to manage {@code NativeActivity} lifecycle events and touch input.</p> | |
| <pre class="no-pretty-print"> | |
| LOCAL_STATIC_LIBRARIES := android_native_app_glue | |
| </pre> | |
| <p>The final line tells the build system to build this static library. | |
| The {@code ndk-build} script places the built library | |
| ({@code libandroid_native_app_glue.a}) into the {@code obj} directory | |
| generated during the build process. For more information about the {@code android_native_app_glue} | |
| library, see its {@code android_native_app_glue.h} header and corresponding {@code .c}source file. | |
| </p> | |
| <pre class="no-pretty-print"> | |
| $(call import-module,android/native_app_glue) | |
| </pre> | |
| <p>For more information about the {@code Android.mk} file, see | |
| <a href="{@docRoot}ndk/guides/android_mk.html">Android.mk</a>.</p> | |
| <h2 id="apm">Application.mk</h2> | |
| <p>This line defines the minimum level of Android API Level support.</p> | |
| <pre class="no-pretty-print"> | |
| APP_PLATFORM := android-10 | |
| </pre> | |
| <p>Because there is no ABI definition, the build system defaults to building only for | |
| {@code armeabi}.</p> | |
| <h2 id="mac">main.c</h2> | |
| <p>This file essentially contains the entire progam.</p> | |
| <p>The following includes correspond to the libraries, both shared and static, | |
| enumerated in {@code Android.mk}.</p> | |
| <pre class="no-pretty-print"> | |
| #include <EGL/egl.h> | |
| #include <GLES/gl.h> | |
| #include <android/sensor.h> | |
| #include <android/log.h> | |
| #include <android_native_app_glue> | |
| </pre> | |
| <p>The {@code android_native_app_glue} library calls the following function, | |
| passing it a predefined state structure. It also serves as a wrapper that | |
| simplifies handling of {@code NativeActivity} callbacks.</p> | |
| <pre class="no-pretty-print"> | |
| void android_main(struct android_app* state) { | |
| </pre> | |
| <p>Next, the program handles events queued by the glue library. The event | |
| handler follows the state structure.</p> | |
| <pre class="no-pretty-print"> | |
| 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; | |
| </pre> | |
| <p>The application prepares to start monitoring the sensors, using the | |
| APIs in {@code sensor.h}.</p> | |
| <pre class="no-pretty-print"> | |
| 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); | |
| </pre> | |
| <p>Next, a loop begins, in which the application polls the system for | |
| messages (sensor events). It sends messages to | |
| {@code android_native_app_glue}, which checks to see whether they match | |
| any {@code onAppCmd} events defined in {@code android_main}. When a | |
| match occurs, the message is sent to the handler for execution.</p> | |
| <pre class="no-pretty-print"> | |
| 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; | |
| } | |
| } | |
| </pre> | |
| <p>Once the queue is empty, and the program exits the polling loop, the | |
| program calls OpenGL to draw the screen.</p> | |
| <pre class="no-pretty-print"> | |
| 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); | |
| } | |
| } | |
| </pre> |