blob: 55362cd81960fc6c126582a41d67fa11fbc553d1 [file] [log] [blame]
David Friedman4a6d5c92015-05-22 02:31:51 -07001page.title=Sample: native-activity
David Friedmane23031c2015-05-22 03:20:03 -07002@jd:body
3
4<div id="qv-wrapper">
5 <div id="qv">
6 <h2>On this page</h2>
7
8 <ol>
David Friedman4a6d5c92015-05-22 02:31:51 -07009 <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 Friedmane23031c2015-05-22 03:20:03 -070013 </ol>
14 </li>
15 </ol>
16 </div>
17 </div>
18
David Friedman4a6d5c92015-05-22 02:31:51 -070019<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 Friedmane23031c2015-05-22 03:20:03 -070021application, with no Java source code. In the absence of any Java source, the
David Friedman4a6d5c92015-05-22 02:31:51 -070022Java compiler still creates an executable stub for the virtual machine to run.
23The stub serves as a wrapper for the actual, native program, which lives in the {@code .so}
24file.</p>
25
26<p>The app itself simply renders a color onto the entire screen, and
27then 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
32the <a href="{@docRoot}ndk/guides/concepts.html#naa">{@code NativeActivity}</a> framework class.</p>
33
34<pre class="no-pretty-print">
35&lt;uses-sdk android:minSdkVersion="9" /&gt;
36</pre>
37
38<p>The following line declares {@code android:hasCode} as {@code false}, as this app has only
39native code&ndash;no Java.
40</p>
41
42<pre class="no-pretty-print">
43&lt;application android:label="@string/app_name"
David Friedmane23031c2015-05-22 03:20:03 -070044android:hasCode="false"&gt;
David Friedman4a6d5c92015-05-22 02:31:51 -070045</pre>
46
47<p>The next line declares the {@code NativeActivity} class.</p>
48
49<pre class="no-pretty-print">
50&lt;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
54built, minus the initial {@code lib} and the {@code .so} extension. This value must be the same as
55the name of {@code LOCAL_MODULE} in {@code Android.mk}.</p>
56
57<pre class="no-pretty-print">
58&lt;meta-data android:name="android.app.lib_name"
59 android:value="native-activity" /&gt;
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">
66LOCAL_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">
72LOCAL_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 Friedmane23031c2015-05-22 03:20:03 -070078<ul>
David Friedman4a6d5c92015-05-22 02:31:51 -070079<li>{@code log} is a logging library.</li>
80<li>{@code android} encompasses the standard Android support APIs for NDK. For more information about
81the APIs that Android and the NDK support, see <a href="stable_apis.html">Android NDK Native
82APIs</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
85depends on EGL.</li>
David Friedmane23031c2015-05-22 03:20:03 -070086</ul>
David Friedman4a6d5c92015-05-22 02:31:51 -070087
88<p>For each library:</p>
89
David Friedmane23031c2015-05-22 03:20:03 -070090<ul>
David Friedman4a6d5c92015-05-22 02:31:51 -070091<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 &lt;ndk&gt;/platforms/android-&lt;sdk_version&gt;/arch-&lt;abi&gt;/usr/lib/}.</li>
David Friedmane23031c2015-05-22 03:20:03 -070096</ul>
David Friedman4a6d5c92015-05-22 02:31:51 -070097
98<pre class="no-pretty-print">
99LOCAL_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
103application uses to manage {@code NativeActivity} lifecycle events and touch input.</p>
104
105<pre class="no-pretty-print">
106LOCAL_STATIC_LIBRARIES := android_native_app_glue
107</pre>
108
109<p>The final line tells the build system to build this static library.
110The {@code ndk-build} script places the built library
111({@code libandroid_native_app_glue.a}) into the {@code obj} directory
112generated during the build process. For more information about the {@code android_native_app_glue}
113library, 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 Friedmane23031c2015-05-22 03:20:03 -0700127<p>This line defines the minimum level of Android API Level support.</p>
David Friedman4a6d5c92015-05-22 02:31:51 -0700128
129<pre class="no-pretty-print">
130APP_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 Friedmane23031c2015-05-22 03:20:03 -0700137<p>This file essentially contains the entire progam.</p>
David Friedman4a6d5c92015-05-22 02:31:51 -0700138
David Friedmane23031c2015-05-22 03:20:03 -0700139<p>The following includes correspond to the libraries, both shared and static,
David Friedman4a6d5c92015-05-22 02:31:51 -0700140enumerated in {@code Android.mk}.</p>
141
142<pre class="no-pretty-print">
143#include &lt;EGL/egl.h&gt;
David Friedmane23031c2015-05-22 03:20:03 -0700144#include &lt;GLES/gl.h&gt;
145
146
147#include &lt;android/sensor.h&gt;
148#include &lt;android/log.h&gt;
149#include &lt;android_native_app_glue&gt;
David Friedman4a6d5c92015-05-22 02:31:51 -0700150</pre>
151
152<p>The {@code android_native_app_glue} library calls the following function,
David Friedmane23031c2015-05-22 03:20:03 -0700153passing it a predefined state structure. It also serves as a wrapper that
David Friedman4a6d5c92015-05-22 02:31:51 -0700154simplifies handling of {@code NativeActivity} callbacks.</p>
155
156<pre class="no-pretty-print">
157void android_main(struct android_app* state) {
158</pre>
159
160<p>Next, the program handles events queued by the glue library. The event
David Friedmane23031c2015-05-22 03:20:03 -0700161handler follows the state structure.</p>
David Friedman4a6d5c92015-05-22 02:31:51 -0700162
163<pre class="no-pretty-print">
164struct engine engine;
David Friedmane23031c2015-05-22 03:20:03 -0700165
166
David Friedman4a6d5c92015-05-22 02:31:51 -0700167
168// Suppress link-time optimization that removes unreferenced code
169// to make sure glue isn't stripped.
David Friedmane23031c2015-05-22 03:20:03 -0700170app_dummy();
171
172
173memset(&amp;engine, 0, sizeof(engine));
174state-&gt;userData = &amp;engine;
175state-&gt;onAppCmd = engine_handle_cmd;
176state-&gt;onInputEvent = engine_handle_input;
177engine.app = state;
David Friedman4a6d5c92015-05-22 02:31:51 -0700178</pre>
179
180<p>The application prepares to start monitoring the sensors, using the
181APIs in {@code sensor.h}.</p>
182
183<pre class="no-pretty-print">
184 engine.sensorManager = ASensorManager_getInstance();
David Friedmane23031c2015-05-22 03:20:03 -0700185 engine.accelerometerSensor =
186 ASensorManager_getDefaultSensor(engine.sensorManager,
David Friedman4a6d5c92015-05-22 02:31:51 -0700187 ASENSOR_TYPE_ACCELEROMETER);
David Friedmane23031c2015-05-22 03:20:03 -0700188 engine.sensorEventQueue =
189 ASensorManager_createEventQueue(engine.sensorManager,
David Friedman4a6d5c92015-05-22 02:31:51 -0700190 state-&gt;looper, LOOPER_ID_USER, NULL, NULL);
191</pre>
192
193<p>Next, a loop begins, in which the application polls the system for
David Friedmane23031c2015-05-22 03:20:03 -0700194messages (sensor events). It sends messages to
David Friedman4a6d5c92015-05-22 02:31:51 -0700195{@code android_native_app_glue}, which checks to see whether they match
196any {@code onAppCmd} events defined in {@code android_main}. When a
David Friedmane23031c2015-05-22 03:20:03 -0700197match occurs, the message is sent to the handler for execution.</p>
David Friedman4a6d5c92015-05-22 02:31:51 -0700198
199<pre class="no-pretty-print">
200while (1) {
David Friedmane23031c2015-05-22 03:20:03 -0700201 // 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 Friedman4a6d5c92015-05-22 02:31:51 -0700211 &amp;events,
David Friedmane23031c2015-05-22 03:20:03 -0700212 (void**)&amp;source)) &gt;= 0) {
213
214
215 // Process this event.
216 if (source != NULL) {
217 source-&gt;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 &amp;event, 1) &gt; 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-&gt;destroyRequested != 0) {
237 engine_term_display(&amp;engine);
238 return;
239 }
240 }
David Friedman4a6d5c92015-05-22 02:31:51 -0700241</pre>
242
243<p>Once the queue is empty, and the program exits the polling loop, the
David Friedmane23031c2015-05-22 03:20:03 -0700244program calls OpenGL to draw the screen.</p>
David Friedman4a6d5c92015-05-22 02:31:51 -0700245<pre class="no-pretty-print">
246 if (engine.animating) {
David Friedmane23031c2015-05-22 03:20:03 -0700247 // Done with events; draw next animation frame.
248 engine.state.angle += .01f;
249 if (engine.state.angle &gt; 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(&amp;engine);
257 }
David Friedman4a6d5c92015-05-22 02:31:51 -0700258}
259</pre>