नए एपीआई इस्तेमाल करना

इस पेज में बताया गया है कि नए वर्शन पर चलने पर, आपका ऐप्लिकेशन नए ओएस की सुविधाओं का इस्तेमाल किस तरह कर सकता है ओएस वर्शन के साथ-साथ, यह पुराने डिवाइसों के साथ काम करता रहेगा.

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

इस वजह से, एनडीके आपको बेहतर रेफ़रंस बनाने से रोक देगा, ताकि ऐसे एपीआई जो आपके ऐप्लिकेशन के minSdkVersion से नए हैं. यह आपको इससे बचाता है गलती से शिपिंग कोड जो आपकी जाँच के दौरान काम कर गया था, लेकिन लोड नहीं हो पाएगा पुराने पर (System.loadLibrary() से UnsatisfiedLinkError को फेंका जाएगा) डिवाइस. दूसरी ओर, एपीआई का इस्तेमाल करने वाले कोड को लिखना ज़्यादा मुश्किल होता है आपके ऐप्लिकेशन के minSdkVersion से नया है, क्योंकि आपको इसका उपयोग करके API को कॉल करना होगा सामान्य फ़ंक्शन कॉल के बजाय dlopen() और dlsym().

मज़बूत पहचान फ़ाइलों का इस्तेमाल करने का विकल्प, कमज़ोर पहचान फ़ाइलों का इस्तेमाल करना है. कमज़ोर वह संदर्भ नहीं मिलता है जो लाइब्रेरी के लोड होने पर उस प्रतीक को लोड करने में विफल होने के बजाय nullptr पर सेट किया जा रहा है. वे अब भी सुरक्षित रूप से कॉल नहीं किया जा सकता, लेकिन जब तक कॉल साइट को एपीआई उपलब्ध न होने पर, बाकी कोड का इस्तेमाल किया जा सकता है. dlopen() और dlsym() का इस्तेमाल किए बिना, एपीआई को सामान्य तौर पर कॉल करें.

कमज़ोर एपीआई के रेफ़रंस के लिए, डाइनैमिक लिंकर से किसी और मदद की ज़रूरत नहीं होती है. ताकि उन्हें Android के किसी भी वर्शन के साथ इस्तेमाल किया जा सके.

आपके बिल्ड में कमज़ोर एपीआई रेफ़रंस चालू करना

सीमेक

CMake चलाते समय -DANDROID_WEAK_API_DEFS=ON पास करें. अगर CMake की मदद से वेबसाइट बनाई जा रही है, तो externalNativeBuild, अपने build.gradle.kts में यह जोड़ें (या अगर आप अब भी build.gradle का इस्तेमाल कर रहे हैं, तो यह शानदार विकल्प है):

android {
    // Other config...

    defaultConfig {
        // Other config...

        externalNativeBuild {
            cmake {
                arguments.add("-DANDROID_WEAK_API_DEFS=ON")
                // Other config...
            }
        }
    }
}

एनडीके-बिल्ड

अपनी Application.mk फ़ाइल में यह जोड़ें:

APP_WEAK_API_DEFS := true

अगर आपके पास पहले से कोई Application.mk फ़ाइल नहीं है, तो इसे भी उसी में बनाएं डायरेक्ट्री को आपकी Android.mk फ़ाइल के रूप में सेव किया गया है. आपके ndk-build के लिए build.gradle.kts (या build.gradle) फ़ाइल ज़रूरी नहीं है.

अन्य बिल्ड सिस्टम

अगर CMake या ndk-build का इस्तेमाल नहीं किया जा रहा है, तो अपने बिल्ड के दस्तावेज़ देखें सिस्टम का इस्तेमाल करके देखा जा सकता है कि इस सुविधा को चालू करने का कोई सुझाया गया तरीका है या नहीं. अगर आपके बिल्ड का सिस्टम इस विकल्प का मूल रूप से समर्थन नहीं करता है, तो आप इस तरीके से सुविधा को सक्षम कर सकते हैं कंपाइल करते समय निम्नलिखित फ़्लैग पास करना:

-D__ANDROID_UNAVAILABLE_SYMBOLS_ARE_WEAK__ -Werror=unguarded-availability

पहला विकल्प कमज़ोर रेफ़रंस को अनुमति देने के लिए, एनडीके हेडर को कॉन्फ़िगर करता है. दूसरा मोड़ आता है असुरक्षित एपीआई कॉल की चेतावनी को गड़बड़ी में शामिल करना.

