最新的 Android 2.3 无需 Java 就可以开发应用,详情请看 这里。
这里是官方给的例子程序 ,来自:http://developer.android.com/reference/android/app/NativeActivity.html
这里是官方给的例子程序 ,来自:http://developer.android.com/reference/android/app/NativeActivity.html
代码片段(2)
[代码] AndroidManifest.xml
01 | < manifest xmlns:android = "http://schemas.android.com/apk/res/android" |
02 | package = "com.example.native_activity" |
03 | android:versionCode = "1" |
04 | android:versionName = "1.0" > |
05 | |
06 | <!-- This is the platform API where NativeActivity was introduced. --> |
07 | < uses-sdk android:minSdkVersion = "8" /> |
08 | |
09 | <!-- This .apk has no Java code itself, so set hasCode to false. --> |
10 | < application android:label = "@string/app_name" android:hasCode = "false" > |
11 | |
12 | <!-- Our activity is the built-in NativeActivity framework class. |
13 | This will take care of integrating with our NDK code. --> |
14 | < activity android:name = "android.app.NativeActivity" |
15 | android:label = "@string/app_name" |
16 | android:configChanges = "orientation|keyboardHidden" > |
17 | <!-- Tell NativeActivity the name of or .so --> |
18 | < meta-data android:name = "android.app.lib_name" |
19 | android:value = "native-activity" /> |
20 | < intent-filter > |
21 | < action android:name = "android.intent.action.MAIN" /> |
22 | < category android:name = "android.intent.category.LAUNCHER" /> |
23 | </ intent-filter > |
24 | </ activity > |
25 | </ application > |
26 | |
27 | </ manifest > |
[代码] Demo.c
001 | #include <jni.h> |
002 | #include <errno.h> |
003 | |
004 | #include <EGL/egl.h> |
005 | #include <GLES/gl.h> |
006 | |
007 | #include <android/sensor.h> |
008 | #include <android/log.h> |
009 | #include <android_native_app_glue.h> |
010 | |
011 | #define LOGI(...) ((void)__android_log_print(ANDROID_LOG_INFO, "native-activity", __VA_ARGS__)) |
012 | #define LOGW(...) ((void)__android_log_print(ANDROID_LOG_WARN, "native-activity", __VA_ARGS__)) |
013 | |
014 | /** |
015 | * Our saved state data. |
016 | */ |
017 | struct saved_state { |
018 | float angle; |
019 | int32_t x; |
020 | int32_t y; |
021 | }; |
022 | |
023 | /** |
024 | * Shared state for our app. |
025 | */ |
026 | struct engine { |
027 | struct android_app* app; |
028 | |
029 | ASensorManager* sensorManager; |
030 | const ASensor* accelerometerSensor; |
031 | ASensorEventQueue* sensorEventQueue; |
032 | |
033 | int animating; |
034 | EGLDisplay display; |
035 | EGLSurface surface; |
036 | EGLContext context; |
037 | int32_t width; |
038 | int32_t height; |
039 | struct saved_state state; |
040 | }; |
041 | |
042 | /** |
043 | * Initialize an EGL context for the current display. |
044 | */ |
045 | static int engine_init_display( struct engine* engine) { |
046 | // initialize OpenGL ES and EGL |
047 | |
048 | /* |
049 | * Here specify the attributes of the desired configuration. |
050 | * Below, we select an EGLConfig with at least 8 bits per color |
051 | * component compatible with on-screen windows |
052 | */ |
053 | const EGLint attribs[] = { |
054 | EGL_SURFACE_TYPE, EGL_WINDOW_BIT, |
055 | EGL_BLUE_SIZE, 8, |
056 | EGL_GREEN_SIZE, 8, |
057 | EGL_RED_SIZE, 8, |
058 | EGL_NONE |
059 | }; |
060 | EGLint w, h, dummy, format; |
061 | EGLint numConfigs; |
062 | EGLConfig config; |
063 | EGLSurface surface; |
064 | EGLContext context; |
065 | |
066 | EGLDisplay display = eglGetDisplay(EGL_DEFAULT_DISPLAY); |
067 | |
068 | eglInitialize(display, 0, 0); |
069 | |
070 | /* Here, the application chooses the configuration it desires. In this |
071 | * sample, we have a very simplified selection process, where we pick |
072 | * the first EGLConfig that matches our criteria */ |
073 | eglChooseConfig(display, attribs, &config, 1, &numConfigs); |
074 | |
075 | /* EGL_NATIVE_VISUAL_ID is an attribute of the EGLConfig that is |
076 | * guaranteed to be accepted by ANativeWindow_setBuffersGeometry(). |
077 | * As soon as we picked a EGLConfig, we can safely reconfigure the |
078 | * ANativeWindow buffers to match, using EGL_NATIVE_VISUAL_ID. */ |
079 | eglGetConfigAttrib(display, config, EGL_NATIVE_VISUAL_ID, &format); |
080 | |
081 | ANativeWindow_setBuffersGeometry(engine->app->window, 0, 0, format); |
082 | |
083 | surface = eglCreateWindowSurface(display, config, engine->app->window, NULL); |
084 | context = eglCreateContext(display, config, NULL, NULL); |
085 | |
086 | if (eglMakeCurrent(display, surface, surface, context) == EGL_FALSE) { |
087 | LOGW( "Unable to eglMakeCurrent" ); |
088 | return -1; |
089 | } |
090 | |
091 | eglQuerySurface(display, surface, EGL_WIDTH, &w); |
092 | eglQuerySurface(display, surface, EGL_HEIGHT, &h); |
093 | |
094 | engine->display = display; |
095 | engine->context = context; |
096 | engine->surface = surface; |
097 | engine->width = w; |
098 | engine->height = h; |
099 | engine->state.angle = 0; |
100 | |
101 | // Initialize GL state. |
102 | glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_FASTEST); |
103 | glEnable(GL_CULL_FACE); |
104 | glShadeModel(GL_SMOOTH); |
105 | glDisable(GL_DEPTH_TEST); |
106 | |
107 | return 0; |
108 | } |
109 | |
110 | /** |
111 | * Just the current frame in the display. |
112 | */ |
113 | static void engine_draw_frame( struct engine* engine) { |
114 | if (engine->display == NULL) { |
115 | // No display. |
116 | return ; |
117 | } |
118 | |
119 | // Just fill the screen with a color. |
120 | glClearColor((( float )engine->state.x)/engine->width, engine->state.angle, |
121 | (( float )engine->state.y)/engine->height, 1); |
122 | glClear(GL_COLOR_BUFFER_BIT); |
123 | |
124 | eglSwapBuffers(engine->display, engine->surface); |
125 | } |
126 | |
127 | /** |
128 | * Tear down the EGL context currently associated with the display. |
129 | */ |
130 | static void engine_term_display( struct engine* engine) { |
131 | if (engine->display != EGL_NO_DISPLAY) { |
132 | eglMakeCurrent(engine->display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT); |
133 | if (engine->context != EGL_NO_CONTEXT) { |
134 | eglDestroyContext(engine->display, engine->context); |
135 | } |
136 | if (engine->surface != EGL_NO_SURFACE) { |
137 | eglDestroySurface(engine->display, engine->surface); |
138 | } |
139 | eglTerminate(engine->display); |
140 | } |
141 | engine->animating = 0; |
142 | engine->display = EGL_NO_DISPLAY; |
143 | engine->context = EGL_NO_CONTEXT; |
144 | engine->surface = EGL_NO_SURFACE; |
145 | } |
146 | |
147 | /** |
148 | * Process the next input event. |
149 | */ |
150 | static int32_t engine_handle_input( struct android_app* app, AInputEvent* event) { |
151 | struct engine* engine = ( struct engine*)app->userData; |
152 | if (AInputEvent_getType(event) == AINPUT_EVENT_TYPE_MOTION) { |
153 | engine->animating = 1; |
154 | engine->state.x = AMotionEvent_getX(event, 0); |
155 | engine->state.y = AMotionEvent_getY(event, 0); |
156 | return 1; |
157 | } |
158 | return 0; |
159 | } |
160 | |
161 | /** |
162 | * Process the next main command. |
163 | */ |
164 | static void engine_handle_cmd( struct android_app* app, int32_t cmd) { |
165 | struct engine* engine = ( struct engine*)app->userData; |
166 | switch (cmd) { |
167 | case APP_CMD_SAVE_STATE: |
168 | // The system has asked us to save our current state. Do so. |
169 | engine->app->savedState = malloc ( sizeof ( struct saved_state)); |
170 | *(( struct saved_state*)engine->app->savedState) = engine->state; |
171 | engine->app->savedStateSize = sizeof ( struct saved_state); |
172 | break ; |
173 | case APP_CMD_INIT_WINDOW: |
174 | // The window is being shown, get it ready. |
175 | if (engine->app->window != NULL) { |
176 | engine_init_display(engine); |
177 | engine_draw_frame(engine); |
178 | } |
179 | break ; |
180 | case APP_CMD_TERM_WINDOW: |
181 | // The window is being hidden or closed, clean it up. |
182 | engine_term_display(engine); |
183 | break ; |
184 | case APP_CMD_GAINED_FOCUS: |
185 | // When our app gains focus, we start monitoring the accelerometer. |
186 | if (engine->accelerometerSensor != NULL) { |
187 | ASensorEventQueue_enableSensor(engine->sensorEventQueue, |
188 | engine->accelerometerSensor); |
189 | // We'd like to get 60 events per second (in us). |
190 | ASensorEventQueue_setEventRate(engine->sensorEventQueue, |
191 | engine->accelerometerSensor, (1000L/60)*1000); |
192 | } |
193 | break ; |
194 | case APP_CMD_LOST_FOCUS: |
195 | // When our app loses focus, we stop monitoring the accelerometer. |
196 | // This is to avoid consuming battery while not being used. |
197 | if (engine->accelerometerSensor != NULL) { |
198 | ASensorEventQueue_disableSensor(engine->sensorEventQueue, |
199 | engine->accelerometerSensor); |
200 | } |
201 | // Also stop animating. |
202 | engine->animating = 0; |
203 | engine_draw_frame(engine); |
204 | break ; |
205 | } |
206 | } |
207 | |
208 | /** |
209 | * This is the main entry point of a native application that is using |
210 | * android_native_app_glue. It runs in its own thread, with its own |
211 | * event loop for receiving input events and doing other things. |
212 | */ |
213 | void android_main( struct android_app* state) { |
214 | struct engine engine; |
215 | |
216 | // Make sure glue isn't stripped. |
217 | app_dummy(); |
218 | |
219 | memset (&engine, 0, sizeof (engine)); |
220 | state->userData = &engine; |
221 | state->onAppCmd = engine_handle_cmd; |
222 | state->onInputEvent = engine_handle_input; |
223 | engine.app = state; |
224 | |
225 | // Prepare to monitor accelerometer |
226 | engine.sensorManager = ASensorManager_getInstance(); |
227 | engine.accelerometerSensor = ASensorManager_getDefaultSensor(engine.sensorManager, |
228 | ASENSOR_TYPE_ACCELEROMETER); |
229 | engine.sensorEventQueue = ASensorManager_createEventQueue(engine.sensorManager, |
230 | state->looper, LOOPER_ID_USER, NULL, NULL); |
231 | |
232 | if (state->savedState != NULL) { |
233 | // We are starting with a previous saved state; restore from it. |
234 | engine.state = *( struct saved_state*)state->savedState; |
235 | } |
236 | |
237 | // loop waiting for stuff to do. |
238 | |
239 | while (1) { |
240 | // Read all pending events. |
241 | int ident; |
242 | int events; |
243 | struct android_poll_source* source; |
244 | |
245 | // If not animating, we will block forever waiting for events. |
246 | // If animating, we loop until all events are read, then continue |
247 | // to draw the next frame of animation. |
248 | while ((ident=ALooper_pollAll(engine.animating ? 0 : -1, NULL, &events, |
249 | ( void **)&source)) >= 0) { |
250 | |
251 | // Process this event. |
252 | if (source != NULL) { |
253 | source->process(state, source); |
254 | } |
255 | |
256 | // If a sensor has data, process it now. |
257 | if (ident == LOOPER_ID_USER) { |
258 | if (engine.accelerometerSensor != NULL) { |
259 | ASensorEvent event; |
260 | while (ASensorEventQueue_getEvents(engine.sensorEventQueue, |
261 | &event, 1) > 0) { |
262 | LOGI( "accelerometer: x=%f y=%f z=%f" , |
263 | event.acceleration.x, event.acceleration.y, |
264 | event.acceleration.z); |
265 | } |
266 | } |
267 | } |
268 | |
269 | // Check if we are exiting. |
270 | if (state->destroyRequested != 0) { |
271 | engine_term_display(&engine); |
272 | return ; |
273 | } |
274 | } |
275 | |
276 | if (engine.animating) { |
277 | // Done with events; draw next animation frame. |
278 | engine.state.angle += .01f; |
279 | if (engine.state.angle > 1) { |
280 | engine.state.angle = 0; |
281 | } |
282 | |
283 | // Drawing is throttled to the screen update rate, so there |
284 | // is no need to do timing here. |
285 | engine_draw_frame(&engine); |
286 | } |
287 | } |
288 | } |
資料來源 http://www.oschina.net/code/snippet_12_2904