「android_main 関数(ループ前)」では、関数の最初の部分について、検討しました。
「android_main 関数(ループ前2)」では、その続きを、メインループ前まで検討しました。
ここでは、続くメインループの部分を検討しています。
ご利用に際しては、必ずプライバシーポリシー(免責事項等)をご参照ください。
また、本サイトが初めての方は、まずこのページの注意事項をご覧ください。
投稿 January 30, 2018, 最終更新 July 30, 2018(様式変更)
ここまでに、Visual C++ 2015 で、新しく Android ネイティブアプリを作成したときに作成される
main.cpp の android_main 関数、メインループの前までを検討しました。
ここでは、while (1) で囲まれているメインループ部を見てみます。
while (1) { // 保留中のすべてのイベントを読み取ります。 int ident; int events; struct android_poll_source* source; // アニメーションしない場合、無期限にブロックしてイベントが発生するのを待ちます。 // アニメーションする場合、すべてのイベントが読み取られるまでループしてから続行します // アニメーションの次のフレームを描画します。 while ((ident = ALooper_pollAll(engine.animating ? 0 : -1, NULL, &events, (void**)&source)) >= 0) { // このイベントを処理します。 if (source != NULL) { source->process(state, source); } // センサーにデータがある場合、今すぐ処理します。 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); } } } // 終了するかどうか確認します。 if (state->destroyRequested != 0) { engine_term_display(&engine); return; } } if (engine.animating) { (中略;次のブロックで詳細を見ます) } }
while (1) 内の最初の while 関数、ALooper_pollAll 関数でイベントを取得します。 Windows でいう PeekMessage のイメージだと思っています。
データがあることを確認してから呼び出す source->process 関数は、
android_main 関数の最初の部分で、
state->onAppCmd = engine_handle_cmd;
としていますので、main.cpp に実装されている
static void engine_handle_cmd(struct android_app* app, int32_t cmd)
が呼び出されることになります。
ここでは触れませんが、struct android_app* app は、いつでも重要な構造体データです。
第2引数の cmd はコマンドで、
APP_CMD_START ならアプリが起動された、
APP_CMD_INIT_WINDOW ならウィンドウが初期化された、
というように通知されますので、適切な処理を実行します。
自動生成されたコードに記述されている以外にも、いくつかの定義があります。
種類は、これらのシンボルの定義位置を参照すれば、すぐにわかります。
APP_CMD_ を処理するのが基本だとは思いますが、
Android Developers の
NativeActivity
のページを見ると、もう少しわかりやすくなるかもしれないと、思わなくもありませんが、(今は)特には困らないと考えています。
続く ident == LOOPER_ID_USER の中は、
センサーの初期化で ASensorManager_createEventQueue 関数
state->destroyRequested に非0がセットされると、終了処理に入ります。 return する前に、確保したメモリの解放等、自分の終了処理を行うのが良いのではないでしょうか。
その時点のすべてのイベントを処理したら、while ループを抜けて、先に進みます。
投稿 January 30, 2018, 最終更新 July 30, 2018(様式変更)
while (1) ループの続きの部分についても確認します。
if (engine.animating) { // イベントが完了したら次のアニメーション フレームを描画します。 engine.state.angle += .01f; if (engine.state.angle > 1) { engine.state.angle = 0; } // 描画は画面の更新レートに合わせて調整されているため、 // ここで時間調整をする必要はありません。 engine_draw_frame(&engine); }
engine.animating は、メインループ開始直前に 1 に設定していますので、起動時は必ず 1 です。 ですので、あえて 0 にしない限りは、この条件にヒットします。 なお、自動生成されたコードでは、アプリが非アクティブになるとき 0 にしていますので、この部分(描画処理)が実行されなくなります。 再びアクティブになったとき、これを 1 にしないと画面が更新されませんが、自動生成されたコードには、それがないようでした。
engine.state.angle は単なる変数であって、画面を描画するときに絵が動いて見えるようにする目的です。
0.00〜1.00 まで、0.01 きざみで変更されている、というだけです。
そして engine_draw_frame 関数を呼び出して、描画を行っています。
この関数は、main.cpp で、次のように定義・実装されています。
static void engine_draw_frame(struct engine* engine)
Android ネイティブアプリでは、毎フレーム、バッファに完全に描画を行って、
eglSwapBuffers 関数を呼び出しで実際に画面に表示する、
ということを繰り返しているようです。
古い Windows プログラマとしては、画面用バッファを常に保持して、変更が必要な部分のみを書き変えて・・・
という手順でないのに驚きますが、十分高速であるようですから、すごいです。
android_main 関数(ループ前)
ネイティブアプリ プロジェクトの基本、android_main 関数について検討しています。最初の部分を扱っています。
android_main 関数(ループ前2)
ネイティブアプリ プロジェクトの基本、android_main 関数について検討しています。このページの前の部分です。
おすすめ記事はありません。