نمونه: قوری

نمونه Teapot در زیر در پوشه samples/Teapot/ ، در پوشه ریشه نصب NDK قرار دارد. این نمونه از کتابخانه OpenGL برای ارائه قوری نمادین یوتا استفاده می کند. به طور خاص، کلاس کمکی ndk_helper را به نمایش می‌گذارد، مجموعه‌ای از توابع کمکی بومی که برای اجرای بازی‌ها و برنامه‌های کاربردی مشابه به عنوان برنامه‌های بومی مورد نیاز است. این کلاس ارائه می دهد:

  • یک لایه انتزاعی، GLContext ، که رفتارهای خاص NDK را کنترل می کند.
  • توابع کمکی که مفید هستند اما در NDK وجود ندارند، مانند تشخیص ضربه.
  • Wrappers برای JNI ویژگی‌های پلتفرمی مانند بارگذاری بافت را می‌طلبد.

AndroidManifest.xml

اعلان فعالیت در اینجا خود NativeActivity نیست، بلکه یک زیر کلاس از آن است: TeapotNativeActivity .

<activity android:name="com.sample.teapot.TeapotNativeActivity"
        android:label="@string/app_name"
        android:configChanges="orientation|keyboardHidden">

در نهایت، نام فایل اشتراک‌گذاری شی که سیستم ساخت می‌سازد libTeapotNativeActivity.so است. سیستم ساخت، پیشوند lib و پسوند .so اضافه می کند. همچنین بخشی از مقداری نیست که مانیفست در ابتدا به android:value اختصاص می‌دهد.

<meta-data android:name="android.app.lib_name"
        android:value="TeapotNativeActivity" />

Application.mk

برنامه‌ای که از کلاس فریمورک NativeActivity استفاده می‌کند نباید سطح API Android کمتر از 9 را که کلاس را معرفی کرده است، مشخص کند. برای اطلاعات بیشتر در مورد کلاس NativeActivity ، به Native Activities and Applications مراجعه کنید.

APP_PLATFORM := android-9

خط بعدی به سیستم ساخت می گوید که برای تمام معماری های پشتیبانی شده بسازد.

APP_ABI := all

در مرحله بعد، فایل به سیستم بیلد می گوید که از کدام کتابخانه پشتیبانی از زمان اجرا C++ استفاده کند.

APP_STL := stlport_static

پیاده سازی سمت جاوا

فایل TeapotNativeActivity در teapots/classic-teapot/src/com/sample/teapot ، در زیر دایرکتوری ریشه NDK repo در GitHub قرار دارد. رویدادهای چرخه حیات فعالیت را مدیریت می کند، یک پنجره بازشو برای نمایش متن روی صفحه با تابع ShowUI() ایجاد می کند و نرخ فریم را به صورت پویا با تابع updateFPS() به روز می کند. کد زیر ممکن است برای شما جالب باشد زیرا Activity برنامه را به صورت تمام صفحه، همه جانبه و بدون نوارهای ناوبری سیستم آماده می کند تا از کل صفحه برای نمایش قاب های قوری رندر شده استفاده شود:

کاتلین

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
            )
}

جاوا

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);
}

پیاده سازی سمت بومی

این بخش بخشی از برنامه Teapot را که در C++ پیاده سازی شده است بررسی می کند.

TeapotRenderer.h

این فراخوانی های تابع، رندر واقعی قوری را انجام می دهند. از ndk_helper برای محاسبه ماتریس و تغییر موقعیت دوربین بر اساس جایی که کاربر ضربه می زند استفاده می کند.

ndk_helper::Mat4 mat_projection_;
ndk_helper::Mat4 mat_view_;
ndk_helper::Mat4 mat_model_;


ndk_helper::TapCamera* camera_;

TeapotNativeActivity.cpp

خطوط زیر شامل ndk_helper در فایل منبع اصلی است و نام کلاس کمکی را تعریف می کند.


#include "NDKHelper.h"

//-------------------------------------------------------------------------
//Preprocessor
//-------------------------------------------------------------------------
#define HELPER_CLASS_NAME "com/sample/helper/NDKHelper" //Class name of helper
function