ज़्यादा जानकारी के लिए, बिल्ड सिस्टम मेंटेनर गाइड देखें.

सुरक्षित एपीआई कॉल

यह सुविधा नए एपीआई को सुरक्षित तरीके से कॉल करने में मदद नहीं करती है. बस एक चीज़ जिसके लिए यह फ़ंक्शन लोड होने में लगने वाले समय की गड़बड़ी को कॉल-टाइम की गड़बड़ी की जगह टाल देता है. इसका फ़ायदा यह है कि रनटाइम के दौरान उस कॉल को सुरक्षित रख सकता है और ग्रेसफ़ुल तरीके से वापस आ सकता है, भले ही लागू करने या उपयोगकर्ता को यह सूचना देने का विकल्प होता है कि ऐप्लिकेशन की वह सुविधा अपने डिवाइस पर उपलब्ध नहीं कराया जाता या उस कोड पाथ को पूरी तरह से अनदेखा किया जाता है.

सुरक्षा के दायरे में न आने पर, Clang से चेतावनी (unguarded-availability) मिल सकती है ऐसे एपीआई को कॉल करें जो आपके ऐप्लिकेशन के minSdkVersion के लिए उपलब्ध नहीं है. अगर आप: ndk-build या हमारी CMake टूलचेन फ़ाइल का इस्तेमाल करना है, तो चेतावनी अपने-आप जब इस सुविधा को चालू किया जाता है, तो इसे चालू कर दिया जाता है और गड़बड़ी के तौर पर दिखाया जाता है.

यहां कुछ कोड का उदाहरण दिया गया है, जो बिना किसी शर्त के एपीआई का इस्तेमाल dlopen() और dlsym() का इस्तेमाल करके, इस सुविधा को चालू किया गया:

void LogImageDecoderResult(int result) {
    void* lib = dlopen("libjnigraphics.so", RTLD_LOCAL);
    CHECK_NE(lib, nullptr) << "Failed to open libjnigraphics.so: " << dlerror();
    auto func = reinterpret_cast<decltype(&AImageDecoder_resultToString)>(
        dlsym(lib, "AImageDecoder_resultToString")
    );
    if (func == nullptr) {
        LOG(INFO) << "cannot stringify result: " << result;
    } else {
        LOG(INFO) << func(result);
    }
}

यह पढ़ने में थोड़ा पेचीदा है, फ़ंक्शन नामों का कुछ दोहराव है (और अगर तो आप C लिखना चाहते हैं, साथ ही हस्ताक्षर भी), तो वह सफलतापूर्वक बन जाएगा, लेकिन अगर आपने गलती से फ़ंक्शन का नाम टाइप कर दिया है, तो रनटाइम पर फ़ॉलबैक आपको यह पैटर्न इस्तेमाल करना होगा. साथ ही, आपको हर एपीआई के लिए इस पैटर्न का इस्तेमाल करना होगा.dlsym

कमज़ोर एपीआई रेफ़रंस के साथ, ऊपर दिए गए फ़ंक्शन को इस तरह फिर से लिखा जा सकता है:

void LogImageDecoderResult(int result) {
    if (__builtin_available(android 31, *)) {
        LOG(INFO) << AImageDecoder_resultToString(result);
    } else {
        LOG(INFO) << "cannot stringify result: " << result;
    }
}

हुड के तहत, __builtin_available(android 31, *) कॉल android_get_device_api_level(), नतीजे को कैश मेमोरी में सेव करता है और उसकी तुलना 31 से करता है (यह वह एपीआई लेवल है जिसने AImageDecoder_resultToString() को पेश किया).

__builtin_available के लिए इस्तेमाल की जाने वाली वैल्यू तय करने का सबसे आसान तरीका बिना किसी गार्ड के (या __builtin_available(android 1, *)) और गड़बड़ी के मैसेज के मुताबिक कार्रवाई करें. उदाहरण के लिए, बिना किसी सुरक्षा के AImageDecoder_createFromAAsset() को किए गए इस कॉल का इस्तेमाल minSdkVersion 24 से यह मिलेगा:

error: 'AImageDecoder_createFromAAsset' is only available on Android 30 or newer [-Werror,-Wunguarded-availability]

ऐसे मामले में, __builtin_available(android 30, *) को सुरक्षित तरीके से कॉल करना चाहिए. अगर बिल्ड से जुड़ी कोई गड़बड़ी नहीं होती, तो एपीआई आपके लिए हमेशा उपलब्ध रहता है minSdkVersion को ऐक्सेस करें और किसी गार्ड की ज़रूरत नहीं है या आपका बिल्ड गलत तरीके से कॉन्फ़िगर किया गया है और unguarded-availability चेतावनी बंद है.

