http://blog.csdn.net/flash129/article/details/8506348
// android-ndk-r5b/samples/native-activity
一、native-activity/default.properties 文件内容:
# This file is automatically generated by Android Tools.
# 本文件是由 Android 工具自动产生的。
#
# Do not modify this file -- YOUR CHANGES WILL BE ERASED!
# 不要修改本文件 -- 你的改变将是被擦除掉!
#
# This file must be checked in Version Control Systems.
# 本文件一定由版本控制系统检查。
#
# To customize properties used by the Ant build system use,
# "build.properties",
# and override values to adapt the script to your project structure.
# Ant 构建系统使用自定义内容 build.properties ,然后替代其值为你的工程结构来改写脚本。
#
# Project target.
# 工程目标。
# 判断 APP_PLATFORM 是否在 Application.mk 文件中定义,如果没有定义,就使用这个默认的定义,
# 如果此文件也找不到,则按照 android-3 (Android 1.5)来定义。
target=android-7
二、native-activity/AndroidManifest.xml 文件内容:
<?xml version="1.0"
encoding="utf-8"?>
<!-- BEGIN_INCLUDE(manifest) -->
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.native_activity"
android:versionCode="1"
android:versionName="1.0">
<!-- This is the platform API where NativeActivity was introduced. -->
<!-- 本地活动将引入的平台 API -->
<uses-sdk android:minSdkVersion="8" />
<!-- This .apk has no Java code itself, so set hasCode to false. -->
<!-- 该 .apk 没有 Java 代码,所以设置 hasCode 属性值为假 -->
<application android:label="@string/app_name"
android:hasCode="false">
<!-- Our activity is the built-in NativeActivity framework class.
This will take care of integrating with our NDK code. -->
<!-- 我们的活动是内置本地活动框架类。
这将处理使我们的 NDK 代码成为一体。 -->
<activity android:name="android.app.NativeActivity"
android:label="@string/app_name"
android:configChanges="orientation|keyboardHidden">
<!-- Tell NativeActivity the name of or .so -->
<!-- 告知本地活动共享库的名字 -->
<meta-data android:name="android.app.lib_name"
android:value="native-activity" />
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>
<!-- END_INCLUDE(manifest) -->
三、native-activity/res/values/strings.xml 文件内容:
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="app_name">NativeActivity</string>
</resources>
四、native-activity/jni/Application.mk 文件内容:
# 使用 android-ndk-r5b/platforms/android-9/arch-arm/usr 目录下的头文件与库文件
APP_PLATFORM := android-9
五、native-activity/jni/Android.mk 文件内容:
# Copyright (C) 2010 The Android Open Source Project
# 版权所有(C)2010 Android 开源工程
#
# Licensed under the Apache License, Version 2.0 (the "License");
# 根据 2.0 版本 Apache 许可证授权
# you may not use this file except in compliance with the License.
# 根据本许可证,你可以不使用此文件。
# You may obtain a copy of the License at
# 你可以获得许可证的副本在
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# 除非因适用法律需要或书面同意,
# 根据许可证分发的软件是基于"按原样"原则提供,无任何明示的或暗示的保证或条件。
# See the License for the specific language governing permissions and
# limitations under the License.
# 详见根据许可证许可下,特定语言的管辖权限和限制。
# 用于返回当前目录的路径
LOCAL_PATH := $(call my-dir)
# CLEAR_VARS 变量是由生成系统已提供的,
# 并且指出一个特殊的 GNU Makefile 文件将为你清除除了 LOCAL_PATH 以外的许多的 LOCAL_XXX 变量,
# (例如:LOCAL_MODULE, LOCAL_SRC_FILES, LOCAL_STATIC_LIBRARIES,等等...)
# 这是必须的,因为全部的生成控制文件是在一个单独的 GNU Make 执行环境中被分析的,在那里所有的变量是全局的。
include $(CLEAR_VARS)
# LOCAL_MODULE 变量必须是已定义的,用来标识你的 Android.mk 文件中描述的每个模块。
# 模块名字必须是唯一,并且不能包含任何的空格。
# 注意生成系统将自动添加适当的前缀和后缀到相应的产生文件。
# 换句话说,一个共享库模块命名为 native-activity 将产生 libnative-activity.so 。
LOCAL_MODULE := native-activity
# LOCAL_SRC_FILES 变量必须包含将生成且汇编成一个模块的 C 和/或 C++ 源文件的列表。
# 注意你将不列出头文件和包含文件在这里,因为生成系统将自动地为你估算依赖;
# 列出的源文件将直接递给编译器。
LOCAL_SRC_FILES := main.c
# 使用在生成你的模块时的额外的链接器标志列表。
# 对于用 -l 前缀传递特定的系统库名是有用的。
# liblog.so 提供 Android 记录日志 API
# libandroid.so 提供 Android 功能访问 API
# libEGL.so 提供 EGL API
# libGLESv1_CM.so 提供 OpenGL ES API
# 注:
# OpenGL ES (OpenGL for Embedded Systems,以下简称 OpenGL)
# OpenGL 三维图形 API 的子集,针对手机、PDA和游戏主机等嵌入式设备而设计。
# 该 API 由 Khronos 集团定义推广,
# Khronos 是一个图形软硬件行业协会,该协会主要关注图形和多媒体方面的开放标准。
#
# EGL 是 OpenGL ES 和 底层本地平台视窗系统 之间的接口,为 OpenGL ES 提供平台独立性而设计。
# 它被用于处理图形管理、表面/缓冲捆绑、渲染同步,
# 以及支援使用其他 Khronos API 进行的高效、加速、混合模式 2D 和 3D 渲染。
LOCAL_LDLIBS := -llog -landroid -lEGL -lGLESv1_CM
# 将链接到本模块的静态库模块列表(用 BUILD_STATIC_LIBRARY 生成的)。
# 这仅在共享库模块中有意义。
LOCAL_STATIC_LIBRARIES := android_native_app_glue
# BUILD_SHARED_LIBRARY 是一个已由生成系统提供的变量,
# 表明一个 GNU Makefile 脚本是负责收集你定义的从最近的 include $(CLEAR_VARS)
# 到决定去生成之间的全部 LOCAL_XXX 变量的信息,然后正确地生成共享库。
# 注意你必须在包含这个文件之前最近位置有 LOCAL_MODULE 或 LOCAL_SRC_FILES 变量的定义。
include $(BUILD_SHARED_LIBRARY)
# $(call macro-name[, param1, ...])
# call 是一个内置于 make 的函数,
# call 会扩展它的第一个参数并把其余参数依次替换到出现 $1、$2、...的地方。
# call 的第一个参数可以是任何宏或变量的名称。
# 允许你通过名字查找且包含其它模块的 Android.mk 文件。
# 这将在你的 NDK_MODULE_PATH 环境变量提到的目录列表中查找模块标记的名字,
# 并自动地为你包含它的 Android.mk 文件。
# 为了方便起见,$NDK/sources 是被 NDK 生成系统附加到你的 NDK_MODULE_PATH 变量值定义中。
$(call import-module,android/native_app_glue)
六、native-activity/jni/main.c 文件内容:
/*
* Copyright (C) 2010 The Android Open Source Project
* 版权所有(C)2010 Android 开源工程
*
* Licensed under the Apache License, Version 2.0 (the "License");
* 根据 2.0 版本 Apache 许可证授权
* you may not use this file except in compliance with the License.
* 根据本许可证,你可以不使用此文件。
* You may obtain a copy of the License at
* 你可以获得许可证的副本在
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* 除非因适用法律需要或书面同意,
* 根据许可证分发的软件是基于"按原样"原则提供,无任何明示的或暗示的保证或条件。
* See the License for the specific language governing permissions and
* limitations under the License.
* 根据许可证分发的软件是基于"按原样"原则提供,无任何明示的或暗示的保证或条件。
*/
//BEGIN_INCLUDE(all)
/* Java Native Interface */
#include <jni.h>
/* 错误报告机制 */
#include <errno.h>
/* EGL */
#include <EGL/egl.h>
/* OpenGL ES 1.x */
#include <GLES/gl.h>
/* 接收和处理传感器事件 */
#include <android/sensor.h>
/* Android logging API */
#include <android/log.h>
/* android-ndk-r5b/sources/android/native_app_glue 静态库头文件 */
#include <android_native_app_glue.h>
#define LOGI(...) \
((void)__android_log_print( ANDROID_LOG_INFO, "native-activity", __VA_ARGS__ ))
#define LOGW(...) \
((void)__android_log_print( ANDROID_LOG_WARN, "native-activity", __VA_ARGS__ ))
/**
* Our saved state data.
* 我们已保存的状态数据。
*/
struct saved_state
{
float angle; /* RGB 中的绿色值 */
int32_t x; /* X 坐标 */
int32_t y; /* Y 坐标 */
};
/**
* Shared state for our app.
* 为我们的应用程序共享状态。
*/
struct engine
{
/* android_native_app_glue.h 中定义的本地应用程序粘合剂模块用数据结构 */
struct android_app* app;
/* sensor.h 中定义的传感器管理器 */
ASensorManager* sensorManager;
/* 加速度传感器 */
const ASensor* accelerometerSensor;
/* 已与一个循环器关联起来的传感器事件队列 */
ASensorEventQueue* sensorEventQueue;
/* 非零为可以绘制动画 */
int animating;
/* 显示器句柄 */
EGLDisplay display;
/* 系统窗口或 frame buffer 句柄 */
EGLSurface surface;
/* OpenGL ES 图形上下文 */
EGLContext context;
/* 系统窗口的宽度(像素) */
int32_t width;
/* 系统窗口的宽度(像素) */
int32_t height;
/* 我们已保存的状态数据 */
struct saved_state state;
};
/**
* Initialize an EGL context for the current display.
* 为当前显示器初始化一个 EGL 上下文。
*/
static int
engine_init_display( struct engine* engine )
{
/* initialize OpenGL ES and EGL
* 初始化 OpenGL ES 和 EGL
*/
/*
* Here specify the attributes of the desired configuration.
* 在这里具体指定想要的配置的属性。
*
* Below, we select an EGLConfig with at least 8 bits per color
* component compatible with on-screen windows.
* 在下面,我们选择一个至少 8 位色的 EGLConfig 与屏幕上的窗口一致。
* 注:通常以 ID, Value 依次存放,对于个别标识性的属性可以只有 ID 没有 Value 。
*/
const EGLint
attribs[] = { EGL_SURFACE_TYPE, EGL_WINDOW_BIT, /* 系统窗口类型 */
EGL_BLUE_SIZE, 8, /* 蓝色位数 */
EGL_GREEN_SIZE, 8, /* 绿色位数 */
EGL_RED_SIZE, 8, /* 红色位数 */
EGL_NONE };
/* 系统窗口的宽度(像素) */
EGLint w;
/* 系统窗口的高度(像素) */
EGLint h;
/* 未使用的变量 */
EGLint dummy;
/* 像素格式ID - RGBA/RGBX/RGB565 */
EGLint format;
/* 系统中 Surface 的 EGL 配置 的总个数 */
EGLint numConfigs;
/* Surface 的 EGL 配置 */
EGLConfig config;
/* 系统窗口句柄 */
EGLSurface surface;
/* OpenGL ES 图形上下文 */
EGLContext context;
/* 1.返回一个显示器连接 - 是一个关联系统物理屏幕的通用数据类型。 */
EGLDisplay display = eglGetDisplay( EGL_DEFAULT_DISPLAY ); /* 得到系统默认的 */
/* 原型:EGLDisplay eglGetDisplay ( NativeDisplayType display );
* display 参数是本地系统显示器类型,取值为本地显示器 ID 值。
* 返回:如果系统中没有一个可用的本地显示器 ID 值与 display 参数匹配,
* 函数将返回 EGL_NO_DISPLAY ,而没有任何 Error 状态被设置。
*/
/* 2. EGL 在使用前需要初始化,因此每个显示器句柄(EGLDisplay)在使用前都需要初始化。*/
eglInitialize( display, /* 有效的显示器句柄 */
0, /* 返回主版本号 - 不关心可设为 NULL 值或零(0) */
0 ); /* 返回次版本号 - 不关心可设为 NULL 值或零(0) */
/* 原型:EGLBoolean eglInitialize( EGLDisplay dpy,
* EGLint* major,
* EGLint* minor );
* EGLint 为 int 数据类型。
* 返回:EGLBOOlean 取值:EGL_TRUE = 1, EGL_FALSE = 0 。
*/
/* Here, the application chooses the configuration it desires.
* 在这里,应用程序决定它要求的配置。
*
* In this sample, we have a very simplified selection process,
* where we pick the first EGLConfig that matches our criteria.
* 在这个示例中,我们有一个非常精简的选择处理,
* 我们选择第一个 EGLConfig 适应我们的标准。
*/
/* 定义一个希望从系统获得的配置,它将返回一个最接近你的需求的配置 */
eglChooseConfig( display, /* 有效的显示器句柄 */
attribs, /* 以 EGL_NONE 结束的参数数组 */
&config, /* Surface 的 EGL 配置 */
1, /* Surface 的 EGL 配置个数 */
&numConfigs ); /* 系统中 Surface 的 EGL 配置 的总个数 */
/* 原型:EGLboolean eglChooseConfig( EGLDisplay dpy,
* const EGLint* attr_list,
* EGLConfig* config,
* EGLint config_size,
* EGLint* num_config );
*/
/* EGL_NATIVE_VISUAL_ID is an attribute of the EGLConfig
* that is guaranteed to be accepted by ANativeWindow_setBuffersGeometry().
* EGL_NATIVE_VISUAL_ID 是一个 EGLConfig 的属性,
* 保证被 ANativeWindow_setBuffersGeometry 函数认可。
*
* As soon as we picked a EGLConfig,
* we can safely reconfigure the ANativeWindow buffers to match,
* using EGL_NATIVE_VISUAL_ID.
* 我们已经挑选了一个 EGLConfig ,
* 使用 EGL_NATIVE_VISUAL_ID 我们可以安全地重新配置相应的 ANativeWindow 缓冲区了。
*/
/* 查询 EGLConfig 的指定属性值 */
eglGetConfigAttrib( display, /* 有效的显示器句柄 */
config, /* 有效的 Surface 的 EGL 配置 */
EGL_NATIVE_VISUAL_ID, /* 与操作系统通讯的可视 ID 句柄 */
&format );
/* 原型:EGLBoolean eglGetConfigAttrib( EGLDisplay display,
* EGLConfig config,
* EGLint attribute,
* EGLint* value );
*/
/* 改变窗口表面像素格式和大小
* 参见:native_window.h */
ANativeWindow_setBuffersGeometry( engine->app->window, /* ANativeWindow */
0, /* 宽度(像素) */
0, /* 高度(像素) */
format ); /* 像素格式 */
/* 创建一个可实际显示的系统窗口句柄,实际上就是一个 FrameBuffer */
surface = eglCreateWindowSurface( display, /* 有效的显示器句柄 */
config, /* 有效的 Surface 的 EGL 配置 */
engine->app->window, /* ANativeWindow */
NULL ); /* 属性列表可以是空,使用默认值 */
/* 原型:EGLSurface eglCreateWindowSurface( EGLDisplay display,
* EGLConfig config,
* EGLNatvieWindowType window,
* const EGLint* attribList );
*/
/* 创建一个 OpenGL ES 图形上下文 */
context = eglCreateContext( display, /* 有效的显示器句柄 */
config, /* 有效的 Surface 的 EGL 配置 */
NULL, /* 不和其他 EGLContext 分享资源 */
NULL ); /* 属性列表可以是空,使用默认值 */
/* 原型:EGLContext eglCreateContext( EGLDisplay display,
* EGLConfig config,
* EGLContext shareContext,
* const EGLint* attribList );
*/
/* 激活 OpenGL ES 图形上下文
* 在 OpenGL ES ,一次只能有一个 context 生效。
*/
if ( EGL_FALSE == eglMakeCurrent( display, /* 有效的显示器句柄 */
surface, /* 上面创建的系统窗口 */
surface, /* 上面创建的系统窗口 */
context ) ) /* OpenGL ES 图形上下文 */
{
LOGW( "Unable to eglMakeCurrent" );
return -1;
}
/* 原型:EGLBoolean eglMakeCurrent( EGLDisplay display,
* EGLSurface draw,
* EGLSurface read,
* EGLContext context );
*/
eglQuerySurface( display, /* 有效的显示器句柄 */
surface, /* 上面创建的系统窗口 */
EGL_WIDTH, /* 返回系统窗口的宽度(像素) */
&w );
/* 原型:EGLBoolean eglQuerySurface( EGLDisplay display,
* EGLSurface surface,
* EGLint attribute,
* EGLint* value );
*/
eglQuerySurface( display,
surface,
EGL_HEIGHT, /* 返回系统窗口的高度(像素) */
&h );
engine->display = display;
engine->context = context;
engine->surface = surface;
engine->width = w;
engine->height = h;
engine->state.angle = 0;
/* Initialize GL state.
* 初始化 GL 状态。
*/
glHint( GL_PERSPECTIVE_CORRECTION_HINT, /* 指定颜色和纹理坐标的插值质量 */
GL_FASTEST ); /* 使用速度最快的模式 */
/* 开启服务端 GL 功能 */
glEnable( GL_CULL_FACE ); /* 开启多边形表面剔除功能 */
/* 原型:void glEnable( GLenum cap ); */
/* 启用光滑着色 */
glShadeModel( GL_SMOOTH );
/* 禁用服务端 GL 功能 */
glDisable( GL_DEPTH_TEST ); /* 禁用深度测试 */
return 0;
}
/**
* Just the current frame in the display.
* 只是当前帧在显示。
*/
static void
engine_draw_frame( struct engine* engine )
{
/* 显示器句柄为空 */
if ( engine->display == NULL )
{
/* No display.
* 不显示。
*/
return;
}
/* Just fill the screen with a color.
* 只是用一种颜色添充屏幕。
*/
glClearColor( ( (float)engine->state.x ) / engine->width,
engine->state.angle,
( (float)engine->state.y ) / engine->height,
1 );
/* 原型:void glClearColor( GLclampf red,
* GLclampf green,
* GLclampf blue,
* GLclampf alpha );
*/
/* 要清除颜色缓冲 */
glClear( GL_COLOR_BUFFER_BIT );
/* 原型:void glClear( GLbitfield mask ); */
/* 发送 EGL 系统窗口绘图缓冲区到本地窗口 */
eglSwapBuffers( engine->display,
engine->surface );
/* 原型:EGLBoolean eglSwapBuffers( EGLDisplay display,
* EGLSurface surface );
*/
}
/**
* Tear down the EGL context currently associated with the display.
* 拆掉与显示器关联的当前 EGL 上下文。
*/
static void
engine_term_display( struct engine* engine )
{
if ( engine->display != EGL_NO_DISPLAY )
{
/* 解绑 OpenGL ES 图形上下文 与 可实际显示的系统窗口句柄 */
eglMakeCurrent( engine->display,
EGL_NO_SURFACE,
EGL_NO_SURFACE,
EGL_NO_CONTEXT );
if ( engine->context != EGL_NO_CONTEXT )
{
/* 销毁 OpenGL ES 图形上下文 */
eglDestroyContext( engine->display,
engine->context );
}
if ( engine->surface != EGL_NO_SURFACE )
{
/* 销毁 可实际显示的系统窗口句柄 */
eglDestroySurface( engine->display,
engine->surface );
}
/* 终止一个显示器的连接 */
eglTerminate( engine->display );
}
/* 禁止动画绘制 */
engine->animating = 0;
/* 以防上面的判断出错,修改记录 */
engine->display = EGL_NO_DISPLAY;
engine->context = EGL_NO_CONTEXT;
engine->surface = EGL_NO_SURFACE;
}
/**
* Process the next input event.
* 处理下一个输入事件。
*/
static int32_t
engine_handle_input( struct android_app* app,
AInputEvent* event )
{
struct engine* engine = (struct engine*)app->userData;
/* 得到的输入事件类型为触摸屏事件 */
if ( AInputEvent_getType( event ) == AINPUT_EVENT_TYPE_MOTION )
{
/* 启用动画绘制 */
engine->animating = 1;
/* 得到第一个触摸点的当前 x 坐标 */
engine->state.x = AMotionEvent_getX( event, 0 );
/* 得到第一个触摸点的当前 y 坐标 */
engine->state.y = AMotionEvent_getY( event, 0 );
return 1;
}
return 0;
}
/**
* Process the next main command.
* 处理下一个主线程命令消息。
*/
static void
engine_handle_cmd( struct android_app* app,
int32_t cmd )
{
struct engine* engine = (struct engine*)app->userData;
switch ( cmd )
{
case APP_CMD_SAVE_STATE:
/* The system has asked us to save our current state.
* 系统告诉我们去保存我们的当前状态。
*
* Do so.
* 如下所做。
*/
engine->app->savedState = malloc( sizeof( struct saved_state ) );
*( (struct saved_state*)engine->app->savedState ) = engine->state;
engine->app->savedStateSize = sizeof( struct saved_state );
break;
case APP_CMD_INIT_WINDOW:
/* The window is being shown, get it ready.
* 窗口是正在显示,准备得到它。
*/
if ( engine->app->window != NULL )
{
engine_init_display( engine );
engine_draw_frame( engine );
}
break;
case APP_CMD_TERM_WINDOW:
/* The window is being hidden or closed, clean it up.
* 窗口是正在隐藏或关闭,清理它。
*/
engine_term_display( engine );
break;
case APP_CMD_GAINED_FOCUS:
/* When our app gains focus, we start monitoring the accelerometer.
* 当我们的应用程序得到焦点时,我们开始监测加速器。
*/
if ( engine->accelerometerSensor != NULL )
{
/* 启用指定的传感器 */
ASensorEventQueue_enableSensor( engine->sensorEventQueue,
engine->accelerometerSensor );
/* We'd like to get 60 events per second (in us).
* 我们将喜欢每秒得到六十个事件。
*/
ASensorEventQueue_setEventRate( engine->sensorEventQueue,
engine->accelerometerSensor,
( 1000L / 60 ) * 1000 );
}
break;
case APP_CMD_LOST_FOCUS:
/* When our app loses focus, we stop monitoring the accelerometer.
* 当我们的应用程序丢失焦点时,我们停止监测加速器。
*
* This is to avoid consuming battery while not being used.
* 在不使用时期避免消耗电池。
*/
if ( engine->accelerometerSensor != NULL )
{
/* 禁用指定的传感器 */
ASensorEventQueue_disableSensor( engine->sensorEventQueue,
engine->accelerometerSensor );
}
/* Also stop animating.
* 同样禁止动画绘制。
*/
engine->animating = 0;
engine_draw_frame( engine );
break;
}
}
/**
* This is the main entry point of a native application
* that is using android_native_app_glue.
* 这是使用了 android_native_app_glue 粘合剂模块的一个本地应用程序的主入口点。
*
* It runs in its own thread,
* with its own event loop for receiving input events and doing other things.
* 它运行在它自己的线程中,用它自己的事件循环来接收输入事件和做其它事情。
*/
void
android_main( struct android_app* state )
{
struct engine engine;
/* Make sure glue isn't stripped.
* 务必粘合剂模块未剥离。
* 注:在 android_native_app_glue.c 文件中这是一个空函数。
*/
app_dummy();
memset( &engine, 0, sizeof( engine ) );
/* 在应用程序各部分之间共享的数据 */
state->userData = &engine;
/* 指向一个处理最重要的应用程序命令的函数 */
state->onAppCmd = engine_handle_cmd;
/* 指向一个处理输入事件的函数 */
state->onInputEvent = engine_handle_input;
engine.app = state;
/* Prepare to monitor accelerometer.
* 准备监测加速器。
*/
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 );
if ( state->savedState != NULL )
{
/* We are starting with a previous saved state;
* 我们使用一个之前保存状态启动。
*
* restore from it.
* 用它来恢复。
*/
engine.state = *(struct saved_state*)state->savedState;
}
/* loop waiting for stuff to do.
* 循环等待材料来做。
*/
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.
* 如果动画绘制中,我们循环直到全部事件是读取,然后继续去画下一个动画的帧。
*/
/* 零(0) - 立即返回,负壹(-1) - 无限等待 */
while ( ( ident = ALooper_pollAll( engine.animating ? 0 : -1,
NULL, /* 不返回发生事件的文件描述符 */
/* 在 android_native_app_glue.c 文件的 android_app_entry 函数体中
* 调用 ALooper_addFd 函数时设置为 ALOOPER_EVENT_INPUT - 文件描述符读操作有效 */
&events,
(void**)&source ) ) >= 0 )
{
/* Process this event.
* 处理这个事件。
*/
if ( source != NULL )
{
/* 处理应用程序主线程的命令
* 在 android_native_app_glue.c 文件的 android_app_entry 函数体中
* 设置指向 process_cmd 或 process_input 函数。
*/
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;
}
}
if ( engine.animating )
{
/* Done with events; draw next animation frame.
* 事件完成;画下一个动画帧。
*/
engine.state.angle += .01f; /* 0.1 递增变化 */
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 );
}
}
}
//END_INCLUDE(all)
一、native-activity/default.properties 文件内容:
# This file is automatically generated by Android Tools.
# 本文件是由 Android 工具自动产生的。
#
# Do not modify this file -- YOUR CHANGES WILL BE ERASED!
# 不要修改本文件 -- 你的改变将是被擦除掉!
#
# This file must be checked in Version Control Systems.
# 本文件一定由版本控制系统检查。
#
# To customize properties used by the Ant build system use,
# "build.properties",
# and override values to adapt the script to your project structure.
# Ant 构建系统使用自定义内容 build.properties ,然后替代其值为你的工程结构来改写脚本。
#
# Project target.
# 工程目标。
# 判断 APP_PLATFORM 是否在 Application.mk 文件中定义,如果没有定义,就使用这个默认的定义,
# 如果此文件也找不到,则按照 android-3 (Android 1.5)来定义。
target=android-7
二、native-activity/AndroidManifest.xml 文件内容:
<?xml version="1.0"
encoding="utf-8"?>
<!-- BEGIN_INCLUDE(manifest) -->
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.native_activity"
android:versionCode="1"
android:versionName="1.0">
<!-- This is the platform API where NativeActivity was introduced. -->
<!-- 本地活动将引入的平台 API -->
<uses-sdk android:minSdkVersion="8" />
<!-- This .apk has no Java code itself, so set hasCode to false. -->
<!-- 该 .apk 没有 Java 代码,所以设置 hasCode 属性值为假 -->
<application android:label="@string/app_name"
android:hasCode="false">
<!-- Our activity is the built-in NativeActivity framework class.
This will take care of integrating with our NDK code. -->
<!-- 我们的活动是内置本地活动框架类。
这将处理使我们的 NDK 代码成为一体。 -->
<activity android:name="android.app.NativeActivity"
android:label="@string/app_name"
android:configChanges="orientation|keyboardHidden">
<!-- Tell NativeActivity the name of or .so -->
<!-- 告知本地活动共享库的名字 -->
<meta-data android:name="android.app.lib_name"
android:value="native-activity" />
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>
<!-- END_INCLUDE(manifest) -->
三、native-activity/res/values/strings.xml 文件内容:
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="app_name">NativeActivity</string>
</resources>
四、native-activity/jni/Application.mk 文件内容:
# 使用 android-ndk-r5b/platforms/android-9/arch-arm/usr 目录下的头文件与库文件
APP_PLATFORM := android-9
五、native-activity/jni/Android.mk 文件内容:
# Copyright (C) 2010 The Android Open Source Project
# 版权所有(C)2010 Android 开源工程
#
# Licensed under the Apache License, Version 2.0 (the "License");
# 根据 2.0 版本 Apache 许可证授权
# you may not use this file except in compliance with the License.
# 根据本许可证,你可以不使用此文件。
# You may obtain a copy of the License at
# 你可以获得许可证的副本在
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# 除非因适用法律需要或书面同意,
# 根据许可证分发的软件是基于"按原样"原则提供,无任何明示的或暗示的保证或条件。
# See the License for the specific language governing permissions and
# limitations under the License.
# 详见根据许可证许可下,特定语言的管辖权限和限制。
# 用于返回当前目录的路径
LOCAL_PATH := $(call my-dir)
# CLEAR_VARS 变量是由生成系统已提供的,
# 并且指出一个特殊的 GNU Makefile 文件将为你清除除了 LOCAL_PATH 以外的许多的 LOCAL_XXX 变量,
# (例如:LOCAL_MODULE, LOCAL_SRC_FILES, LOCAL_STATIC_LIBRARIES,等等...)
# 这是必须的,因为全部的生成控制文件是在一个单独的 GNU Make 执行环境中被分析的,在那里所有的变量是全局的。
include $(CLEAR_VARS)
# LOCAL_MODULE 变量必须是已定义的,用来标识你的 Android.mk 文件中描述的每个模块。
# 模块名字必须是唯一,并且不能包含任何的空格。
# 注意生成系统将自动添加适当的前缀和后缀到相应的产生文件。
# 换句话说,一个共享库模块命名为 native-activity 将产生 libnative-activity.so 。
LOCAL_MODULE := native-activity
# LOCAL_SRC_FILES 变量必须包含将生成且汇编成一个模块的 C 和/或 C++ 源文件的列表。
# 注意你将不列出头文件和包含文件在这里,因为生成系统将自动地为你估算依赖;
# 列出的源文件将直接递给编译器。
LOCAL_SRC_FILES := main.c
# 使用在生成你的模块时的额外的链接器标志列表。
# 对于用 -l 前缀传递特定的系统库名是有用的。
# liblog.so 提供 Android 记录日志 API
# libandroid.so 提供 Android 功能访问 API
# libEGL.so 提供 EGL API
# libGLESv1_CM.so 提供 OpenGL ES API
# 注:
# OpenGL ES (OpenGL for Embedded Systems,以下简称 OpenGL)
# OpenGL 三维图形 API 的子集,针对手机、PDA和游戏主机等嵌入式设备而设计。
# 该 API 由 Khronos 集团定义推广,
# Khronos 是一个图形软硬件行业协会,该协会主要关注图形和多媒体方面的开放标准。
#
# EGL 是 OpenGL ES 和 底层本地平台视窗系统 之间的接口,为 OpenGL ES 提供平台独立性而设计。
# 它被用于处理图形管理、表面/缓冲捆绑、渲染同步,
# 以及支援使用其他 Khronos API 进行的高效、加速、混合模式 2D 和 3D 渲染。
LOCAL_LDLIBS := -llog -landroid -lEGL -lGLESv1_CM
# 将链接到本模块的静态库模块列表(用 BUILD_STATIC_LIBRARY 生成的)。
# 这仅在共享库模块中有意义。
LOCAL_STATIC_LIBRARIES := android_native_app_glue
# BUILD_SHARED_LIBRARY 是一个已由生成系统提供的变量,
# 表明一个 GNU Makefile 脚本是负责收集你定义的从最近的 include $(CLEAR_VARS)
# 到决定去生成之间的全部 LOCAL_XXX 变量的信息,然后正确地生成共享库。
# 注意你必须在包含这个文件之前最近位置有 LOCAL_MODULE 或 LOCAL_SRC_FILES 变量的定义。
include $(BUILD_SHARED_LIBRARY)
# $(call macro-name[, param1, ...])
# call 是一个内置于 make 的函数,
# call 会扩展它的第一个参数并把其余参数依次替换到出现 $1、$2、...的地方。
# call 的第一个参数可以是任何宏或变量的名称。
# 允许你通过名字查找且包含其它模块的 Android.mk 文件。
# 这将在你的 NDK_MODULE_PATH 环境变量提到的目录列表中查找模块标记的名字,
# 并自动地为你包含它的 Android.mk 文件。
# 为了方便起见,$NDK/sources 是被 NDK 生成系统附加到你的 NDK_MODULE_PATH 变量值定义中。
$(call import-module,android/native_app_glue)
六、native-activity/jni/main.c 文件内容:
/*
* Copyright (C) 2010 The Android Open Source Project
* 版权所有(C)2010 Android 开源工程
*
* Licensed under the Apache License, Version 2.0 (the "License");
* 根据 2.0 版本 Apache 许可证授权
* you may not use this file except in compliance with the License.
* 根据本许可证,你可以不使用此文件。
* You may obtain a copy of the License at
* 你可以获得许可证的副本在
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* 除非因适用法律需要或书面同意,
* 根据许可证分发的软件是基于"按原样"原则提供,无任何明示的或暗示的保证或条件。
* See the License for the specific language governing permissions and
* limitations under the License.
* 根据许可证分发的软件是基于"按原样"原则提供,无任何明示的或暗示的保证或条件。
*/
//BEGIN_INCLUDE(all)
/* Java Native Interface */
#include <jni.h>
/* 错误报告机制 */
#include <errno.h>
/* EGL */
#include <EGL/egl.h>
/* OpenGL ES 1.x */
#include <GLES/gl.h>
/* 接收和处理传感器事件 */
#include <android/sensor.h>
/* Android logging API */
#include <android/log.h>
/* android-ndk-r5b/sources/android/native_app_glue 静态库头文件 */
#include <android_native_app_glue.h>
#define LOGI(...) \
((void)__android_log_print( ANDROID_LOG_INFO, "native-activity", __VA_ARGS__ ))
#define LOGW(...) \
((void)__android_log_print( ANDROID_LOG_WARN, "native-activity", __VA_ARGS__ ))
/**
* Our saved state data.
* 我们已保存的状态数据。
*/
struct saved_state
{
float angle; /* RGB 中的绿色值 */
int32_t x; /* X 坐标 */
int32_t y; /* Y 坐标 */
};
/**
* Shared state for our app.
* 为我们的应用程序共享状态。
*/
struct engine
{
/* android_native_app_glue.h 中定义的本地应用程序粘合剂模块用数据结构 */
struct android_app* app;
/* sensor.h 中定义的传感器管理器 */
ASensorManager* sensorManager;
/* 加速度传感器 */
const ASensor* accelerometerSensor;
/* 已与一个循环器关联起来的传感器事件队列 */
ASensorEventQueue* sensorEventQueue;
/* 非零为可以绘制动画 */
int animating;
/* 显示器句柄 */
EGLDisplay display;
/* 系统窗口或 frame buffer 句柄 */
EGLSurface surface;
/* OpenGL ES 图形上下文 */
EGLContext context;
/* 系统窗口的宽度(像素) */
int32_t width;
/* 系统窗口的宽度(像素) */
int32_t height;
/* 我们已保存的状态数据 */
struct saved_state state;
};
/**
* Initialize an EGL context for the current display.
* 为当前显示器初始化一个 EGL 上下文。
*/
static int
engine_init_display( struct engine* engine )
{
/* initialize OpenGL ES and EGL
* 初始化 OpenGL ES 和 EGL
*/
/*
* Here specify the attributes of the desired configuration.
* 在这里具体指定想要的配置的属性。
*
* Below, we select an EGLConfig with at least 8 bits per color
* component compatible with on-screen windows.
* 在下面,我们选择一个至少 8 位色的 EGLConfig 与屏幕上的窗口一致。
* 注:通常以 ID, Value 依次存放,对于个别标识性的属性可以只有 ID 没有 Value 。
*/
const EGLint
attribs[] = { EGL_SURFACE_TYPE, EGL_WINDOW_BIT, /* 系统窗口类型 */
EGL_BLUE_SIZE, 8, /* 蓝色位数 */
EGL_GREEN_SIZE, 8, /* 绿色位数 */
EGL_RED_SIZE, 8, /* 红色位数 */
EGL_NONE };
/* 系统窗口的宽度(像素) */
EGLint w;
/* 系统窗口的高度(像素) */
EGLint h;
/* 未使用的变量 */
EGLint dummy;
/* 像素格式ID - RGBA/RGBX/RGB565 */
EGLint format;
/* 系统中 Surface 的 EGL 配置 的总个数 */
EGLint numConfigs;
/* Surface 的 EGL 配置 */
EGLConfig config;
/* 系统窗口句柄 */
EGLSurface surface;
/* OpenGL ES 图形上下文 */
EGLContext context;
/* 1.返回一个显示器连接 - 是一个关联系统物理屏幕的通用数据类型。 */
EGLDisplay display = eglGetDisplay( EGL_DEFAULT_DISPLAY ); /* 得到系统默认的 */
/* 原型:EGLDisplay eglGetDisplay ( NativeDisplayType display );
* display 参数是本地系统显示器类型,取值为本地显示器 ID 值。
* 返回:如果系统中没有一个可用的本地显示器 ID 值与 display 参数匹配,
* 函数将返回 EGL_NO_DISPLAY ,而没有任何 Error 状态被设置。
*/
/* 2. EGL 在使用前需要初始化,因此每个显示器句柄(EGLDisplay)在使用前都需要初始化。*/
eglInitialize( display, /* 有效的显示器句柄 */
0, /* 返回主版本号 - 不关心可设为 NULL 值或零(0) */
0 ); /* 返回次版本号 - 不关心可设为 NULL 值或零(0) */
/* 原型:EGLBoolean eglInitialize( EGLDisplay dpy,
* EGLint* major,
* EGLint* minor );
* EGLint 为 int 数据类型。
* 返回:EGLBOOlean 取值:EGL_TRUE = 1, EGL_FALSE = 0 。
*/
/* Here, the application chooses the configuration it desires.
* 在这里,应用程序决定它要求的配置。
*
* In this sample, we have a very simplified selection process,
* where we pick the first EGLConfig that matches our criteria.
* 在这个示例中,我们有一个非常精简的选择处理,
* 我们选择第一个 EGLConfig 适应我们的标准。
*/
/* 定义一个希望从系统获得的配置,它将返回一个最接近你的需求的配置 */
eglChooseConfig( display, /* 有效的显示器句柄 */
attribs, /* 以 EGL_NONE 结束的参数数组 */
&config, /* Surface 的 EGL 配置 */
1, /* Surface 的 EGL 配置个数 */
&numConfigs ); /* 系统中 Surface 的 EGL 配置 的总个数 */
/* 原型:EGLboolean eglChooseConfig( EGLDisplay dpy,
* const EGLint* attr_list,
* EGLConfig* config,
* EGLint config_size,
* EGLint* num_config );
*/
/* EGL_NATIVE_VISUAL_ID is an attribute of the EGLConfig
* that is guaranteed to be accepted by ANativeWindow_setBuffersGeometry().
* EGL_NATIVE_VISUAL_ID 是一个 EGLConfig 的属性,
* 保证被 ANativeWindow_setBuffersGeometry 函数认可。
*
* As soon as we picked a EGLConfig,
* we can safely reconfigure the ANativeWindow buffers to match,
* using EGL_NATIVE_VISUAL_ID.
* 我们已经挑选了一个 EGLConfig ,
* 使用 EGL_NATIVE_VISUAL_ID 我们可以安全地重新配置相应的 ANativeWindow 缓冲区了。
*/
/* 查询 EGLConfig 的指定属性值 */
eglGetConfigAttrib( display, /* 有效的显示器句柄 */
config, /* 有效的 Surface 的 EGL 配置 */
EGL_NATIVE_VISUAL_ID, /* 与操作系统通讯的可视 ID 句柄 */
&format );
/* 原型:EGLBoolean eglGetConfigAttrib( EGLDisplay display,
* EGLConfig config,
* EGLint attribute,
* EGLint* value );
*/
/* 改变窗口表面像素格式和大小
* 参见:native_window.h */
ANativeWindow_setBuffersGeometry( engine->app->window, /* ANativeWindow */
0, /* 宽度(像素) */
0, /* 高度(像素) */
format ); /* 像素格式 */
/* 创建一个可实际显示的系统窗口句柄,实际上就是一个 FrameBuffer */
surface = eglCreateWindowSurface( display, /* 有效的显示器句柄 */
config, /* 有效的 Surface 的 EGL 配置 */
engine->app->window, /* ANativeWindow */
NULL ); /* 属性列表可以是空,使用默认值 */
/* 原型:EGLSurface eglCreateWindowSurface( EGLDisplay display,
* EGLConfig config,
* EGLNatvieWindowType window,
* const EGLint* attribList );
*/
/* 创建一个 OpenGL ES 图形上下文 */
context = eglCreateContext( display, /* 有效的显示器句柄 */
config, /* 有效的 Surface 的 EGL 配置 */
NULL, /* 不和其他 EGLContext 分享资源 */
NULL ); /* 属性列表可以是空,使用默认值 */
/* 原型:EGLContext eglCreateContext( EGLDisplay display,
* EGLConfig config,
* EGLContext shareContext,
* const EGLint* attribList );
*/
/* 激活 OpenGL ES 图形上下文
* 在 OpenGL ES ,一次只能有一个 context 生效。
*/
if ( EGL_FALSE == eglMakeCurrent( display, /* 有效的显示器句柄 */
surface, /* 上面创建的系统窗口 */
surface, /* 上面创建的系统窗口 */
context ) ) /* OpenGL ES 图形上下文 */
{
LOGW( "Unable to eglMakeCurrent" );
return -1;
}
/* 原型:EGLBoolean eglMakeCurrent( EGLDisplay display,
* EGLSurface draw,
* EGLSurface read,
* EGLContext context );
*/
eglQuerySurface( display, /* 有效的显示器句柄 */
surface, /* 上面创建的系统窗口 */
EGL_WIDTH, /* 返回系统窗口的宽度(像素) */
&w );
/* 原型:EGLBoolean eglQuerySurface( EGLDisplay display,
* EGLSurface surface,
* EGLint attribute,
* EGLint* value );
*/
eglQuerySurface( display,
surface,
EGL_HEIGHT, /* 返回系统窗口的高度(像素) */
&h );
engine->display = display;
engine->context = context;
engine->surface = surface;
engine->width = w;
engine->height = h;
engine->state.angle = 0;
/* Initialize GL state.
* 初始化 GL 状态。
*/
glHint( GL_PERSPECTIVE_CORRECTION_HINT, /* 指定颜色和纹理坐标的插值质量 */
GL_FASTEST ); /* 使用速度最快的模式 */
/* 开启服务端 GL 功能 */
glEnable( GL_CULL_FACE ); /* 开启多边形表面剔除功能 */
/* 原型:void glEnable( GLenum cap ); */
/* 启用光滑着色 */
glShadeModel( GL_SMOOTH );
/* 禁用服务端 GL 功能 */
glDisable( GL_DEPTH_TEST ); /* 禁用深度测试 */
return 0;
}
/**
* Just the current frame in the display.
* 只是当前帧在显示。
*/
static void
engine_draw_frame( struct engine* engine )
{
/* 显示器句柄为空 */
if ( engine->display == NULL )
{
/* No display.
* 不显示。
*/
return;
}
/* Just fill the screen with a color.
* 只是用一种颜色添充屏幕。
*/
glClearColor( ( (float)engine->state.x ) / engine->width,
engine->state.angle,
( (float)engine->state.y ) / engine->height,
1 );
/* 原型:void glClearColor( GLclampf red,
* GLclampf green,
* GLclampf blue,
* GLclampf alpha );
*/
/* 要清除颜色缓冲 */
glClear( GL_COLOR_BUFFER_BIT );
/* 原型:void glClear( GLbitfield mask ); */
/* 发送 EGL 系统窗口绘图缓冲区到本地窗口 */
eglSwapBuffers( engine->display,
engine->surface );
/* 原型:EGLBoolean eglSwapBuffers( EGLDisplay display,
* EGLSurface surface );
*/
}
/**
* Tear down the EGL context currently associated with the display.
* 拆掉与显示器关联的当前 EGL 上下文。
*/
static void
engine_term_display( struct engine* engine )
{
if ( engine->display != EGL_NO_DISPLAY )
{
/* 解绑 OpenGL ES 图形上下文 与 可实际显示的系统窗口句柄 */
eglMakeCurrent( engine->display,
EGL_NO_SURFACE,
EGL_NO_SURFACE,
EGL_NO_CONTEXT );
if ( engine->context != EGL_NO_CONTEXT )
{
/* 销毁 OpenGL ES 图形上下文 */
eglDestroyContext( engine->display,
engine->context );
}
if ( engine->surface != EGL_NO_SURFACE )
{
/* 销毁 可实际显示的系统窗口句柄 */
eglDestroySurface( engine->display,
engine->surface );
}
/* 终止一个显示器的连接 */
eglTerminate( engine->display );
}
/* 禁止动画绘制 */
engine->animating = 0;
/* 以防上面的判断出错,修改记录 */
engine->display = EGL_NO_DISPLAY;
engine->context = EGL_NO_CONTEXT;
engine->surface = EGL_NO_SURFACE;
}
/**
* Process the next input event.
* 处理下一个输入事件。
*/
static int32_t
engine_handle_input( struct android_app* app,
AInputEvent* event )
{
struct engine* engine = (struct engine*)app->userData;
/* 得到的输入事件类型为触摸屏事件 */
if ( AInputEvent_getType( event ) == AINPUT_EVENT_TYPE_MOTION )
{
/* 启用动画绘制 */
engine->animating = 1;
/* 得到第一个触摸点的当前 x 坐标 */
engine->state.x = AMotionEvent_getX( event, 0 );
/* 得到第一个触摸点的当前 y 坐标 */
engine->state.y = AMotionEvent_getY( event, 0 );
return 1;
}
return 0;
}
/**
* Process the next main command.
* 处理下一个主线程命令消息。
*/
static void
engine_handle_cmd( struct android_app* app,
int32_t cmd )
{
struct engine* engine = (struct engine*)app->userData;
switch ( cmd )
{
case APP_CMD_SAVE_STATE:
/* The system has asked us to save our current state.
* 系统告诉我们去保存我们的当前状态。
*
* Do so.
* 如下所做。
*/
engine->app->savedState = malloc( sizeof( struct saved_state ) );
*( (struct saved_state*)engine->app->savedState ) = engine->state;
engine->app->savedStateSize = sizeof( struct saved_state );
break;
case APP_CMD_INIT_WINDOW:
/* The window is being shown, get it ready.
* 窗口是正在显示,准备得到它。
*/
if ( engine->app->window != NULL )
{
engine_init_display( engine );
engine_draw_frame( engine );
}
break;
case APP_CMD_TERM_WINDOW:
/* The window is being hidden or closed, clean it up.
* 窗口是正在隐藏或关闭,清理它。
*/
engine_term_display( engine );
break;
case APP_CMD_GAINED_FOCUS:
/* When our app gains focus, we start monitoring the accelerometer.
* 当我们的应用程序得到焦点时,我们开始监测加速器。
*/
if ( engine->accelerometerSensor != NULL )
{
/* 启用指定的传感器 */
ASensorEventQueue_enableSensor( engine->sensorEventQueue,
engine->accelerometerSensor );
/* We'd like to get 60 events per second (in us).
* 我们将喜欢每秒得到六十个事件。
*/
ASensorEventQueue_setEventRate( engine->sensorEventQueue,
engine->accelerometerSensor,
( 1000L / 60 ) * 1000 );
}
break;
case APP_CMD_LOST_FOCUS:
/* When our app loses focus, we stop monitoring the accelerometer.
* 当我们的应用程序丢失焦点时,我们停止监测加速器。
*
* This is to avoid consuming battery while not being used.
* 在不使用时期避免消耗电池。
*/
if ( engine->accelerometerSensor != NULL )
{
/* 禁用指定的传感器 */
ASensorEventQueue_disableSensor( engine->sensorEventQueue,
engine->accelerometerSensor );
}
/* Also stop animating.
* 同样禁止动画绘制。
*/
engine->animating = 0;
engine_draw_frame( engine );
break;
}
}
/**
* This is the main entry point of a native application
* that is using android_native_app_glue.
* 这是使用了 android_native_app_glue 粘合剂模块的一个本地应用程序的主入口点。
*
* It runs in its own thread,
* with its own event loop for receiving input events and doing other things.
* 它运行在它自己的线程中,用它自己的事件循环来接收输入事件和做其它事情。
*/
void
android_main( struct android_app* state )
{
struct engine engine;
/* Make sure glue isn't stripped.
* 务必粘合剂模块未剥离。
* 注:在 android_native_app_glue.c 文件中这是一个空函数。
*/
app_dummy();
memset( &engine, 0, sizeof( engine ) );
/* 在应用程序各部分之间共享的数据 */
state->userData = &engine;
/* 指向一个处理最重要的应用程序命令的函数 */
state->onAppCmd = engine_handle_cmd;
/* 指向一个处理输入事件的函数 */
state->onInputEvent = engine_handle_input;
engine.app = state;
/* Prepare to monitor accelerometer.
* 准备监测加速器。
*/
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 );
if ( state->savedState != NULL )
{
/* We are starting with a previous saved state;
* 我们使用一个之前保存状态启动。
*
* restore from it.
* 用它来恢复。
*/
engine.state = *(struct saved_state*)state->savedState;
}
/* loop waiting for stuff to do.
* 循环等待材料来做。
*/
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.
* 如果动画绘制中,我们循环直到全部事件是读取,然后继续去画下一个动画的帧。
*/
/* 零(0) - 立即返回,负壹(-1) - 无限等待 */
while ( ( ident = ALooper_pollAll( engine.animating ? 0 : -1,
NULL, /* 不返回发生事件的文件描述符 */
/* 在 android_native_app_glue.c 文件的 android_app_entry 函数体中
* 调用 ALooper_addFd 函数时设置为 ALOOPER_EVENT_INPUT - 文件描述符读操作有效 */
&events,
(void**)&source ) ) >= 0 )
{
/* Process this event.
* 处理这个事件。
*/
if ( source != NULL )
{
/* 处理应用程序主线程的命令
* 在 android_native_app_glue.c 文件的 android_app_entry 函数体中
* 设置指向 process_cmd 或 process_input 函数。
*/
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;
}
}
if ( engine.animating )
{
/* Done with events; draw next animation frame.
* 事件完成;画下一个动画帧。
*/
engine.state.angle += .01f; /* 0.1 递增变化 */
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 );
}
}
}
//END_INCLUDE(all)
七、编译 native-activity 工程:
注:具体命令参见:http://wzhnsc.blogspot.com/2011/04/ide-android.html
注:具体命令参见:http://wzhnsc.blogspot.com/2011/04/ide-android.html
1.进入 native-activity 工程目录下
$ cd ~/android-ndk-r5b/samples/native-activity
2.用 ~/android-sdk-linux/tools 中的 android 工具生成一个可以让 ant 编译的工程
$ android update project -p .
$ android update project -p .
3.生成签名文件
$ keytool -genkey -alias wzhnsc.keystore -keyalg RSA -validity 10000 -keystore wzhnsc.keystore
输入keystore密码:
再次输入新密码:
再次输入新密码:
您的名字与姓氏是什么?
[Unknown]: wzhnsc
您的组织单位名称是什么?
[Unknown]: wn
您的组织名称是什么?
[Unknown]: nw
您所在的城市或区域名称是什么?
[Unknown]: beijing
您所在的州或省份名称是什么?
[Unknown]: bj
该单位的两字母国家代码是什么
[Unknown]: cn
CN=wzhnsc, OU=wn, O=nw, L=beijing, ST=bj, C=cn 正确吗?
[否]: Y
正在为以下对象生成 1,024 位 DSA 密钥对和自签名证书 (SHA1withDSA)(有效期为 90 天):
CN=wzhnsc, OU=wn, O=nw, L=beijing, ST=bj, C=cn
输入<mykey>的主密码
(如果和 keystore 密码相同,按回车):
[Unknown]: wzhnsc
您的组织单位名称是什么?
[Unknown]: wn
您的组织名称是什么?
[Unknown]: nw
您所在的城市或区域名称是什么?
[Unknown]: beijing
您所在的州或省份名称是什么?
[Unknown]: bj
该单位的两字母国家代码是什么
[Unknown]: cn
CN=wzhnsc, OU=wn, O=nw, L=beijing, ST=bj, C=cn 正确吗?
[否]: Y
正在为以下对象生成 1,024 位 DSA 密钥对和自签名证书 (SHA1withDSA)(有效期为 90 天):
CN=wzhnsc, OU=wn, O=nw, L=beijing, ST=bj, C=cn
输入<mykey>的主密码
(如果和 keystore 密码相同,按回车):
4.用 ant 生成发行版应用程序
$ ant release
5.为 apk 文件签名
$ jarsigner -verbose -keystore wzhnsc.keystore -signedjar ./bin/NativeActivity-release-signed.apk ./bin/NativeActivity-release-unsigned.apk wzhnsc.keystore
输入密钥库的口令短语: <-- 即,创建 wzhnsc.keystore 时输入的 keystore 密码
正在添加: META-INF/MANIFEST.MF
正在添加: META-INF/WZHNSC_K.SF
正在添加: META-INF/WZHNSC_K.RSA
正在签名: AndroidManifest.xml
正在签名: resources.arsc
正在签名: lib/armeabi/libnative-activity.so
正在添加: META-INF/MANIFEST.MF
正在添加: META-INF/WZHNSC_K.SF
正在添加: META-INF/WZHNSC_K.RSA
正在签名: AndroidManifest.xml
正在签名: resources.arsc
正在签名: lib/armeabi/libnative-activity.so
注:keytool 与 jarsigner 需安装 openjdk-6-jdk 来获得
$ sudo apt-get install openjdk-6-jdk
6.运行创建好的模拟器
$ emulator -avd Android2.3
7.将应用程序安装到模拟器中去
$ adb install ./bin/NativeActivity-release-signed.apk