اولین استفاده از کلاس ndk_helper ، مدیریت چرخه حیات مرتبط با EGL است که حالت‌های بافت EGL (ایجاد شده/از دست رفته) را با رویدادهای چرخه حیات Android مرتبط می‌کند. کلاس ndk_helper برنامه را قادر می سازد تا اطلاعات زمینه را حفظ کند تا سیستم بتواند یک فعالیت از بین رفته را بازیابی کند. این توانایی مفید است، به عنوان مثال، زمانی که ماشین مورد نظر می چرخد ​​(که باعث از بین رفتن یک فعالیت می شود، سپس بلافاصله در جهت جدید بازیابی می شود)، یا زمانی که صفحه قفل ظاهر می شود.

ndk_helper::GLContext* gl_context_; // handles EGL-related lifecycle.

بعد، ndk_helper کنترل لمسی را فراهم می کند.

ndk_helper::DoubletapDetector doubletap_detector_;
ndk_helper::PinchDetector pinch_detector_;
ndk_helper::DragDetector drag_detector_;
ndk_helper::PerfMonitor monitor_;

همچنین کنترل دوربین (openGL view frustum) را فراهم می کند.

ndk_helper::TapCamera tap_camera_;

سپس برنامه با استفاده از APIهای بومی ارائه شده در NDK برای استفاده از حسگرهای دستگاه آماده می شود.

ASensorManager* sensor_manager_;
const ASensor* accelerometer_sensor_;
ASensorEventQueue* sensor_event_queue_;

این برنامه با استفاده از عملکردهای مختلف ارائه شده توسط ndk_helper از طریق کلاس Engine ، توابع زیر را در پاسخ به رویدادهای مختلف چرخه زندگی Android و تغییرات وضعیت بافت EGL فراخوانی می‌کند.


void LoadResources();
void UnloadResources();
void DrawFrame();
void TermDisplay();
void TrimMemory();
bool IsReady();

سپس، تابع زیر به سمت جاوا باز می گردد تا نمایشگر رابط کاربری را به روز کند.

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;
}

در مرحله بعد، این تابع به سمت جاوا برمی گردد تا یک کادر متنی که روی صفحه نمایش داده شده در سمت اصلی نمایش داده شده است، بکشد و تعداد فریم ها را نشان دهد.

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;
}

این برنامه ساعت سیستم را دریافت می کند و آن را برای انیمیشن مبتنی بر زمان بر اساس ساعت بلادرنگ در اختیار رندر قرار می دهد. این اطلاعات، برای مثال، در محاسبه حرکت، که در آن سرعت به عنوان تابعی از زمان کاهش می یابد، استفاده می شود.

renderer_.Update( monitor_.GetCurrentTime() );

برنامه اکنون فریم رندر شده را به بافر جلویی برای نمایش از طریق تابع GLcontext::Swap() برمیگرداند. همچنین خطاهای احتمالی رخ داده در طول فرآیند چرخش را کنترل می کند.

if( EGL_SUCCESS != gl_context_->Swap() )  // swaps
buffer.

برنامه رویدادهای حرکت لمسی را به آشکارساز ژست تعریف شده در کلاس ndk_helper ارسال می کند. ردیاب ژست حرکات چند لمسی، مانند فشار دادن و کشیدن، را ردیابی می کند و هنگامی که توسط هر یک از این رویدادها فعال می شود، اعلان ارسال می کند.

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 همچنین دسترسی به کتابخانه ریاضی بردار ( vecmath.h ) را فراهم می کند، که از آن در اینجا برای تبدیل مختصات لمسی استفاده می کند.

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() دستورات ارسال شده از کتابخانه android_native_app_glue را کنترل می کند. برای اطلاعات بیشتر درباره معنای پیام‌ها، به نظرات موجود در فایل‌های منبع android_native_app_glue.h و .c مراجعه کنید.

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;
    }
}

کلاس ndk_helper APP_CMD_INIT_WINDOW را زمانی که android_app_glue یک پاسخ تماس onNativeWindowCreated() از سیستم دریافت می کند، پست می کند. برنامه ها معمولاً می توانند مقداردهی اولیه پنجره را انجام دهند، مانند مقداردهی اولیه EGL. آنها این کار را خارج از چرخه حیات فعالیت انجام می دهند، زیرا فعالیت هنوز آماده نیست.

//Init helper functions
ndk_helper::JNIHelper::Init( state->activity, HELPER_CLASS_NAME );

state->userData = &g_engine;
state->onAppCmd = Engine::HandleCmd;
state->onInputEvent = Engine::HandleInput;