इसके अलावा, एनडीके एपीआई का रेफ़रंस "एपीआई 30 में पेश किया गया" एपीआई की शर्तों को पूरा करना होगा. अगर वह टेक्स्ट मौजूद नहीं है, तो इसका मतलब है कि यह एपीआई, इसके साथ काम करने वाले सभी एपीआई लेवल के लिए उपलब्ध है.

एपीआई गार्ड के दोहराव से बचना

अगर इसका इस्तेमाल किया जा रहा है, तो हो सकता है कि आपके ऐप्लिकेशन में कोड के ऐसे सेक्शन हों जो सिर्फ़ नए डिवाइसों पर ही इस्तेमाल किए जा सकते हैं. उन्हें दोहराने के बजाय __builtin_available() अपने हर फ़ंक्शन को चेक करें, तो आप अपने के लिए एक खास एपीआई लेवल की ज़रूरत होती है. उदाहरण के लिए, ImageDecoder एपीआई को एपीआई 30 में जोड़ा गया था. इसलिए, फ़ंक्शन को बेहतर बनाने के लिए, एपीआई, जिनमें ये काम किए जा सकते हैं:

#define REQUIRES_API(x) __attribute__((__availability__(android,introduced=x)))
#define API_AT_LEAST(x) __builtin_available(android x, *)

void DecodeImageWithImageDecoder() REQUIRES_API(30) {
    // Call any APIs that were introduced in API 30 or newer without guards.
}

void DecodeImageFallback() {
    // Pay the overhead to call the Java APIs via JNI, or use third-party image
    // decoding libraries.
}

void DecodeImage() {
    if (API_AT_LEAST(30)) {
        DecodeImageWithImageDecoder();
    } else {
        DecodeImageFallback();
    }
}

एपीआई गार्ड के बाहर

__builtin_available का इस्तेमाल कैसे किया जाता है, यह Clang इस बारे में काफ़ी अहम है. सिर्फ़ लिटरल वैल्यू if (__builtin_available(...)) काम करता है. हालांकि, इसे मैक्रो की जगह से बदला जा सकता है. बराबर if (!__builtin_available(...)) जैसी मामूली कार्रवाइयां काम नहीं करेंगी (Clang) unsupported-availability-guard चेतावनी उत्सर्जित करेगा. unguarded-availability). आने वाले समय में, Clang के वर्शन को बेहतर बनाया जा सकता है. यहां जाएं: LLVM समस्या 33161 पर जाएं.

unguarded-availability की जांच सिर्फ़ उस फ़ंक्शन पर लागू होती है जहां वे इस्तेमाल किए जाते हैं. Clang, चेतावनी जारी करेगा, भले ही एपीआई कॉल वाला फ़ंक्शन जिन्हें कभी भी सुरक्षित दायरे से कॉल किया जाता है. गार्ड को दोहराने से बचने के लिए अगर आपको अपना कोड इस्तेमाल करना है, तो एपीआई गार्ड के दोहराव से बचना लेख देखें.

यह डिफ़ॉल्ट क्यों नहीं है?

जब तक सही तरीके से उपयोग न किया जाए, तब तक सशक्त API संदर्भ और कमज़ोर API के बीच अंतर इस बात का ज़िक्र है कि पहले वाली क्वेरी तेज़ी से काम नहीं करेगी. हालांकि, बाद वाला विकल्प तब तक काम नहीं करेगा, जब तक उपयोगकर्ता कोई ऐसी कार्रवाई नहीं करता जिसकी वजह से एपीआई मौजूद न हो कॉल किया जा सकता है. ऐसा होने पर, गड़बड़ी के मैसेज में यह साफ़ तौर पर नहीं बताया जाएगा कंपाइल- टाइम "AFoo_bar() उपलब्ध नहीं है" गड़बड़ी हुई है, तो यह सेगफ़ॉल्ट होगी. के साथ मज़बूत संदर्भ का इस्तेमाल किया है, तो गड़बड़ी का मैसेज ज़्यादा साफ़ तौर पर दिख रहा है. डिफ़ॉल्ट तौर पर ज़्यादा सुरक्षित रहता है.

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

हम आपको इसका इस्तेमाल करने का सुझाव दते हैं, लेकिन इससे समस्याएं बढ़ जाएंगी का पता लगाना और डीबग करना मुश्किल है, तो आपको उन जोखिमों को जान-बूझकर स्वीकार करना चाहिए जिसमें आपकी जानकारी के बिना बदलाव होता है.

