नमूना: टीपॉट

टीपॉट का नमूना, एनडीके के तहत samples/Teapot/ डायरेक्ट्री में मौजूद होता है इंस्टॉलेशन की रूट डायरेक्ट्री से मेल खाना चाहिए. यह नमूना ऐतिहासिक ज़ेहन को रेंडर करने के लिए OpenGL लाइब्रेरी का इस्तेमाल करता है यूटा टीपॉट. खास तौर पर, यह ndk_helper हेल्पर क्लास को दिखाता है, गेम को लागू करने के लिए ज़रूरी नेटिव हेल्पर फ़ंक्शन का कलेक्शन और नेटिव ऐप्लिकेशन की तरह मिलते-जुलते ऐप्लिकेशन. इस क्लास में ये सुविधाएं मिलती हैं:

  • एक ऐब्स्ट्रैक्ट लेयर, GLContext, जो एनडीके से जुड़ी कुछ खास गतिविधियों को हैंडल करती है.
  • ऐसे हेल्पर फ़ंक्शन जो मददगार हैं, लेकिन एनडीके (एनडीके) में मौजूद नहीं हैं. जैसे, टैप डिटेक्शन.
  • टेक्सचर लोडिंग जैसी प्लैटफ़ॉर्म सुविधाओं के लिए, 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" />

ऐप्लिकेशन.एमके

NativeActivity फ़्रेमवर्क क्लास का इस्तेमाल करने वाले ऐप्लिकेशन को Android 9 से कम एपीआई लेवल, जिसने इस क्लास को लॉन्च किया. इस बारे में ज़्यादा जानकारी पाने के लिए NativeActivity क्लास, देखें स्थानीय गतिविधियां और ऐप्लिकेशन.

APP_PLATFORM := android-9

अगली लाइन, बिल्ड सिस्टम को इसके साथ काम करने वाले सभी आर्किटेक्चर के लिए बनाने के बारे में बताती है.

APP_ABI := all

इसके बाद, फ़ाइल बिल्ड सिस्टम को बताती है इस्तेमाल करने के लिए, C++ रनटाइम सहायता लाइब्रेरी.

APP_STL := stlport_static

Java-साइड लागू करना

TeapotNativeActivity फ़ाइल, teapots/classic-teapot/src/com/sample/teapot में GitHub पर NDK रेपो रूट डायरेक्ट्री में मौजूद है. यह, गतिविधि के लाइफ़साइकल इवेंट को मैनेज करता है और ShowUI() फ़ंक्शन वाली स्क्रीन पर टेक्स्ट दिखाने के लिए, पॉप-अप विंडो बनाता है. साथ ही, updateFPS() फ़ंक्शन की मदद से, फ़्रेम रेट को डाइनैमिक तौर पर अपडेट करता है. यहां दिया गया कोड आपको दिलचस्प लग सकता है, क्योंकि यह ऐप्लिकेशन की गतिविधि को फ़ुल स्क्रीन, इमर्सिव, और सिस्टम नेविगेशन बार के बिना तैयार करता है. इससे टीपॉट के रेंडर किए गए फ़्रेम दिखाने के लिए पूरी स्क्रीन का इस्तेमाल किया जा सकता है:

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

नेटिव-साइड लागू करना

इस सेक्शन में, C++ में लागू किए गए Teaपॉट ऐप्लिकेशन के बारे में जानकारी दी गई है.

टीपॉटरेंडरर.एच

ये फ़ंक्शन कॉल टीपॉट की असल रेंडरिंग करते हैं. यह मैट्रिक्स कैलकुलेशन और कैमरे की जगह बदलने के लिए ndk_helper टैप किया जाता है.

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


ndk_helper::TapCamera* camera_;

टी-पॉटनेटिव एक्टिविटी.सीपीपी

इन लाइनों में, नेटिव सोर्स फ़ाइल में ndk_helper शामिल है और हेल्पर क्लास का नाम.

#include "NDKHelper.h"

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

ndk_helper क्लास का पहला इस्तेमाल ईजीएल से जुड़ा लाइफ़साइकल, ईजीएल के कॉन्टेक्स्ट की स्थितियों (बनाए गए/खोए हुए) को इनके साथ जोड़ना 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 व्यू फ़्रस्टम) भी मिलता है.

ndk_helper::TapCamera tap_camera_;

इसके बाद, ऐप्लिकेशन, डिवाइस के सेंसर का इस्तेमाल करता है. इसके लिए, वह एनडीके में दिए गए नेटिव एपीआई का इस्तेमाल करता है.

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

यह ऐप्लिकेशन, अलग-अलग Android ऐप्लिकेशन के लिए इन फ़ंक्शन को कॉल करता है अलग-अलग फ़ंक्शन का इस्तेमाल करके, लाइफ़साइकल इवेंट और ईजीएल कॉन्टेक्स्ट की स्थिति में बदलाव ndk_helper ने Engine क्लास के ज़रिए उपलब्ध कराया.

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

इसके बाद, नीचे दिया गया फ़ंक्शन यूज़र इंटरफ़ेस (यूआई) डिसप्ले को अपडेट करने के लिए फिर से Java का इस्तेमाल करता है.

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

इसके बाद, यह फ़ंक्शन एक टेक्स्ट बॉक्स बनाने के लिए Java की तरफ़ वापस कॉल करता है नेटिव साइड में रेंडर की गई स्क्रीन पर सुपरइंपोज़्ड और फ़्रेम दिखा रहा है संख्या.

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 कक्षा, android_app_glue होने पर APP_CMD_INIT_WINDOW पोस्ट करती है सिस्टम से 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;