David Friedman | 4a6d5c9 | 2015-05-22 02:31:51 -0700 | [diff] [blame] | 1 | page.title=Sample: native-activity |
David Friedman | e23031c | 2015-05-22 03:20:03 -0700 | [diff] [blame] | 2 | @jd:body |
| 3 | |
| 4 | <div id="qv-wrapper"> |
| 5 | <div id="qv"> |
| 6 | <h2>On this page</h2> |
| 7 | |
| 8 | <ol> |
David Friedman | 4a6d5c9 | 2015-05-22 02:31:51 -0700 | [diff] [blame] | 9 | <li><a href="#am">AndroidManifest.xml</a></li> |
| 10 | <li><a href="#anm">Android.mk</a></li> |
| 11 | <li><a href="#apm">Application.mk</a></li> |
| 12 | <li><a href="#mac">main.c</a></li> |
David Friedman | e23031c | 2015-05-22 03:20:03 -0700 | [diff] [blame] | 13 | </ol> |
| 14 | </li> |
| 15 | </ol> |
| 16 | </div> |
| 17 | </div> |
| 18 | |
David Friedman | 4a6d5c9 | 2015-05-22 02:31:51 -0700 | [diff] [blame] | 19 | <p>The native-activity sample resides under the NDK installation root, in |
| 20 | {@code samples/native-activity}. It is a very simple example of a purely native |
David Friedman | e23031c | 2015-05-22 03:20:03 -0700 | [diff] [blame] | 21 | application, with no Java source code. In the absence of any Java source, the |
David Friedman | 4a6d5c9 | 2015-05-22 02:31:51 -0700 | [diff] [blame] | 22 | Java compiler still creates an executable stub for the virtual machine to run. |
| 23 | The stub serves as a wrapper for the actual, native program, which lives in the {@code .so} |
| 24 | file.</p> |
| 25 | |
| 26 | <p>The app itself simply renders a color onto the entire screen, and |
| 27 | then changes the color partly in response to movement that it detects.</p> |
| 28 | |
| 29 | <h2 id="am">AndroidManifest.xml</h2> |
| 30 | |
| 31 | <p>An app with only native code must not specify an Android API level lower than 9, which introduced |
| 32 | the <a href="{@docRoot}ndk/guides/concepts.html#naa">{@code NativeActivity}</a> framework class.</p> |
| 33 | |
| 34 | <pre class="no-pretty-print"> |
| 35 | <uses-sdk android:minSdkVersion="9" /> |
| 36 | </pre> |
| 37 | |
| 38 | <p>The following line declares {@code android:hasCode} as {@code false}, as this app has only |
| 39 | native code–no Java. |
| 40 | </p> |
| 41 | |
| 42 | <pre class="no-pretty-print"> |
| 43 | <application android:label="@string/app_name" |
David Friedman | e23031c | 2015-05-22 03:20:03 -0700 | [diff] [blame] | 44 | android:hasCode="false"> |
David Friedman | 4a6d5c9 | 2015-05-22 02:31:51 -0700 | [diff] [blame] | 45 | </pre> |
| 46 | |
| 47 | <p>The next line declares the {@code NativeActivity} class.</p> |
| 48 | |
| 49 | <pre class="no-pretty-print"> |
| 50 | <activity android:name="android.app.NativeActivity" |
| 51 | </pre> |
| 52 | |
| 53 | <p>Finally, the manifest specifies {@code android:value} as the name of the shared library to be |
| 54 | built, minus the initial {@code lib} and the {@code .so} extension. This value must be the same as |
| 55 | the name of {@code LOCAL_MODULE} in {@code Android.mk}.</p> |
| 56 | |
| 57 | <pre class="no-pretty-print"> |
| 58 | <meta-data android:name="android.app.lib_name" |
| 59 | android:value="native-activity" /> |
| 60 | </pre> |
| 61 | |
| 62 | <h2 id="anm">Android.mk</h2> |
| 63 | <p>This file begins by providing the name of the shared library to generate.</p> |
| 64 | |
| 65 | <pre class="no-pretty-print"> |
| 66 | LOCAL_MODULE := native-activity |
| 67 | </pre> |
| 68 | |
| 69 | <p>Next, it declares the name of the native source-code file.</p> |
| 70 | |
| 71 | <pre class="no-pretty-print"> |
| 72 | LOCAL_SRC_FILES := main.c |
| 73 | </pre> |
| 74 | |
| 75 | <p>Next, it lists the external libraries for the build system to use in building the binary. The |
| 76 | {@code -l} (link-against) option precedes each library name.</p> |
| 77 | |
David Friedman | e23031c | 2015-05-22 03:20:03 -0700 | [diff] [blame] | 78 | <ul> |
David Friedman | 4a6d5c9 | 2015-05-22 02:31:51 -0700 | [diff] [blame] | 79 | <li>{@code log} is a logging library.</li> |
| 80 | <li>{@code android} encompasses the standard Android support APIs for NDK. For more information about |
| 81 | the APIs that Android and the NDK support, see <a href="stable_apis.html">Android NDK Native |
| 82 | APIs</a>.</li> |
| 83 | <li>{@code EGL} corresponds to the platform-specific portion of the graphics API.</li> |
| 84 | <li>{@code GLESv1_CM} corresponds to OpenGL ES, the version of OpenGL for Android. This library |
| 85 | depends on EGL.</li> |
David Friedman | e23031c | 2015-05-22 03:20:03 -0700 | [diff] [blame] | 86 | </ul> |
David Friedman | 4a6d5c9 | 2015-05-22 02:31:51 -0700 | [diff] [blame] | 87 | |
| 88 | <p>For each library:</p> |
| 89 | |
David Friedman | e23031c | 2015-05-22 03:20:03 -0700 | [diff] [blame] | 90 | <ul> |
David Friedman | 4a6d5c9 | 2015-05-22 02:31:51 -0700 | [diff] [blame] | 91 | <li>The actual file name starts with {@code lib}, and ends with the |
| 92 | {@code .so} extension. For example, the actual file name for the |
| 93 | {@code log} library is {@code liblog.so}.</li> |
| 94 | <li>The library resides in the following directory, NDK root: |
| 95 | {@code <ndk>/platforms/android-<sdk_version>/arch-<abi>/usr/lib/}.</li> |
David Friedman | e23031c | 2015-05-22 03:20:03 -0700 | [diff] [blame] | 96 | </ul> |
David Friedman | 4a6d5c9 | 2015-05-22 02:31:51 -0700 | [diff] [blame] | 97 | |
| 98 | <pre class="no-pretty-print"> |
| 99 | LOCAL_LDLIBS := -llog -landroid -lEGL -lGLESv1_CM |
| 100 | </pre> |
| 101 | |
| 102 | <p>The next line provides the name of the static library, {@code android_native_app_glue}, which the |
| 103 | application uses to manage {@code NativeActivity} lifecycle events and touch input.</p> |
| 104 | |
| 105 | <pre class="no-pretty-print"> |
| 106 | LOCAL_STATIC_LIBRARIES := android_native_app_glue |
| 107 | </pre> |
| 108 | |
| 109 | <p>The final line tells the build system to build this static library. |
| 110 | The {@code ndk-build} script places the built library |
| 111 | ({@code libandroid_native_app_glue.a}) into the {@code obj} directory |
| 112 | generated during the build process. For more information about the {@code android_native_app_glue} |
| 113 | library, see its {@code android_native_app_glue.h} header and corresponding {@code .c}source file. |
| 114 | </p> |
| 115 | |
| 116 | |
| 117 | <pre class="no-pretty-print"> |
| 118 | $(call import-module,android/native_app_glue) |
| 119 | </pre> |
| 120 | |
| 121 | <p>For more information about the {@code Android.mk} file, see |
| 122 | <a href="{@docRoot}ndk/guides/android_mk.html">Android.mk</a>.</p> |
| 123 | |
| 124 | |
| 125 | <h2 id="apm">Application.mk</h2> |
| 126 | |
David Friedman | e23031c | 2015-05-22 03:20:03 -0700 | [diff] [blame] | 127 | <p>This line defines the minimum level of Android API Level support.</p> |
David Friedman | 4a6d5c9 | 2015-05-22 02:31:51 -0700 | [diff] [blame] | 128 | |
| 129 | <pre class="no-pretty-print"> |
| 130 | APP_PLATFORM := android-10 |
| 131 | </pre> |
| 132 | |
| 133 | <p>Because there is no ABI definition, the build system defaults to building only for |
| 134 | {@code armeabi}.</p> |
| 135 | |
| 136 | <h2 id="mac">main.c</h2> |
David Friedman | e23031c | 2015-05-22 03:20:03 -0700 | [diff] [blame] | 137 | <p>This file essentially contains the entire progam.</p> |
David Friedman | 4a6d5c9 | 2015-05-22 02:31:51 -0700 | [diff] [blame] | 138 | |
David Friedman | e23031c | 2015-05-22 03:20:03 -0700 | [diff] [blame] | 139 | <p>The following includes correspond to the libraries, both shared and static, |
David Friedman | 4a6d5c9 | 2015-05-22 02:31:51 -0700 | [diff] [blame] | 140 | enumerated in {@code Android.mk}.</p> |
| 141 | |
| 142 | <pre class="no-pretty-print"> |
| 143 | #include <EGL/egl.h> |
David Friedman | e23031c | 2015-05-22 03:20:03 -0700 | [diff] [blame] | 144 | #include <GLES/gl.h> |
| 145 | |
| 146 | |
| 147 | #include <android/sensor.h> |
| 148 | #include <android/log.h> |
| 149 | #include <android_native_app_glue> |
David Friedman | 4a6d5c9 | 2015-05-22 02:31:51 -0700 | [diff] [blame] | 150 | </pre> |
| 151 | |
| 152 | <p>The {@code android_native_app_glue} library calls the following function, |
David Friedman | e23031c | 2015-05-22 03:20:03 -0700 | [diff] [blame] | 153 | passing it a predefined state structure. It also serves as a wrapper that |
David Friedman | 4a6d5c9 | 2015-05-22 02:31:51 -0700 | [diff] [blame] | 154 | simplifies handling of {@code NativeActivity} callbacks.</p> |
| 155 | |
| 156 | <pre class="no-pretty-print"> |
| 157 | void android_main(struct android_app* state) { |
| 158 | </pre> |
| 159 | |
| 160 | <p>Next, the program handles events queued by the glue library. The event |
David Friedman | e23031c | 2015-05-22 03:20:03 -0700 | [diff] [blame] | 161 | handler follows the state structure.</p> |
David Friedman | 4a6d5c9 | 2015-05-22 02:31:51 -0700 | [diff] [blame] | 162 | |
| 163 | <pre class="no-pretty-print"> |
| 164 | struct engine engine; |
David Friedman | e23031c | 2015-05-22 03:20:03 -0700 | [diff] [blame] | 165 | |
| 166 | |
David Friedman | 4a6d5c9 | 2015-05-22 02:31:51 -0700 | [diff] [blame] | 167 | |
| 168 | // Suppress link-time optimization that removes unreferenced code |
| 169 | // to make sure glue isn't stripped. |
David Friedman | e23031c | 2015-05-22 03:20:03 -0700 | [diff] [blame] | 170 | app_dummy(); |
| 171 | |
| 172 | |
| 173 | memset(&engine, 0, sizeof(engine)); |
| 174 | state->userData = &engine; |
| 175 | state->onAppCmd = engine_handle_cmd; |
| 176 | state->onInputEvent = engine_handle_input; |
| 177 | engine.app = state; |
David Friedman | 4a6d5c9 | 2015-05-22 02:31:51 -0700 | [diff] [blame] | 178 | </pre> |
| 179 | |
| 180 | <p>The application prepares to start monitoring the sensors, using the |
| 181 | APIs in {@code sensor.h}.</p> |
| 182 | |
| 183 | <pre class="no-pretty-print"> |
| 184 | engine.sensorManager = ASensorManager_getInstance(); |
David Friedman | e23031c | 2015-05-22 03:20:03 -0700 | [diff] [blame] | 185 | engine.accelerometerSensor = |
| 186 | ASensorManager_getDefaultSensor(engine.sensorManager, |
David Friedman | 4a6d5c9 | 2015-05-22 02:31:51 -0700 | [diff] [blame] | 187 | ASENSOR_TYPE_ACCELEROMETER); |
David Friedman | e23031c | 2015-05-22 03:20:03 -0700 | [diff] [blame] | 188 | engine.sensorEventQueue = |
| 189 | ASensorManager_createEventQueue(engine.sensorManager, |
David Friedman | 4a6d5c9 | 2015-05-22 02:31:51 -0700 | [diff] [blame] | 190 | state->looper, LOOPER_ID_USER, NULL, NULL); |
| 191 | </pre> |
| 192 | |
| 193 | <p>Next, a loop begins, in which the application polls the system for |
David Friedman | e23031c | 2015-05-22 03:20:03 -0700 | [diff] [blame] | 194 | messages (sensor events). It sends messages to |
David Friedman | 4a6d5c9 | 2015-05-22 02:31:51 -0700 | [diff] [blame] | 195 | {@code android_native_app_glue}, which checks to see whether they match |
| 196 | any {@code onAppCmd} events defined in {@code android_main}. When a |
David Friedman | e23031c | 2015-05-22 03:20:03 -0700 | [diff] [blame] | 197 | match occurs, the message is sent to the handler for execution.</p> |
David Friedman | 4a6d5c9 | 2015-05-22 02:31:51 -0700 | [diff] [blame] | 198 | |
| 199 | <pre class="no-pretty-print"> |
| 200 | while (1) { |
David Friedman | e23031c | 2015-05-22 03:20:03 -0700 | [diff] [blame] | 201 | // Read all pending events. |
| 202 | int ident; |
| 203 | int events; |
| 204 | struct android_poll_source* source; |
| 205 | |
| 206 | |
| 207 | // If not animating, we will block forever waiting for events. |
| 208 | // If animating, we loop until all events are read, then continue |
| 209 | // to draw the next frame of animation. |
| 210 | while ((ident=ALooper_pollAll(engine.animating ? 0 : -1, NULL, |
David Friedman | 4a6d5c9 | 2015-05-22 02:31:51 -0700 | [diff] [blame] | 211 | &events, |
David Friedman | e23031c | 2015-05-22 03:20:03 -0700 | [diff] [blame] | 212 | (void**)&source)) >= 0) { |
| 213 | |
| 214 | |
| 215 | // Process this event. |
| 216 | if (source != NULL) { |
| 217 | source->process(state, source); |
| 218 | } |
| 219 | |
| 220 | |
| 221 | // If a sensor has data, process it now. |
| 222 | if (ident == LOOPER_ID_USER) { |
| 223 | if (engine.accelerometerSensor != NULL) { |
| 224 | ASensorEvent event; |
| 225 | while (ASensorEventQueue_getEvents(engine.sensorEventQueue, |
| 226 | &event, 1) > 0) { |
| 227 | LOGI("accelerometer: x=%f y=%f z=%f", |
| 228 | event.acceleration.x, event.acceleration.y, |
| 229 | event.acceleration.z); |
| 230 | } |
| 231 | } |
| 232 | } |
| 233 | |
| 234 | |
| 235 | // Check if we are exiting. |
| 236 | if (state->destroyRequested != 0) { |
| 237 | engine_term_display(&engine); |
| 238 | return; |
| 239 | } |
| 240 | } |
David Friedman | 4a6d5c9 | 2015-05-22 02:31:51 -0700 | [diff] [blame] | 241 | </pre> |
| 242 | |
| 243 | <p>Once the queue is empty, and the program exits the polling loop, the |
David Friedman | e23031c | 2015-05-22 03:20:03 -0700 | [diff] [blame] | 244 | program calls OpenGL to draw the screen.</p> |
David Friedman | 4a6d5c9 | 2015-05-22 02:31:51 -0700 | [diff] [blame] | 245 | <pre class="no-pretty-print"> |
| 246 | if (engine.animating) { |
David Friedman | e23031c | 2015-05-22 03:20:03 -0700 | [diff] [blame] | 247 | // Done with events; draw next animation frame. |
| 248 | engine.state.angle += .01f; |
| 249 | if (engine.state.angle > 1) { |
| 250 | engine.state.angle = 0; |
| 251 | } |
| 252 | |
| 253 | |
| 254 | // Drawing is throttled to the screen update rate, so there |
| 255 | // is no need to do timing here. |
| 256 | engine_draw_frame(&engine); |
| 257 | } |
David Friedman | 4a6d5c9 | 2015-05-22 02:31:51 -0700 | [diff] [blame] | 258 | } |
| 259 | </pre> |