* `android_main()` is the entry point for an app using `native_app_glue`. * * This function is called from a separate thread spawned from * `ANativeActivity_onCreate`, which is the native equivalent of the * `onCreate` stage in the activity lifecycle: * https://developer.android.com/guide/components/activities/activity-lifecycle * * The `android_main()` implementation typically will perform
| 277 | * enter the main event loop, and shut down if necessary. |
| 278 | */ |
| 279 | void android_main(android_app* state) { |
| 280 | Engine engine{state}; |
| 281 | |
| 282 | // onAppCmd is called whenever native_app_glue receives one of the activity |
| 283 | // lifecycle events from the framework: |
| 284 | // https://developer.android.com/guide/components/activities/activity-lifecycle |
| 285 | // |
| 286 | // Typical native Android applications would implement the various |
| 287 | // onPause(), onResume(), etc in JNI methods. native_app_glue handles that |
| 288 | // for us and instead presents those method calls as if they were a pollable |
| 289 | // event queue. Our engine_handle_cmd callback is the function that will |
| 290 | // respond to new events in that queue. |
| 291 | state->onAppCmd = engine_handle_cmd; |
| 292 | |
| 293 | // The userData property will be passed to the callback we registered with |
| 294 | // onAppCmd. |
| 295 | state->userData = &engine; |
| 296 | |
| 297 | // destroyRequested will be set when onDestroy() is called: |
| 298 | // https://developer.android.com/guide/components/activities/activity-lifecycle#ondestroy |
| 299 | while (!state->destroyRequested) { |
| 300 | // native_app_glue communicates events to the app using Looper rather than |
| 301 | // method calls like a Java activity would use. Looper is an Android API |
| 302 | // similar to POSIX's select(2): |
| 303 | // https://developer.android.com/ndk/reference/group/looper#alooper |
| 304 | // |
| 305 | // Whenever an activity lifecycle method is called on our ANativeActivity, |
| 306 | // or an input event is received, native_app_glue will forward that to our |
| 307 | // app as a looper event. |
| 308 | // |
| 309 | // Polling looper can be done in either a blocking or non-blocking manner. |
| 310 | // If your app needs to wake periodically on this thread, pass a value for |
| 311 | // the poll timeout. Most of the things you'd normally do during this loop |
| 312 | // (respond to input or sensor updates, or even render the next frame of |
| 313 | // your game) should be driven by callbacks registered with those |
| 314 | // subsystems though rather than done here. Our main loop doesn't need to |
| 315 | // do anything other than process looper events. |
| 316 | android_poll_source* source = nullptr; |
| 317 | auto result = ALooper_pollOnce(-1, nullptr, nullptr, |
| 318 | reinterpret_cast<void**>(&source)); |
| 319 | if (result == ALOOPER_POLL_ERROR) { |
| 320 | fatal("ALooper_pollOnce returned an error"); |
| 321 | } |
| 322 | |
| 323 | if (source != nullptr) { |
| 324 | source->process(state, source); |
| 325 | } |
| 326 | } |
| 327 | |
| 328 | // Most cleanup code should actually run in response to activity lifecycle |
| 329 | // events that are processed in engine_handle_cmd rather than after the main |
| 330 | // loop exits, as would be more typical of main loops on desktop platforms. |
| 331 | } |