am 4a6d5c93: Docs: Sample-walkthrough chapters for NDK documentation
* commit '4a6d5c9399179abcef7877c43f46855152129bd3': Docs: Sample-walkthrough chapters for NDK documentation
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
page.title=Sample: HelloJNI
|
||||
page.title=Sample: hello-jni
|
||||
@jd:body
|
||||
|
||||
<div id="qv-wrapper">
|
||||
@@ -16,14 +16,15 @@ page.title=Sample: HelloJNI
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<p>This sample provides a bare-bones look at a minimal
|
||||
application built with the NDK.</p>
|
||||
<p>This sample provides a bare-bones look at HelloJNI, a minimal
|
||||
application built with the NDK. This sample is in the {@code samples/hello-jni/} directory
|
||||
under the root directory of your NDK installation.</p>
|
||||
|
||||
<h2 id="an">Android.mk</h2>
|
||||
|
||||
<p>The following two lines provide the name of the native source file, along
|
||||
with the name of the shared library to build. The full name of the built
|
||||
library is {@code libhello-jni.so}, but you should omit the
|
||||
library is {@code libhello-jni.so}, once the build system adds the
|
||||
{@code lib} prefix and the {@code .so} extension.</p>
|
||||
|
||||
<pre class="no-pretty-print">
|
||||
@@ -31,23 +32,23 @@ LOCAL_SRC_FILES := hello-jni.c
|
||||
LOCAL_MODULE := hello-jni
|
||||
</pre>
|
||||
|
||||
<p>For more information on what this file does, and how to use it, see
|
||||
<p>For more information about what the {@code Android.mk} file does, and how to use it, see
|
||||
<a href="{@docRoot}ndk/guides/android_mk.html">Android.mk</a>.</p>
|
||||
|
||||
<h2 id="ap">Application.mk</h2>
|
||||
<p>This line tells the build system the architecture against which to build. If
|
||||
you don't specify anything, the build system defaults to {@code armeabi}.</p>
|
||||
<p>This line tells the build system the CPU and architecture against which to build. In this
|
||||
example, the build system builds for all supported architectures.</p>
|
||||
|
||||
<pre class="no-pretty-print">
|
||||
APP_ABI := all
|
||||
</pre>
|
||||
|
||||
<p>For more information on what this file does, and how to use it, see
|
||||
<p>For more information about the {@code Application.mk} file, and how to use it, see
|
||||
<a href="{@docRoot}ndk/guides/application_mk.html">Application.mk</a>.</p>
|
||||
|
||||
<h2 id="ji">Java-side implementation</h2>
|
||||
<p>This file calls a function to retrieve a string from the native side, then
|
||||
displays it on the screen.</p>
|
||||
<h2 id="ji">Java-side Implementation</h2>
|
||||
<p>The {@code helloJNI.java} file is located in {@code hellojni/src/com/example/hellojni/}. It calls
|
||||
a function to retrieve a string from the native side, then displays it on the screen.</p>
|
||||
|
||||
<p>The source code contains three lines of particular interest to the NDK user.
|
||||
They are presented here in the order in which they are used, rather than by
|
||||
@@ -60,8 +61,8 @@ System.loadLibrary("hello-jni");
|
||||
</pre>
|
||||
|
||||
<p>The {@code native} keyword in this method declaration tells the
|
||||
virtual machine that the function is in the shared library (i.e., implemented on the native side).
|
||||
</p>
|
||||
virtual machine that the function is in the shared library (that is, implemented on the native
|
||||
side).</p>
|
||||
|
||||
<pre class="no-pretty-print">
|
||||
public native String stringFromJNI();
|
||||
@@ -74,15 +75,15 @@ previous steps, displaying the string on the screen.</p>
|
||||
tv.setText( stringFromJNI() );
|
||||
</pre>
|
||||
|
||||
<h2 id="ci">C-side implementation</h2>
|
||||
<p>This file contains a function that returns a string that the Java side
|
||||
requested (see <a href="#ji">Java-side implementation</a>). The function declaration is as
|
||||
<h2 id="ci">C-side Implementation</h2>
|
||||
<p>The {@code hello-jni.c} file is located in {@code hello-jni/jni/}. It contains a function that
|
||||
returns a string that <a href="#ji">the Java side requested</a>). The function declaration is as
|
||||
follows:</p>
|
||||
|
||||
<pre>
|
||||
jstring Java_com_example_hellojni_HelloJni_stringFromJNI(
|
||||
JNIEnv* env,
|
||||
jobject x )
|
||||
jstring
|
||||
Java_com_example_hellojni_HelloJni_stringFromJNI( JNIEnv* env,
|
||||
jobject thiz )
|
||||
</pre>
|
||||
|
||||
<p>This declaration corresponds to the native function declared in the
|
||||
@@ -104,12 +105,12 @@ according to the following rules:</p>
|
||||
<li>After the last underscore, append the function name.</li>
|
||||
</ul>
|
||||
|
||||
<p>Based on these rules, in this example, the function name
|
||||
{@code Java_com_example_hellojni_HelloJni_stringFromJNI} refers to a Java
|
||||
<p>Following these rules, this example uses the function name
|
||||
{@code Java_com_example_hellojni_HelloJni_stringFromJNI}. This name refers to a Java
|
||||
function called {@code stringFromJNI()}, which resides in
|
||||
{@code hellojni/src/com/example/hellojni/HelloJni.java}.</p>
|
||||
|
||||
<p>Finally, {@code JNIEnv*} is the pointer to the VM, and
|
||||
<p>{@code JNIEnv*} is the pointer to the VM, and
|
||||
{@code jobject} is a pointer to the implicit {@code this} object passed from
|
||||
the Java side.</p>
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
page.title=Sample: Native Activity
|
||||
page.title=Sample: native-activity
|
||||
@jd:body
|
||||
|
||||
<div id="qv-wrapper">
|
||||
@@ -6,102 +6,167 @@ page.title=Sample: Native Activity
|
||||
<h2>On this page</h2>
|
||||
|
||||
<ol>
|
||||
<li><a href="#bb">Before Beginning</a></li>
|
||||
<li><a href="#intro">Introduction</a></li>
|
||||
<li><a href="#hiw">How It Works</a></li>
|
||||
<li><a href="#naa">Native Activities and Applications</a></li>
|
||||
<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>This is a very simple example of a purely native
|
||||
<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 Dalvik Virtual Machine
|
||||
("DVM") to run. The stub serves as a wrapper for the actual, native program,
|
||||
which lives in the .so file.</p>
|
||||
<p>The application itself simply renders a color onto the entire screen, and
|
||||
then changes the color partly in response to detected movement.</p>
|
||||
<h3>AndroidManifest.xml</h3>
|
||||
<p>Make sure not to specify an Android API level lower than 9.</p>
|
||||
<pre class="fragment"><uses-sdk android:minSdkVersion="9" />
|
||||
</pre><p>Because this application has only native code, specify
|
||||
<code>android:hasCode</code> as <code>false</code>.</p>
|
||||
<pre class="fragment"><application android:label="@string/app_name"
|
||||
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 lives 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>Declare the <code>NativeActivity</code> class.</p>
|
||||
<pre class="fragment"> <activity android:name="android.app.NativeActivity"
|
||||
</pre><p>For <code>android:value</code>, provide the name of the shared library
|
||||
to be built, minus the initial <code>lib</code> and the <code>.so</code>
|
||||
extension. This value must be the same as the one you described for
|
||||
<code>LOCAL_MODULE</code> in <code>Android.mk</code>.</p>
|
||||
<pre class="fragment"> <meta-data android:name="android.app.lib_name"
|
||||
android:value="native-activity" />
|
||||
</pre><h3>Android.mk</h3>
|
||||
<p>This file tells the build system the following information:</p>
|
||||
<p>The name of the shared library to generate.</p>
|
||||
<pre class="fragment">LOCAL_MODULE := native-activity
|
||||
</pre><p>The name of the native source-code file.</p>
|
||||
<pre class="fragment">LOCAL_SRC_FILES := main.c
|
||||
</pre><p>A list of external libraries that will be used in building the binary,
|
||||
each preceded by the <code>-l</code> (link-against) option.</p>
|
||||
</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>log is a logging library.</li>
|
||||
<li>android encompasses the standard Android support APIs for NDK. The <a href="./md_3__key__topics__libraries__s_t_a_b_l_e-_a_p_i_s.html">Stable APIs</a>
|
||||
section discusses these in more detail.</li>
|
||||
<li>EGL, standardized by Khronos, corresponds to the platform-specific portion
|
||||
of the graphics API.</li>
|
||||
<li>OpenGL ES, the version of OpenGL for Android, depends on EGL.</li>
|
||||
<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>Note that, for each library:</p>
|
||||
|
||||
<p>For each library:</p>
|
||||
|
||||
<ul>
|
||||
<li>The actual file name starts with <code>lib</code>, and ends with the
|
||||
<code>.so</code> extension. For example, the actual file name for the
|
||||
<code>log</code> library is <code>liblog.so</code>.</li>
|
||||
<li>The library lives in the following directory, relative to the NDK root:
|
||||
<code><ndk>/platforms/android-<sdk_version>/arch-<abi>/usr/lib/</code>.</li>
|
||||
<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="fragment">LOCAL_LDLIBS := -llog -landroid -lEGL -lGLESv1_CM
|
||||
</pre><p>A static library, <code>android_native_app_glue</code>, that the
|
||||
application uses to manage <code>NativeActivity</code> lifecycle events, along
|
||||
with touch input.</p>
|
||||
<pre class="fragment">LOCAL_STATIC_LIBRARIES := android_native_app_glue
|
||||
</pre><p>The final line tells the build system to build this static library.
|
||||
<code>ndk-build</code> places the built library
|
||||
(<code>libandroid_native_app_glue.a</code>) into the <code>obj</code> directory
|
||||
generated during the build process. The next sample discusses the
|
||||
android_native_app_glue in more detail.</p>
|
||||
<pre class="fragment">$(call import-module,android/native_app_glue)
|
||||
</pre><p>For more information about the Application.mk file, consult the <a
|
||||
href="./md_3__key__topics__building__a_p_p_l_i_c_a_t_i_o_n-_m_k.html">Application.mk section</a> of this guide.</p>
|
||||
<h3><code>Application.mk</code></h3>
|
||||
|
||||
<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="fragment">APP_PLATFORM := android-10
|
||||
</pre><p>Because there is no ABI definition, the build system defaults to
|
||||
building only for armeabi.</p>
|
||||
<h3>main.c</h3>
|
||||
|
||||
<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</code>.</p>
|
||||
<pre class="fragment">#include <EGL/egl.h>
|
||||
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><code>android_native_app_glue</code> calls the following function,
|
||||
</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</code> callbacks.</p>
|
||||
<pre class="fragment">void android_main(struct android_app* state) {
|
||||
</pre><p>Next, the program handles events queued by the glue library. The event
|
||||
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="fragment">struct engine engine;
|
||||
|
||||
<pre class="no-pretty-print">
|
||||
struct engine engine;
|
||||
|
||||
|
||||
// Make sure glue isn't stripped by suppressing link-time optimization that
|
||||
removes unreferenced code.
|
||||
|
||||
// Suppress link-time optimization that removes unreferenced code
|
||||
// to make sure glue isn't stripped.
|
||||
app_dummy();
|
||||
|
||||
|
||||
@@ -110,21 +175,29 @@ 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</code>.</p>
|
||||
<pre class="fragment"> engine.sensorManager = ASensorManager_getInstance();
|
||||
</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);
|
||||
ASENSOR_TYPE_ACCELEROMETER);
|
||||
engine.sensorEventQueue =
|
||||
ASensorManager_createEventQueue(engine.sensorManager,
|
||||
state->looper, LOOPER_ID_USER, NULL, NULL);
|
||||
</pre><p>Now, a loop begins, in which the application polls the system for
|
||||
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</code>, which checks to see whether they match
|
||||
any <code>onAppCmd</code> events defined in <code>android_main</code>. When a
|
||||
{@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="fragment">while (1) {
|
||||
|
||||
<pre class="no-pretty-print">
|
||||
while (1) {
|
||||
// Read all pending events.
|
||||
int ident;
|
||||
int events;
|
||||
@@ -135,7 +208,7 @@ match occurs, the message is sent to the handler for execution.</p>
|
||||
// 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,
|
||||
&events,
|
||||
(void**)&source)) >= 0) {
|
||||
|
||||
|
||||
@@ -165,9 +238,12 @@ match occurs, the message is sent to the handler for execution.</p>
|
||||
return;
|
||||
}
|
||||
}
|
||||
</pre><p>Once the queue is empty, and the program exits the polling loop, the
|
||||
</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="fragment"> if (engine.animating) {
|
||||
<pre class="no-pretty-print">
|
||||
if (engine.animating) {
|
||||
// Done with events; draw next animation frame.
|
||||
engine.state.angle += .01f;
|
||||
if (engine.state.angle > 1) {
|
||||
@@ -179,16 +255,5 @@ program calls OpenGL to draw the screen.</p>
|
||||
// is no need to do timing here.
|
||||
engine_draw_frame(&engine);
|
||||
}
|
||||
} </pre> </div></div><!-- contents -->
|
||||
</div><!-- doc-content -->
|
||||
<!-- start footer part -->
|
||||
<div id="nav-path" class="navpath"><!-- id is needed for treeview function! -->
|
||||
<ul>
|
||||
<li class="footer">Generated on Wed Jun 25 2014 00:51:19 for NDK
|
||||
Programmer's Guide by
|
||||
<a href="http://www.doxygen.org/index.html">
|
||||
<img class="footer" src="doxygen.png" alt="doxygen"/></a> 1.8.5 </li>
|
||||
</ul>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
}
|
||||
</pre>
|
||||
@@ -6,58 +6,80 @@ page.title=Sample: Teapot
|
||||
<h2>On this page</h2>
|
||||
|
||||
<ol>
|
||||
<li><a href="#bb">Before Beginning</a></li>
|
||||
<li><a href="#intro">Introduction</a></li>
|
||||
<li><a href="#hiw">How It Works</a></li>
|
||||
<li><a href="#naa">Native Activities and Applications</a></li>
|
||||
<li><a href="#am">AndroidManifest.xml</a></li>
|
||||
<li><a href="#ap">Application.mk</a></li>
|
||||
<li><a href="#ji">Java-side Implementation</a></li>
|
||||
<li><a href="#ni">Native-side Implementation</a></li>
|
||||
</ol>
|
||||
</li>
|
||||
</ol>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<p>This sample uses the OpenGL library to render the
|
||||
iconic <a
|
||||
href="http://math.hws.edu/bridgeman/courses/324/s06/doc/opengl.html#basic">Utah
|
||||
teapot</a>. It particularly showcases the <code>ndk_helper</code> helper class,
|
||||
<p>The Teapot sample is located under in the {@code samples/Teapot/} directory, under the NDK
|
||||
installation's root directory. This sample uses the OpenGL library to render the iconic
|
||||
<a href="http://math.hws.edu/bridgeman/courses/324/s06/doc/opengl.html#basic">Utah
|
||||
teapot</a>. In particular, it showcases the {@code ndk_helper} helper class,
|
||||
a collection of native helper functions required for implementing games and
|
||||
similar applications as native applications. This class provides:</p>
|
||||
|
||||
<ul>
|
||||
<li>an abstraction layer that handles certain NDK-specific behaviors (e.g.,
|
||||
<code>GLContext</code>).</li>
|
||||
<li>some helper functions that are useful but not present in the NDK, itself
|
||||
(e.g., tap detection).</li>
|
||||
<li>wrappers for JNI calls for certain platform features (e.g., texture
|
||||
loading).</li>
|
||||
<li>An abstraction layer, {@code GLContext}, that handles certain NDK-specific behaviors.</li>
|
||||
<li>Helper functions that are useful but not present in the NDK, such as tap detection.</li>
|
||||
<li>Wrappers for JNI calls for platform features such as texture loading.</li>
|
||||
</ul>
|
||||
<h3>AndroidManifest.xml</h3>
|
||||
<p>The activity declaration here is not <code>NativeActivity</code> itself, but
|
||||
a subclass: <code>TeapotNativeActivity</code>.</p>
|
||||
<pre class="fragment"> <activity
|
||||
android:name="com.sample.teapot.TeapotNativeActivity"
|
||||
|
||||
<h2 id="am">AndroidManifest.xml</h2>
|
||||
<p>The activity declaration here is not {@link android.app.NativeActivity} itself, but
|
||||
a subclass of it: {@code TeapotNativeActivity}.</p>
|
||||
|
||||
<pre class="no-pretty-print">
|
||||
<activity android:name="com.sample.teapot.TeapotNativeActivity"
|
||||
android:label="@string/app_name"
|
||||
android:configChanges="orientation|keyboardHidden">
|
||||
</pre><p>The name of the <code>.so</code> file is
|
||||
<code>libTeapotNativeActivity.so</code>; the <code>lib</code> and
|
||||
<code>.so</code> are stripped off from the value assigned to
|
||||
<code>android:value</code>.</p>
|
||||
<pre class="fragment"> <meta-data android:name="android.app.lib_name"
|
||||
android:value="TeapotNativeActivity" />
|
||||
</pre><h3><code>Application.mk</code></h3>
|
||||
<p>Define the minimum level of Android API Level support.</p>
|
||||
<pre class="fragment">APP_PLATFORM := android-9
|
||||
</pre><p>Build for all supported architectures.</p>
|
||||
<pre class="fragment">APP_ABI := all
|
||||
</pre><p>Specify the <a
|
||||
href="./md_3__key__topics__libraries__c_p_l_u_s_p_l_u_s-_s_u_p_p_o_r_t.html">C++
|
||||
runtime support library</a> to use. </p>
|
||||
<pre class="fragment">APP_STL := stlport_static
|
||||
</pre><h3>Java-side implementation: TeapotNativeActivity.java</h3>
|
||||
<p>This file handles activity lifecycle events, as well as displaying text on
|
||||
the screen.</p>
|
||||
<pre class="fragment">// Our popup window, you will call it from your C/C++
|
||||
code later
|
||||
</pre>
|
||||
|
||||
<p>Ultimately, the name of the shared-object file that the build system builds is
|
||||
{@code libTeapotNativeActivity.so}. The build system adds the {@code lib} prefix and the {@code .so}
|
||||
extension; neither is part of the value that the manifest originally assigns to
|
||||
{@code android:value}.</p>
|
||||
|
||||
<pre class="no-pretty-print">
|
||||
<meta-data android:name="android.app.lib_name"
|
||||
android:value="TeapotNativeActivity" />
|
||||
</pre>
|
||||
|
||||
<h2 id="ap">Application.mk</h2>
|
||||
<p>An app that uses the {@link android.app.NativeActivity} framework class must not specify an
|
||||
Android API level lower than 9, which introduced that class. For more information about the
|
||||
{@link android.app.NativeActivity} class, see
|
||||
<a href="{@docRoot}ndk/guides/concepts.html#naa">Native Activities and Applications</a>.
|
||||
</p>
|
||||
|
||||
<pre class="no-pretty-print">
|
||||
APP_PLATFORM := android-9
|
||||
</pre>
|
||||
|
||||
<p>The next line tells the build system to build for all supported architectures.</p>
|
||||
<pre class="no-pretty-print">
|
||||
APP_ABI := all
|
||||
</pre>
|
||||
|
||||
<p>Next, the file tells the build system which
|
||||
<a href="{@docRoot}ndk/guides/cpp-support.html">C++ runtime support library</a> to use. </p>
|
||||
|
||||
<pre class="no-pretty-print">
|
||||
APP_STL := stlport_static
|
||||
</pre>
|
||||
|
||||
<h2 id="ji">Java-side Implementation</h2>
|
||||
<p>The {@code TeapotNativeActivity.java} file is located in
|
||||
{@code samples/Teapot/src/com/sample/teapot}, under the NDK installation root directory. It handles
|
||||
activity lifecycle events, and also enables the app to display text on the screen. The following
|
||||
block of code is most important from the perspective of the native-side implementation: The native
|
||||
code calls it to display a popup window for text display.</p>
|
||||
|
||||
<pre class="no-pretty-print">
|
||||
|
||||
void setImmersiveSticky() {
|
||||
View decorView = getWindow().getDecorView();
|
||||
@@ -68,17 +90,19 @@ void setImmersiveSticky() {
|
||||
| View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
|
||||
| View.SYSTEM_UI_FLAG_LAYOUT_STABLE);
|
||||
}
|
||||
</pre><h3>Native-side implementation</h3>
|
||||
</pre>
|
||||
|
||||
<p>This section explores the C++ part of the Teapot app.</p>
|
||||
<h2 id="ni">Native-side Implementation</h2>
|
||||
|
||||
<h4>TeapotRenderer.*</h4>
|
||||
<p>This section explores the part of the Teapot app implemented in C++.</p>
|
||||
|
||||
<p>This code does the actual rendering of the teapot. It uses
|
||||
<code>ndk_helper</code> for matrix calculation, and to reposition the camera
|
||||
based on where the user taps:</p>
|
||||
<h3>TeapotRenderer.h</h3>
|
||||
|
||||
<pre>
|
||||
<p>These function calls perform the actual rendering of the teapot. It uses
|
||||
{@code ndk_helper} for matrix calculation and to reposition the camera
|
||||
based on where the user taps.</p>
|
||||
|
||||
<pre class="no-pretty-print">
|
||||
ndk_helper::Mat4 mat_projection_;
|
||||
ndk_helper::Mat4 mat_view_;
|
||||
ndk_helper::Mat4 mat_model_;
|
||||
@@ -87,50 +111,75 @@ ndk_helper::Mat4 mat_model_;
|
||||
ndk_helper::TapCamera* camera_;
|
||||
</pre>
|
||||
|
||||
<h4>TeapotNativeActivity.cpp</code></h4>
|
||||
<h3>TeapotNativeActivity.cpp</h3>
|
||||
|
||||
<p>Include <code>ndk_helper</code> in your native source file, and define the
|
||||
helper-class name:</p>
|
||||
<pre class="fragment">#include "NDKHelper.h"
|
||||
<p>The following lines include {@code ndk_helper} in the native source file, and define the
|
||||
helper-class name.</p>
|
||||
|
||||
<pre class="no-pretty-print">
|
||||
|
||||
#include "NDKHelper.h"
|
||||
|
||||
//-------------------------------------------------------------------------
|
||||
//Preprocessor
|
||||
//-------------------------------------------------------------------------
|
||||
#define HELPER_CLASS_NAME "com/sample/helper/NDKHelper" //Class name of helper
|
||||
function
|
||||
</pre><p>The first use of the <code>ndk_helper</code> class is to handle the
|
||||
</pre>
|
||||
|
||||
<p>The first use of the {@code ndk_helper} class is to handle the
|
||||
EGL-related lifecycle, associating EGL context states (created/lost) with
|
||||
Android lifecycle events. It enables the application to preserve context
|
||||
information so that a destroyed activity can be restored. This is useful, for
|
||||
Android lifecycle events. The {@code ndk_helper} class enables the application to preserve context
|
||||
information so that the system can restore a destroyed activity. This ability is useful, for
|
||||
example, when the target machine is rotated (causing an activity to be
|
||||
destroyed, then immediately restored in the new orientation), or when the lock
|
||||
screen appears.</p>
|
||||
<pre class="fragment">ndk_helper::GLContext* gl_context_; // handles
|
||||
EGL-related lifecycle.
|
||||
</pre><p>Next, <code>ndk_helper</code> provides touch control.</p>
|
||||
<pre class="fragment">ndk_helper::DoubletapDetector doubletap_detector_;
|
||||
|
||||
<pre class="no-pretty-print">
|
||||
ndk_helper::GLContext* gl_context_; // handles EGL-related lifecycle.
|
||||
</pre>
|
||||
|
||||
<p>Next, {@code ndk_helper} provides touch control.</p>
|
||||
|
||||
<pre class="no-pretty-print">
|
||||
ndk_helper::DoubletapDetector doubletap_detector_;
|
||||
ndk_helper::PinchDetector pinch_detector_;
|
||||
ndk_helper::DragDetector drag_detector_;
|
||||
ndk_helper::PerfMonitor monitor_;
|
||||
</pre><p>And camera control (openGL view frustum).</p>
|
||||
<pre class="fragment">ndk_helper::TapCamera tap_camera_;
|
||||
</pre><p>As in the native-activity sample, the application prepares to use the
|
||||
sensors, using the native APIs provided in the NDK.</p>
|
||||
<pre class="fragment">ASensorManager* sensor_manager_;
|
||||
</pre>
|
||||
|
||||
<p>It also provides camera control (openGL view frustum).</p>
|
||||
|
||||
<pre class="no-pretty-print">
|
||||
ndk_helper::TapCamera tap_camera_;
|
||||
</pre>
|
||||
|
||||
<p>The app then prepares to use the device's sensors, using the native APIs provided in the NDK.</p>
|
||||
|
||||
<pre class="no-pretty-print">
|
||||
ASensorManager* sensor_manager_;
|
||||
const ASensor* accelerometer_sensor_;
|
||||
ASensorEventQueue* sensor_event_queue_;
|
||||
</pre><p>The following functions are called in response to various Android
|
||||
</pre>
|
||||
|
||||
<p>The app calls the following functions in response to various Android
|
||||
lifecycle events and EGL context state changes, using various functionalities
|
||||
provided by <code>ndk_helper</code> via the <code>Engine</code> class.</p>
|
||||
<pre class="fragment">void LoadResources();
|
||||
provided by {@code ndk_helper} via the {@code Engine} class.</p>
|
||||
|
||||
<pre class="no-pretty-print">
|
||||
|
||||
void LoadResources();
|
||||
void UnloadResources();
|
||||
void DrawFrame();
|
||||
void TermDisplay();
|
||||
void TrimMemory();
|
||||
bool IsReady();
|
||||
</pre><p>This function calls back to the Java side to update the UI display.</p>
|
||||
<pre class="fragment">void Engine::ShowUI()
|
||||
</pre>
|
||||
|
||||
<p>Then, the following function calls back to the Java side to update the UI display.</p>
|
||||
|
||||
<pre class="no-pretty-print">
|
||||
void Engine::ShowUI()
|
||||
{
|
||||
JNIEnv *jni;
|
||||
app_->activity->vm->AttachCurrentThread( &jni, NULL );
|
||||
@@ -145,10 +194,14 @@ bool IsReady();
|
||||
app_->activity->vm->DetachCurrentThread();
|
||||
return;
|
||||
}
|
||||
</pre><p>And this one calls back to the Java side to draw a text box
|
||||
</pre>
|
||||
|
||||
<p>Next, this function calls back to the Java side to draw a text box
|
||||
superimposed on the screen rendered on the native side, and showing frame
|
||||
count.</p>
|
||||
<pre class="fragment">void Engine::UpdateFPS( float fFPS )
|
||||
|
||||
<pre class="no-pretty-print">
|
||||
void Engine::UpdateFPS( float fFPS )
|
||||
{
|
||||
JNIEnv *jni;
|
||||
app_->activity->vm->AttachCurrentThread( &jni, NULL );
|
||||
@@ -163,74 +216,145 @@ count.</p>
|
||||
app_->activity->vm->DetachCurrentThread();
|
||||
return;
|
||||
}
|
||||
</pre><p>The application gets the system clock and supplies it to the renderer
|
||||
for time-based animation based on real-time clock. For example, calculating
|
||||
momentum, where speed declines as a function of time.</p>
|
||||
<pre class="fragment">renderer_.Update( monitor_.GetCurrentTime() );
|
||||
</pre><p>Having earlier been set up to preserve context information, the
|
||||
application now checks whether <code>GLcontext</code> is still valid. If not,
|
||||
<code>ndk-helper</code> swaps the buffer, reinstantiating the GL context.</p>
|
||||
<pre class="fragment">if( EGL_SUCCESS != gl_context_->Swap() ) // swaps
|
||||
buffer.
|
||||
</pre><p>The program passes touch-motion events to the gesture detector defined
|
||||
in the <code>ndk_helper</code> class. The gesture detector tracks multitouch
|
||||
gestures, such as pinch-and-drag, and sends a notification when triggered by
|
||||
any of these events.</p>
|
||||
<pre class="fragment">if( AInputEvent_getType( event ) ==
|
||||
AINPUT_EVENT_TYPE_MOTION )
|
||||
{
|
||||
ndk_helper::GESTURE_STATE doubleTapState =
|
||||
eng->doubletap_detector_.Detect( event );
|
||||
ndk_helper::GESTURE_STATE dragState = eng->drag_detector_.Detect( event
|
||||
);
|
||||
ndk_helper::GESTURE_STATE pinchState = eng->pinch_detector_.Detect(
|
||||
event );
|
||||
|
||||
|
||||
//Double tap detector has a priority over other detectors
|
||||
if( doubleTapState == ndk_helper::GESTURE_STATE_ACTION )
|
||||
{
|
||||
//Detect double tap
|
||||
eng->tap_camera_.Reset( true );
|
||||
}
|
||||
else
|
||||
{
|
||||
//Handle pinch state
|
||||
if( pinchState & ndk_helper::GESTURE_STATE_START )
|
||||
{
|
||||
//Start new pinch
|
||||
ndk_helper::Vec2 v1;
|
||||
ndk_helper::Vec2 v2;
|
||||
eng->pinch_detector_.GetPointers( v1, v2 );
|
||||
</pre><p><code>ndk_helper</code> also provides access to a vector-math library
|
||||
(<code>vecmath.h</code>), using it here to transform touch coordinates.</p>
|
||||
<pre class="fragment">void Engine::TransformPosition( ndk_helper::Vec2& vec
|
||||
) { vec = ndk_helper::Vec2( 2.0f, 2.0f ) * vec / ndk_helper::Vec2(
|
||||
gl_context_->GetScreenWidth(), gl_context_->GetScreenHeight() )
|
||||
|
||||
ndk_helper::Vec2( 1.f, 1.f ); }</li></pre>
|
||||
</ul>
|
||||
<p><code>HandleCmd()</code> handles commands posted from the
|
||||
android_native_app_glue library. For more information about what the messages
|
||||
mean, refer to the comments in the <code>android_native_app_glue.h</code> and
|
||||
<code>.c</code> source files.</p>
|
||||
|
||||
<pre>
|
||||
void Engine::HandleCmd( struct android_app* app, int32_t
|
||||
cmd ) { Engine* eng = (Engine*) app->userData; switch( cmd ) { case
|
||||
APP_CMD_SAVE_STATE: break; case APP_CMD_INIT_WINDOW: // The window is being
|
||||
shown, get it ready. if( app->window != NULL )
|
||||
</pre>
|
||||
|
||||
<p><code>ndk_helper</code> posts APP_CMD_INIT_WINDOW when android_app_glue
|
||||
receives an <code>onNativeWindowCreated()</code> callback from the system.
|
||||
<p>The application gets the system clock and supplies it to the renderer
|
||||
for time-based animation based on real-time clock. This information is used, for example, in
|
||||
calculating momentum, where speed declines as a function of time.</p>
|
||||
|
||||
<pre class="no-pretty-print">
|
||||
renderer_.Update( monitor_.GetCurrentTime() );
|
||||
</pre>
|
||||
|
||||
<p>The application now checks whether the context information that {@code GLcontext} holds is still
|
||||
valid. If not, {@code ndk-helper} swaps the buffer, reinstantiating the GL context.</p>
|
||||
|
||||
<pre class="no-pretty-print">
|
||||
if( EGL_SUCCESS != gl_context_->Swap() ) // swaps
|
||||
buffer.
|
||||
</pre>
|
||||
|
||||
<p>The program passes touch-motion events to the gesture detector defined
|
||||
in the {@code ndk_helper} class. The gesture detector tracks multitouch
|
||||
gestures, such as pinch-and-drag, and sends a notification when triggered by
|
||||
any of these events.</p>
|
||||
|
||||
<pre class="no-pretty-print">
|
||||
if( AInputEvent_getType( event ) == AINPUT_EVENT_TYPE_MOTION )
|
||||
{
|
||||
ndk_helper::GESTURE_STATE doubleTapState =
|
||||
eng->doubletap_detector_.Detect( event );
|
||||
ndk_helper::GESTURE_STATE dragState = eng->drag_detector_.Detect( event );
|
||||
ndk_helper::GESTURE_STATE pinchState = eng->pinch_detector_.Detect( event );
|
||||
|
||||
//Double tap detector has a priority over other detectors
|
||||
if( doubleTapState == ndk_helper::GESTURE_STATE_ACTION )
|
||||
{
|
||||
//Detect double tap
|
||||
eng->tap_camera_.Reset( true );
|
||||
}
|
||||
else
|
||||
{
|
||||
//Handle drag state
|
||||
if( dragState & ndk_helper::GESTURE_STATE_START )
|
||||
{
|
||||
//Otherwise, start dragging
|
||||
ndk_helper::Vec2 v;
|
||||
eng->drag_detector_.GetPointer( v );
|
||||
eng->TransformPosition( v );
|
||||
eng->tap_camera_.BeginDrag( v );
|
||||
}
|
||||
// ...else other possible drag states...
|
||||
|
||||
//Handle pinch state
|
||||
if( pinchState & ndk_helper::GESTURE_STATE_START )
|
||||
{
|
||||
//Start new pinch
|
||||
ndk_helper::Vec2 v1;
|
||||
ndk_helper::Vec2 v2;
|
||||
eng->pinch_detector_.GetPointers( v1, v2 );
|
||||
eng->TransformPosition( v1 );
|
||||
eng->TransformPosition( v2 );
|
||||
eng->tap_camera_.BeginPinch( v1, v2 );
|
||||
}
|
||||
// ...else other possible pinch states...
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
</pre>
|
||||
|
||||
<p>The {@code ndk_helper} class also provides access to a vector-math library
|
||||
({@code vecmath.h}), using it here to transform touch coordinates.</p>
|
||||
|
||||
<pre class="no-pretty-print">
|
||||
void Engine::TransformPosition( ndk_helper::Vec2& vec )
|
||||
{
|
||||
vec = ndk_helper::Vec2( 2.0f, 2.0f ) * vec
|
||||
/ ndk_helper::Vec2( gl_context_->GetScreenWidth(),
|
||||
gl_context_->GetScreenHeight() ) - ndk_helper::Vec2( 1.f, 1.f );
|
||||
}
|
||||
</pre>
|
||||
</ul>
|
||||
|
||||
<p>The {@code HandleCmd()} method handles commands posted from the
|
||||
android_native_app_glue library. For more information about what the messages
|
||||
mean, refer to the comments in the {@code android_native_app_glue.h} and
|
||||
{@code .c} source files.</p>
|
||||
|
||||
<pre class="no-pretty-print">
|
||||
void Engine::HandleCmd( struct android_app* app,
|
||||
int32_t cmd )
|
||||
{
|
||||
Engine* eng = (Engine*) app->userData;
|
||||
switch( cmd )
|
||||
{
|
||||
case APP_CMD_SAVE_STATE:
|
||||
break;
|
||||
case APP_CMD_INIT_WINDOW:
|
||||
// The window is being shown, get it ready.
|
||||
if( app->window != NULL )
|
||||
{
|
||||
eng->InitDisplay();
|
||||
eng->DrawFrame();
|
||||
}
|
||||
break;
|
||||
case APP_CMD_TERM_WINDOW:
|
||||
// The window is being hidden or closed, clean it up.
|
||||
eng->TermDisplay();
|
||||
eng->has_focus_ = false;
|
||||
break;
|
||||
case APP_CMD_STOP:
|
||||
break;
|
||||
case APP_CMD_GAINED_FOCUS:
|
||||
eng->ResumeSensors();
|
||||
//Start animation
|
||||
eng->has_focus_ = true;
|
||||
break;
|
||||
case APP_CMD_LOST_FOCUS:
|
||||
eng->SuspendSensors();
|
||||
// Also stop animating.
|
||||
eng->has_focus_ = false;
|
||||
eng->DrawFrame();
|
||||
break;
|
||||
case APP_CMD_LOW_MEMORY:
|
||||
//Free up GL resources
|
||||
eng->TrimMemory();
|
||||
break;
|
||||
}
|
||||
}
|
||||
</pre>
|
||||
|
||||
<p>The {@code ndk_helper} class posts {@code APP_CMD_INIT_WINDOW} when {@code android_app_glue}
|
||||
receives an {@code onNativeWindowCreated()} callback from the system.
|
||||
Applications can normally perform window initializations, such as EGL
|
||||
initialization. They do this outside of the activity lifecycle, since the
|
||||
activity is not yet ready.</p>
|
||||
<pre class="fragment">ndk_helper::JNIHelper::Init( state->activity,
|
||||
HELPER_CLASS_NAME );
|
||||
|
||||
<pre class="no-pretty-print">
|
||||
//Init helper functions
|
||||
ndk_helper::JNIHelper::Init( state->activity, HELPER_CLASS_NAME );
|
||||
|
||||
state->userData = &g_engine;
|
||||
state->onAppCmd = Engine::HandleCmd;
|
||||
state->onInputEvent = Engine::HandleInput; </pre>
|
||||
state->userData = &g_engine;
|
||||
state->onAppCmd = Engine::HandleCmd;
|
||||
state->onInputEvent = Engine::HandleInput;
|
||||
</pre>
|
||||
Reference in New Issue
Block a user