Das Teapot-Beispiel befindet sich im Verzeichnis samples/Teapot/
unter dem NDK.
das Stammverzeichnis der Installation. In diesem Beispiel wird die OpenGL-Bibliothek verwendet,
Utah, USA
Teekanne. Insbesondere wird die Hilfsklasse ndk_helper
präsentiert,
eine Sammlung nativer Hilfsfunktionen, die für die Implementierung von Spielen und
ähnliche Anwendungen wie native Anwendungen. Dieser Kurs bietet folgende Vorteile:
- Die Abstraktionsebene
GLContext
, die bestimmte NDK-spezifische Verhaltensweisen verarbeitet. - Hilfsfunktionen, die nützlich, aber im NDK nicht vorhanden sind, z. B. die Tipperkennung.
- Wrapper für JNI-Aufrufe für Plattformfunktionen wie das Laden von Texturen.
AndroidManifest.xml
Die Erklärung zur Aktivität ist hier nicht „NativeActivity
“, aber
eine abgeleitete Klasse davon: TeapotNativeActivity
.
<activity android:name="com.sample.teapot.TeapotNativeActivity" android:label="@string/app_name" android:configChanges="orientation|keyboardHidden">
Letztendlich lautet der Name der gemeinsam genutzten Objektdatei, die das Build-System erstellt,
libTeapotNativeActivity.so
Das Build-System fügt das Präfix lib
und den .so
Erweiterung; Beide sind nicht Teil des Werts, dem das Manifest ursprünglich zuweist.
android:value
<meta-data android:name="android.app.lib_name" android:value="TeapotNativeActivity" />
Anwendung.mk
In einer App, die die Framework-Klasse NativeActivity
verwendet, darf kein Wert für
Android API-Level niedriger als 9, in dem diese Klasse eingeführt wurde. Weitere Informationen zur
NativeActivity
Klasse, siehe
Native Aktivitäten und Anwendungen.
APP_PLATFORM := android-9
In der nächsten Zeile wird das Build-System angewiesen, alle unterstützten Architekturen zu erstellen.
APP_ABI := all
Als Nächstes teilt die Datei dem Build-System mit, C++-Laufzeitsupportbibliothek, die verwendet werden soll.
APP_STL := stlport_static
Implementierung auf Java-Seite
Die DateiTeapotNativeActivity
befindet sich in teapots/classic-teapot/src/com/sample/teapot
im NDK-Repository-Stammverzeichnis auf GitHub. Sie verarbeitet Lebenszyklusereignisse von Aktivitäten, erstellt ein Pop-up-Fenster, um Text auf dem Bildschirm mit der Funktion „ShowUI()
“ anzuzeigen, und aktualisiert die Framerate dynamisch mit der Funktion „updateFPS()
“. Der folgende Code könnte interessant für Sie sein, da er die Activity-Aktivitäten der App als Vollbild, immersive und keine Systemnavigationsleisten vorbereitet, sodass der gesamte Bildschirm für die Darstellung gerenderter Teekannen-Frames verwendet werden kann:
Kotlin
fun setImmersiveSticky() { window.decorView.systemUiVisibility = ( View.SYSTEM_UI_FLAG_FULLSCREEN or View.SYSTEM_UI_FLAG_HIDE_NAVIGATION or View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY or View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN or View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION or View.SYSTEM_UI_FLAG_LAYOUT_STABLE ) }
Java
void setImmersiveSticky() { View decorView = getWindow().getDecorView(); decorView.setSystemUiVisibility(View.SYSTEM_UI_FLAG_FULLSCREEN | View.SYSTEM_UI_FLAG_HIDE_NAVIGATION | View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION | View.SYSTEM_UI_FLAG_LAYOUT_STABLE); }
Native Implementierung
In diesem Abschnitt wird der Teil der Teapot-App erläutert, der in C++ implementiert wurde.
TeapotRenderer.h.
Diese Funktionsaufrufe führen das eigentliche Rendern der Teekanne durch. Sie verwendet
ndk_helper
für die Matrixberechnung und die Neupositionierung der Kamera
je nachdem, worauf die
Nutzenden tippen.
ndk_helper::Mat4 mat_projection_; ndk_helper::Mat4 mat_view_; ndk_helper::Mat4 mat_model_; ndk_helper::TapCamera* camera_;
TeekanneNativeActivity.cpp
Die folgenden Zeilen enthalten ndk_helper
in der nativen Quelldatei und definieren die
Name der Hilfsklasse.
#include "NDKHelper.h" //------------------------------------------------------------------------- //Preprocessor //------------------------------------------------------------------------- #define HELPER_CLASS_NAME "com/sample/helper/NDKHelper" //Class name of helper function
Die erste Verwendung der Klasse ndk_helper
besteht darin,
EGL-bezogener Lebenszyklus, Verknüpfung von (erstellten/verlorenen) EGL-Kontextstatus mit
Android-Lebenszyklusereignisse Die Klasse ndk_helper
ermöglicht der Anwendung, den Kontext beizubehalten
damit das System eine zerstörte Aktivität wiederherstellen kann. Diese Funktion ist nützlich,
wenn die Zielmaschine rotiert wird (wodurch eine Aktivität
zerstört und dann sofort in der neuen Ausrichtung wiederhergestellt werden kann, oder wenn das Schloss
erscheint.
ndk_helper::GLContext* gl_context_; // handles EGL-related lifecycle.
Als Nächstes bietet ndk_helper
die Touchbedienung.
ndk_helper::DoubletapDetector doubletap_detector_; ndk_helper::PinchDetector pinch_detector_; ndk_helper::DragDetector drag_detector_; ndk_helper::PerfMonitor monitor_;
Außerdem bietet es die Kamerasteuerung (OpenGL-Ansichten).
ndk_helper::TapCamera tap_camera_;
Die App bereitet sich dann auf die Verwendung der Gerätesensoren mithilfe der nativen APIs des NDK vor.
ASensorManager* sensor_manager_; const ASensor* accelerometer_sensor_; ASensorEventQueue* sensor_event_queue_;
Die App ruft die folgenden Funktionen als Reaktion auf verschiedene Android-
Lebenszyklus-Ereignisse und Änderungen des EGL-Kontextstatus mithilfe verschiedener Funktionen
über die Klasse Engine
von ndk_helper
bereitgestellt.
void LoadResources(); void UnloadResources(); void DrawFrame(); void TermDisplay(); void TrimMemory(); bool IsReady();
Die folgende Funktion ruft dann auf der Java-Seite auf, um die UI-Anzeige zu aktualisieren.
void Engine::ShowUI() { JNIEnv *jni; app_->activity->vm->AttachCurrentThread( &jni, NULL ); //Default class retrieval jclass clazz = jni->GetObjectClass( app_->activity->clazz ); jmethodID methodID = jni->GetMethodID( clazz, "showUI", "()V" ); jni->CallVoidMethod( app_->activity->clazz, methodID ); app_->activity->vm->DetachCurrentThread(); return; }
Als Nächstes ruft diese Funktion auf der Java-Seite einen Aufruf auf, um ein Textfeld zu zeichnen. wird auf dem Bildschirm der nativen Seite eingeblendet. Anzahl.
void Engine::UpdateFPS( float fFPS ) { JNIEnv *jni; app_->activity->vm->AttachCurrentThread( &jni, NULL ); //Default class retrieval jclass clazz = jni->GetObjectClass( app_->activity->clazz ); jmethodID methodID = jni->GetMethodID( clazz, "updateFPS", "(F)V" ); jni->CallVoidMethod( app_->activity->clazz, methodID, fFPS ); app_->activity->vm->DetachCurrentThread(); return; }
Die Anwendung ruft die Systemuhr ab und stellt sie dem Renderer bereit. für zeitbasierte Animationen auf Basis der Echtzeituhr. Diese Informationen werden beispielsweise in Impuls berechnet, bei dem die Geschwindigkeit als Funktion der Zeit abnimmt.
renderer_.Update( monitor_.GetCurrentTime() );
Die Anwendung dreht den gerenderten Frame jetzt mithilfe der Funktion GLcontext::Swap()
in den Front-Puffer für die Anzeige. behandelt sie auch mögliche Fehler,
die während des Umblättern-Vorgangs auftreten können.
if( EGL_SUCCESS != gl_context_->Swap() ) // swaps buffer.
Das Programm übergibt Berührungsbewegungen an den definierten Gestendetektor.
in der Klasse ndk_helper
. Die Bewegungserkennung erfasst die Multi-Touch-Funktion
zum Beispiel durch Auseinander- und Zusammenziehen der Finger. Außerdem wird eine Benachrichtigung gesendet, wenn
alle diese Ereignisse.
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; }
Die Klasse ndk_helper
bietet auch Zugriff auf eine Bibliothek für Vektor-Mathematik
(vecmath.h
) und verwende es hier zur Umwandlung von Berührungskoordinaten.
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 ); }
Die Methode HandleCmd()
verarbeitet Befehle, die vom
android_native_app_glue-Bibliothek. Weitere Informationen zu den Nachrichten,
finden Sie in den Kommentaren unter android_native_app_glue.h
und
.c
Quelldateien.
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; } }
Der Kurs "ndk_helper
" postet APP_CMD_INIT_WINDOW
, wenn android_app_glue
erhält einen onNativeWindowCreated()
-Callback vom System.
Anwendungen können normalerweise Fensterinitialisierungen wie EGL ausführen
die Initialisierung bei. Dies geschieht außerhalb des Aktivitätslebenszyklus,
Aktivität ist noch nicht bereit.
//Init helper functions ndk_helper::JNIHelper::Init( state->activity, HELPER_CLASS_NAME ); state->userData = &g_engine; state->onAppCmd = Engine::HandleCmd; state->onInputEvent = Engine::HandleInput;