सीमाएं

यह सुविधा ज़्यादातर एपीआई के लिए काम करती है. हालांकि, कुछ मामलों में यह काम नहीं करती काम.

नए libc API से ज़्यादा समस्या हो सकती है. बाकी के ऐप्लिकेशन से अलग Android के एपीआई, हेडर में #if __ANDROID_API__ >= X की मदद से सुरक्षित किए जाते हैं न कि सिर्फ़ __INTRODUCED_IN(X), जो देखा जा सकता है. सबसे पुराना एपीआई लेवल मॉडर्न एनडीके (NDK) r21 पर काम करता है. इसलिए, आम तौर पर इस्तेमाल किए जाने वाले libc एपीआई पहले से ही उपलब्ध हैं. हर नए libc एपीआई को जोड़ा जाता है रिलीज़ करें (status.md देखें). हालांकि, वे जितने नए होते हैं, उनके होने की संभावना उतनी ही ज़्यादा होती है होना चाहिए, जिसकी ज़रूरत कुछ ही डेवलपर को होगी. इसके बावजूद, अगर आप उन डेवलपर को कॉल किया है. फ़िलहाल, आपको उन्हें कॉल करने के लिए dlsym() का इस्तेमाल जारी रखना होगा एपीआई, अगर आपका minSdkVersion, एपीआई से पुराना है. यह एक बड़ी समस्या है, हालांकि, ऐसा करने से सभी ऐप्लिकेशन (किसी भी ऐप्लिकेशन में) libc API के पॉलिफ़िल वाले कोड को कंपाइल नहीं किया जा सकेगा, क्योंकि लाइब्रेरी और लोकल एलान में, availability एट्रिब्यूट मेल नहीं खाते हैं), इसलिए हमें पक्के तौर पर नहीं पता कि हम इसे कब ठीक करेंगे या नहीं करेंगे.

ज़्यादातर डेवलपर को ऐसी लाइब्रेरी का सामना करना पड़ सकता है जो में शामिल है नया API आपके minSdkVersion से नया है. सिर्फ़ यह सुविधा कमज़ोर सिंबल के रेफ़रंस चालू करता है; कमज़ोर लाइब्रेरी जैसी कोई चीज़ नहीं है संदर्भ. उदाहरण के लिए, अगर आपका minSdkVersion, 24 साल का है, तो libvulkan.so और vkBindBufferMemory2 को सुरक्षित कॉल करें, क्योंकि libvulkan.so, एपीआई 24 से शुरू होने वाले डिवाइसों पर उपलब्ध है. दूसरी ओर, अगर आपकी minSdkVersion की उम्र 23 साल थी, तो आपको dlopen और dlsym पर वापस आना होगा क्योंकि लाइब्रेरी उस डिवाइस पर मौजूद नहीं होगी जो केवल एपीआई 23. हमारे पास इस मामले को ठीक करने का कोई अच्छा समाधान नहीं है, लेकिन लंबे समय में अपने-आप हल हो जाएगी, क्योंकि हम (जब भी संभव हो) नए नई लाइब्रेरी बनाने के लिए API.

लाइब्रेरी के लेखकों के लिए

अगर आप Android ऐप्लिकेशन में इस्तेमाल करने के लिए कोई लाइब्रेरी बना रहे हैं, तो आपको अपने सार्वजनिक हेडर में इस सुविधा का इस्तेमाल करने से बचें. इसका इस्तेमाल, विज्ञापन इन्वेंट्री के लिए आउट-ऑफ़-लाइन कोड, लेकिन अगर आप अपने ब्राउज़र में किसी भी कोड के लिए __builtin_available पर भरोसा करते हैं हेडर, जैसे कि इनलाइन फ़ंक्शन या टेंप्लेट की डेफ़िनिशन, उपभोक्ता इस सुविधा को चालू कर सकें. इन्हीं वजहों से हम इसे चालू नहीं करते एनडीके में डिफ़ॉल्ट रूप से उपलब्ध होती है, तो आपको अपनी तरफ़ से अपने उपभोक्ताओं को ध्यान में रखकर बनाया गया है.

अगर आपको अपने सार्वजनिक हेडर में ऐसा व्यवहार करना है, तो ताकि आपके उपयोगकर्ता यह जान सकें कि उन्हें इस सुविधा को चालू करना होगा और करने के जोखिमों के बारे में अच्छी तरह से जानती हो.