নেটিভ-অ্যাক্টিভিটি নমুনা NDK স্যাম্পল রুটের অধীনে, ফোল্ডার native-activity
তে থাকে। এটি একটি সম্পূর্ণরূপে নেটিভ অ্যাপ্লিকেশনের একটি খুব সাধারণ উদাহরণ, কোন জাভা সোর্স কোড নেই। কোনো জাভা উৎসের অনুপস্থিতিতে, জাভা কম্পাইলার এখনও ভার্চুয়াল মেশিন চালানোর জন্য একটি এক্সিকিউটেবল স্টাব তৈরি করে। স্টাবটি প্রকৃত, নেটিভ প্রোগ্রামের জন্য একটি মোড়ক হিসাবে কাজ করে, যা .so
ফাইলে অবস্থিত।
অ্যাপটি নিজেই কেবল পুরো স্ক্রিনে একটি রঙ রেন্ডার করে এবং তারপরে এটি সনাক্ত করা আন্দোলনের প্রতিক্রিয়া হিসাবে আংশিকভাবে রঙ পরিবর্তন করে।
AndroidManifest.xml
শুধুমাত্র নেটিভ কোড সহ একটি অ্যাপ অবশ্যই 9 এর কম একটি Android API স্তর নির্দিষ্ট করবে না, যা NativeActivity
ফ্রেমওয়ার্ক ক্লাস চালু করেছে।
<uses-sdk android:minSdkVersion="9" />
নিম্নলিখিত লাইনটি android:hasCode
false
হিসাবে ঘোষণা করে, কারণ এই অ্যাপটিতে শুধুমাত্র নেটিভ কোড আছে-কোন জাভা নেই।
<application android:label="@string/app_name" android:hasCode="false">
পরবর্তী লাইন NativeActivity
ক্লাস ঘোষণা করে।
<activity android:name="android.app.NativeActivity"
পরিশেষে, ম্যানিফেস্ট android:value
নির্দিষ্ট করে তৈরি করা শেয়ার্ড লাইব্রেরির নাম হিসেবে, প্রাথমিক lib
এবং .so
এক্সটেনশন বিয়োগ করে। এই মানটি অবশ্যই Android.mk
এ LOCAL_MODULE
এর নামের মতোই হতে হবে।
<meta-data android:name="android.app.lib_name" android:value="native-activity" />
Android.mk
এই ফাইলটি জেনারেট করার জন্য শেয়ার করা লাইব্রেরির নাম দিয়ে শুরু হয়।
LOCAL_MODULE := native-activity
এর পরে, এটি নেটিভ সোর্স-কোড ফাইলের নাম ঘোষণা করে।
LOCAL_SRC_FILES := main.c
এর পরে, এটি বাইনারি তৈরিতে ব্যবহার করার জন্য বিল্ড সিস্টেমের জন্য বাহ্যিক লাইব্রেরিগুলির তালিকা করে। -l
(link-against) বিকল্পটি প্রতিটি লাইব্রেরির নামের আগে থাকে।
-
log
হল একটি লগিং লাইব্রেরি। -
android
NDK-এর জন্য স্ট্যান্ডার্ড অ্যান্ড্রয়েড সমর্থন API গুলিকে অন্তর্ভুক্ত করে। Android এবং NDK সমর্থন করে এমন APIগুলি সম্পর্কে আরও তথ্যের জন্য, Android NDK নেটিভ API দেখুন। -
EGL
গ্রাফিক্স API-এর প্ল্যাটফর্ম-নির্দিষ্ট অংশের সাথে মিলে যায়। -
GLESv1_CM
OpenGL ES-এর সাথে মিলে যায়, Android এর জন্য OpenGL-এর সংস্করণ। এই লাইব্রেরি EGL এর উপর নির্ভর করে।
প্রতিটি লাইব্রেরির জন্য:
- প্রকৃত ফাইলের নাম
lib
দিয়ে শুরু হয় এবং.so
এক্সটেনশন দিয়ে শেষ হয়। উদাহরণস্বরূপ,log
লাইব্রেরির প্রকৃত ফাইলের নাম হলliblog.so
। - লাইব্রেরিটি নিম্নলিখিত ডিরেক্টরিতে থাকে, NDK রুট:
<ndk>/platforms/android-<sdk_version>/arch-<abi>/usr/lib/
LOCAL_LDLIBS := -llog -landroid -lEGL -lGLESv1_CM
পরের লাইনটি স্ট্যাটিক লাইব্রেরির নাম প্রদান করে, android_native_app_glue
, যা অ্যাপ্লিকেশনটি NativeActivity
জীবনচক্র ইভেন্ট এবং স্পর্শ ইনপুট পরিচালনা করতে ব্যবহার করে।
LOCAL_STATIC_LIBRARIES := android_native_app_glue
চূড়ান্ত লাইনটি এই স্ট্যাটিক লাইব্রেরি তৈরি করতে বিল্ড সিস্টেমকে বলে। ndk-build
স্ক্রিপ্ট বিল্ড লাইব্রেরি ( libandroid_native_app_glue.a
) বিল্ড প্রক্রিয়া চলাকালীন উৎপন্ন obj
ডিরেক্টরিতে রাখে। android_native_app_glue
লাইব্রেরি সম্পর্কে আরও তথ্যের জন্য, এর android_native_app_glue.h
হেডার এবং সংশ্লিষ্ট .c
সোর্স ফাইলটি দেখুন।
$(call import-module,android/native_app_glue)
Android.mk
ফাইল সম্পর্কে আরও তথ্যের জন্য, Android.mk দেখুন।
main.c
এই ফাইলটি মূলত পুরো প্রোগ্রাম ধারণ করে।
নিম্নলিখিতগুলি Android.mk
এ গণনাকৃত শেয়ার্ড এবং স্ট্যাটিক উভয় লাইব্রেরির সাথে সম্পর্কিত।
#include <EGL/egl.h> #include <GLES/gl.h> #include <android/sensor.h> #include <android/log.h> #include <android_native_app_glue>
android_native_app_glue
লাইব্রেরি নিম্নলিখিত ফাংশনটিকে কল করে, এটি একটি পূর্বনির্ধারিত স্টেট স্ট্রাকচার পাস করে। এটি একটি মোড়ক হিসাবেও কাজ করে যা NativeActivity
কলব্যাকগুলি পরিচালনাকে সহজ করে।
void android_main(struct android_app* state) {
এর পরে, প্রোগ্রামটি আঠালো লাইব্রেরি দ্বারা সারিবদ্ধ ইভেন্টগুলি পরিচালনা করে। ইভেন্ট হ্যান্ডলার রাষ্ট্র কাঠামো অনুসরণ করে।
struct engine engine; // Suppress link-time optimization that removes unreferenced code // to make sure glue isn't stripped. app_dummy(); memset(&engine, 0, sizeof(engine)); state->userData = &engine; state->onAppCmd = engine_handle_cmd; state->onInputEvent = engine_handle_input; engine.app = state;
অ্যাপ্লিকেশনটি sensor.h
এ API ব্যবহার করে সেন্সর নিরীক্ষণ শুরু করার জন্য প্রস্তুত করে।
engine.sensorManager = ASensorManager_getInstance(); engine.accelerometerSensor = ASensorManager_getDefaultSensor(engine.sensorManager, ASENSOR_TYPE_ACCELEROMETER); engine.sensorEventQueue = ASensorManager_createEventQueue(engine.sensorManager, state->looper, LOOPER_ID_USER, NULL, NULL);
এর পরে, একটি লুপ শুরু হয়, যেখানে অ্যাপ্লিকেশনটি বার্তাগুলির (সেন্সর ইভেন্ট) জন্য সিস্টেমটি পোল করে। এটি android_native_app_glue
এ বার্তা পাঠায়, যা android_main
এ সংজ্ঞায়িত কোনো onAppCmd
ইভেন্টের সাথে মেলে কিনা তা পরীক্ষা করে। যখন একটি মিল ঘটে, তখন বার্তাটি হ্যান্ডলারকে কার্যকর করার জন্য পাঠানো হয়।
while (1) { // Read all pending events. int ident; int events; struct android_poll_source* source; // If not animating, we will block forever waiting for events. // If animating, we loop until all events are read, then continue // to draw the next frame of animation. while ((ident=ALooper_pollAll(engine.animating ? 0 : -1, NULL, &events, (void**)&source)) >= 0) { // Process this event. if (source != NULL) { source->process(state, source); } // If a sensor has data, process it now. 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); } } } // Check if we are exiting. if (state->destroyRequested != 0) { engine_term_display(&engine); return; } }
একবার সারি খালি হয়ে গেলে এবং প্রোগ্রামটি পোলিং লুপ থেকে বেরিয়ে গেলে, প্রোগ্রামটি স্ক্রীন আঁকতে ওপেনজিএলকে কল করে।
if (engine.animating) { // Done with events; draw next animation frame. engine.state.angle += .01f; if (engine.state.angle > 1) { engine.state.angle = 0; } // Drawing is throttled to the screen update rate, so there // is no need to do timing here. engine_draw_frame(&engine); } }