Çaydanlık örneği samples/Teapot/
dizininde, NDK yüklemesinin kök dizininde yer alır. Bu örnek, ikonik Utah çaydanlığı oluşturmak için OpenGL kitaplığını kullanır. Özellikle ndk_helper
yardımcı sınıfını, oyunları ve benzer uygulamaları yerel uygulamalar olarak uygulamak için gereken yerel yardımcı işlevleri gösterir. Bu sınıf aşağıdakileri sağlar:
- NDK'ya özgü belirli davranışları işleyen bir soyutlama katmanı (
GLContext
). - Dokunma algılama gibi yararlı olan ancak NDK'da bulunmayan yardımcı işlevler.
- Doku yükleme gibi platform özelliklerine yönelik JNI çağrıları için sarmalayıcılar.
AndroidManifest.xml
Buradaki etkinlik beyanı NativeActivity
değil, bunun bir alt sınıfıdır: TeapotNativeActivity
.
<activity android:name="com.sample.teapot.TeapotNativeActivity" android:label="@string/app_name" android:configChanges="orientation|keyboardHidden">
Sonuç olarak, derleme sisteminin oluşturduğu paylaşılan nesne dosyasının adı libTeapotNativeActivity.so
olur. Derleme sistemi, lib
önekini ve .so
uzantısını ekler. Her ikisi de manifestin başlangıçta android:value
'ye atadığı değerin parçası değildir.
<meta-data android:name="android.app.lib_name" android:value="TeapotNativeActivity" />
Başvuru.mk
NativeActivity
çerçeve sınıfını kullanan bir uygulama, ilgili sınıfı kullanıma sunan 9'dan düşük bir Android API düzeyi belirtmemelidir. NativeActivity
sınıfı hakkında daha fazla bilgi için Yerel Etkinlikler ve Uygulamalar bölümüne bakın.
APP_PLATFORM := android-9
Sonraki satır, derleme sistemine desteklenen tüm mimariler için derleme yapacağını belirtir.
APP_ABI := all
Ardından dosya, derleme sistemine hangi C++ çalışma zamanı destek kitaplığının kullanılacağını bildirir.
APP_STL := stlport_static
Java Tarafı Uygulama
TeapotNativeActivity
dosyası teapots/classic-teapot/src/com/sample/teapot
konumunda, GitHub'daki NDK depo kök dizininin altında yer alır. Etkinlik yaşam döngüsü olaylarını işler, ShowUI()
işleviyle ekranda metin görüntülemek için bir pop-up pencere oluşturur ve updateFPS()
işleviyle kare hızını dinamik olarak günceller. Aşağıdaki kod ilginizi çekebilir çünkü uygulamanın Etkinliği tam ekran, sürükleyici ve sistem gezinme çubukları olmadan hazırlanır, böylece oluşturulan çaydanlık karelerini göstermek için tüm ekran kullanılabilir:
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); }
Yerel Taraf Uygulama
Bu bölümde, Çaydanlık uygulamasının C++'ta uygulanan bölümü incelenmektedir.
ÇaydanlıkRenderer.h
Bu işlev çağrıları, çaydanlığın gerçek oluşturulmasını gerçekleştirir. Matris hesaplaması ve kullanıcının dokunduğu yere göre kamerayı yeniden konumlandırmak için ndk_helper
kullanır.
ndk_helper::Mat4 mat_projection_; ndk_helper::Mat4 mat_view_; ndk_helper::Mat4 mat_model_; ndk_helper::TapCamera* camera_;
ÇaydanlıkNativeActivity.cpp
Aşağıdaki satırlar yerel kaynak dosyada ndk_helper
içerir ve yardımcı sınıf adını tanımlar.
#include "NDKHelper.h" //------------------------------------------------------------------------- //Preprocessor //------------------------------------------------------------------------- #define HELPER_CLASS_NAME "com/sample/helper/NDKHelper" //Class name of helper function
ndk_helper
sınıfının ilk kullanımı, EGL ile ilgili yaşam döngüsünü ele alarak EGL bağlamı durumlarını (oluşturulan/kaybedilen) Android yaşam döngüsü olaylarıyla ilişkilendirmektir. ndk_helper
sınıfı, uygulamanın yok edilmiş bir etkinliği geri yükleyebilmesi için bağlam bilgilerini koruyabilmesini sağlar. Bu özellik, örneğin hedef makine döndürüldüğünde (bir etkinliğin kaldırılması, ardından hemen yeni yönde geri yüklenmesi) veya kilit ekranı göründüğünde yararlıdır.
ndk_helper::GLContext* gl_context_; // handles EGL-related lifecycle.
ndk_helper
ayrıca dokunmatik kontrol sağlar.
ndk_helper::DoubletapDetector doubletap_detector_; ndk_helper::PinchDetector pinch_detector_; ndk_helper::DragDetector drag_detector_; ndk_helper::PerfMonitor monitor_;
Aynı zamanda kamera kontrolü (openGL view frustum) sağlar.
ndk_helper::TapCamera tap_camera_;
Daha sonra uygulama, NDK'da sağlanan yerel API'lerden yararlanarak cihazın sensörlerini kullanmaya hazırlanır.
ASensorManager* sensor_manager_; const ASensor* accelerometer_sensor_; ASensorEventQueue* sensor_event_queue_;
Uygulama, ndk_helper
tarafından Engine
sınıfı aracılığıyla sağlanan çeşitli işlevleri kullanarak çeşitli Android yaşam döngüsü olaylarına ve EGL bağlam durumu değişikliklerine yanıt olarak aşağıdaki işlevleri çağırır.
void LoadResources(); void UnloadResources(); void DrawFrame(); void TermDisplay(); void TrimMemory(); bool IsReady();
Ardından, aşağıdaki işlev, kullanıcı arayüzü görüntüsünü güncellemek için Java tarafına geri çağırır.
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; }
Daha sonra bu işlev, yerel tarafta oluşturulan ekranın üzerine binen ve kare sayısını gösteren bir metin kutusu çizmek için Java tarafına geri çağrı yapar.
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; }
Uygulama, sistem saatini alır ve gerçek zamanlı saate dayalı, zamana dayalı animasyon için bunu oluşturucuya sağlar. Bu bilgiler, örneğin, hızın zamanın bir fonksiyonu olarak düştüğü momentumu hesaplarken kullanılır.
renderer_.Update( monitor_.GetCurrentTime() );
Uygulama artık oluşturulan kareyi GLcontext::Swap()
işlevi aracılığıyla görüntüleme için ön arabelleğe çeviriyor; ayrıca çevirme işlemi sırasında oluşan olası hataları da ele alıyor.
if( EGL_SUCCESS != gl_context_->Swap() ) // swaps buffer.
Program, dokunma hareketi etkinliklerini ndk_helper
sınıfında tanımlanan hareket algılayıcıya iletir. Hareket algılayıcı, sıkıştırma ve sürükleme gibi çoklu dokunma hareketlerini izler ve bu etkinliklerden biri tetiklendiğinde bir bildirim gönderir.
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; }
ndk_helper
sınıfı ayrıca, dokunma koordinatlarını dönüştürmek için burada kullanılan bir vektör-matematik kitaplığına
(vecmath.h
) erişim sağlar.
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 ); }
HandleCmd()
yöntemi, android_native_app_glue kitaplığından gönderilen komutları işler. Mesajların ne anlama geldiği hakkında daha fazla bilgi için android_native_app_glue.h
ve .c
kaynak dosyalarındaki yorumlara bakın.
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; } }
android_app_glue
sistemden onNativeWindowCreated()
geri çağırması aldığında ndk_helper
sınıfı APP_CMD_INIT_WINDOW
değerini yayınlar.
Uygulamalar normalde EGL başlatma gibi pencere başlatma işlemleri gerçekleştirebilir. Bunu etkinlik yaşam döngüsünün
dışında yaparlar, çünkü etkinlik henüz hazır değildir.
//Init helper functions ndk_helper::JNIHelper::Init( state->activity, HELPER_CLASS_NAME ); state->userData = &g_engine; state->onAppCmd = Engine::HandleCmd; state->onInputEvent = Engine::HandleInput;