Bu sayfada, uygulamanızın yeni bir işletim sistemi sisteminde çalışırken yeni işletim sistemi Eski cihazlarla uyumluluğu korurken işletim sistemi sürümleri.
Varsayılan olarak, uygulamanızdaki NDK API'lerine yapılan referanslar güçlü referanslardır. Kitaplığınız açıkken Android'in dinamik yükleyicisi bu sorunları gönül rahatlığıyla çözecektir yüklendi. Semboller bulunmazsa uygulama iptal eder. Bu, devre dışı bırakılır (bu şekilde, eksik API devre dışı bırakılana kadar çağrıldı.
Bu nedenle, NDK,
Uygulamanızın minSdkVersion
ürününden daha yeni API'ler. Bu, sizi
testiniz sırasında çalışan ancak yüklenmeyen kodu yanlışlıkla göndermek
(UnsatisfiedLinkError
, System.loadLibrary()
konumundan atılacak)
cihazlar. Diğer yandan, API'ler kullanan kod yazmak daha zordur.
uygulamanızın minSdkVersion
değerinden daha yenidir çünkü API'leri
dlopen()
ve dlsym()
tuşlarına basın.
Güçlü referanslar kullanmanın alternatifi, zayıf referanslar kullanmaktır. Zayıf
kitaplığın yüklenmesi şu adresle sonuçlandığında bulunamayan referans:
bu simgenin yüklenmemesi yerine nullptr
olarak ayarlanması gerekir. Hala
güvenli bir şekilde çağrılamaz, ancak çağrı siteleri aramayı önlemek için korunduğu sürece
API'nin kullanılamadığı durumlarda, kodunuzun geri kalanı çalıştırılabilir ve
dlopen()
ve dlsym()
uygulamalarına gerek kalmadan API'yi normal şekilde çağırın.
Zayıf API referansları, dinamik bağlayıcıdan ek destek gerektirmez. Android'in tüm sürümlerinde kullanılabilir.
Derlemenizde zayıf API referanslarını etkinleştirme
CMake
CMake çalıştırırken -DANDROID_WEAK_API_DEFS=ON
öğesini geçirin. CMake kullanıyorsanız
externalNativeBuild
, aşağıdakini build.gradle.kts
(veya
Hâlâ build.gradle
kullanıyorsanız harika bir eşdeğeri):
android {
// Other config...
defaultConfig {
// Other config...
externalNativeBuild {
cmake {
arguments.add("-DANDROID_WEAK_API_DEFS=ON")
// Other config...
}
}
}
}
ndk-kurum
Application.mk
dosyanıza aşağıdakileri ekleyin:
APP_WEAK_API_DEFS := true
Henüz Application.mk
dosyanız yoksa dosyayı aynı
Android.mk
dosyanız olarak kullanılır.
build.gradle.kts
(veya build.gradle
) dosyası ndk-build için gerekli değil.
Diğer derleme sistemleri
CMake veya ndk-build kullanmıyorsanız derlemenizin belgelerine bakın ve sistemin bu özelliği etkinleştirmenin önerilen bir yolunun olup olmadığına bakın. Derlemeniz sistem bu seçeneği yerleşik olarak desteklemiyorsa özelliği derleme sırasında şu işaretleri iletmek:
-D__ANDROID_UNAVAILABLE_SYMBOLS_ARE_WEAK__ -Werror=unguarded-availability
İlki, NDK başlıklarını zayıf referanslara izin verecek şekilde yapılandırır. İkinci dönüş uyarısını bir hataya dönüştürür.
Daha fazla bilgi için Derleme Sistem Bakımı Kılavuzu'na bakın.
Korumalı API çağrıları
Bu özellik, sihirli bir şekilde yeni API'lere yapılan çağrıların güvenli olmasını sağlamaz. Projedeki tek şey bir yükleme süresi hatasını arama zamanı hatasına ertelemektir. Bunun avantajı, bu aramayı çalışma zamanında koruyabilir ve hatta alternatif bir uygulama ya da uygulamanın bu özelliğinin kullanıcılara veya bu kod yolundan tamamen kaçınabilir.
Clang, güvenlik önlemi yaptığınızda uyarı (unguarded-availability
) yayınlayabilir
uygulamanızın minSdkVersion
cihazında kullanılamayan bir API'ye çağrı. Eğer
veya CMake araç zinciri dosyamızı kullanırsanız bu uyarı otomatik olarak
ve bu özellik etkinleştirilirken hataya yükseltildi.
Aşağıdaki örnekte, API'yi rastgele kullanan
bu özellik, dlopen()
ve dlsym()
kullanılarak etkinleştirildi:
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);
}
}
Okuması biraz karmaşık. Fonksiyon adları yineleniyor (ve
C, imzaları da yazın) der, o zaman başarılı bir şekilde
geçen fonksiyon adında yazım hatası yaparsanız çalışma zamanında yedeği alır
dlsym
olarak ayarlıyorsunuz ve her API için bu kalıbı kullanmanız gerekiyor.
Zayıf API referansları kullanıldığında yukarıdaki işlev şu şekilde yeniden yazılabilir:
void LogImageDecoderResult(int result) {
if (__builtin_available(android 31, *)) {
LOG(INFO) << AImageDecoder_resultToString(result);
} else {
LOG(INFO) << "cannot stringify result: " << result;
}
}
Gelişmiş seçenekler, __builtin_available(android 31, *)
aramaları
android_get_device_api_level()
, sonucu önbelleğe alır ve 31
ile karşılaştırır
(AImageDecoder_resultToString()
'ı kullanıma sunan API düzeyidir).
__builtin_available
için hangi değerin kullanılacağını belirlemenin en basit yolu
güvenlik duvarı veya güvenlik açığı
__builtin_available(android 1, *)
) ve hata mesajında belirtilen şeyi uygulayın.
Örneğin, AImageDecoder_createFromAAsset()
için güvenlik açığı içeren bir çağrı,
minSdkVersion 24
şunu üretecek:
error: 'AImageDecoder_createFromAAsset' is only available on Android 30 or newer [-Werror,-Wunguarded-availability]
Bu durumda, arama __builtin_available(android 30, *)
tarafından korunmalıdır.
Yapı hatası yoksa
minSdkVersion
ve koruma gerekmez ya da derlemeniz yanlış yapılandırılmıştır.
unguarded-availability
uyarısı devre dışı bırakıldı.
Alternatif olarak, NDK API referansında şöyle bir şey söylenebilir: "API 30'da kullanıma sunuldu" tıklayın. Böyle bir metin yoksa bu, API, desteklenen tüm API düzeylerinde kullanılabilir.
API korumalarının tekrarından kaçınma
Bunu kullanıyorsanız uygulamanızda muhtemelen
yalnızca yeterli sayıda yeni cihazda kullanılabilir. Aynı bilgileri tekrarlamak yerine
__builtin_available()
her bir fonksiyonunuzu kontrol ediyorsa
kendi kodunuzu belirli bir API düzeyi gerektiriyor. Örneğin, ImageDecoder API'leri
kendileri API 30'a eklendiğinden, bu kodların yoğun bir şekilde kullanıldığı işlevler için
Yapabileceğiniz API'ler:
#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();
}
}
İlginç API korumaları
Clang, __builtin_available
'nin nasıl kullanıldığı konusunda son derece özeldir. Yalnızca düz bir değer
(ancak büyük olasılıkla makroyla değiştirilmiştir) if (__builtin_available(...))
çalışır. Eşit
if (!__builtin_available(...))
gibi basit işlemler çalışmaz (Clang
hem de unsupported-availability-guard
uyarısı verir.
unguarded-availability
). Bu, Clang'in gelecekteki bir sürümünde iyileştirilebilir. Görüntüleyin
LLVM Sorunu 33161 başlıklı makaleyi inceleyin.
unguarded-availability
kontrolleri yalnızca
bu düzenlemelerin nedenlerinden biri. Clang, API çağrısına sahip işlev şu durumda olsa bile uyarı verir:
hiçbir zaman sadece korumalı bir kapsam içinden çağrılmaz. Garsonların tekrarlanmasını önlemek için
API korumalarının tekrarlanmasını önleme bölümüne bakın.
Bu neden varsayılan ayar değil?
Doğru kullanılmadığı sürece güçlü API referansları ile zayıf API arasındaki fark ilkinin hızla ve bariz bir şekilde başarısız olacağı, ancak kullanıcı, eksik API'ye neden olan bir işlem yapana kadar ikincisi başarısız olmaz. çağrılması gerekir. Bu durumda, hata mesajı net bir şekilde derleme zamanı "AFoo_bar() kullanılamıyor" bu bir hata olacaktır. Entegre güçlü referanslar varsa, hata mesajı çok daha nettir ve varsayılan olarak daha güvenlidir.
Bu yeni bir özellik olduğu için, kodu kullanmak için mevcut güvenli bir şekilde açıklayacağım. Android düşünülerek yazılmamış üçüncü taraf kod bu sorunu her zaman yaşayabilir; bu nedenle varsayılan davranışı tercih edebilirsiniz.
Daha fazla sorun oluşturacağı için bunu kullanmanızı öneririz ancak tespit edilmesi ve hatalarının ayıklanması zorsa bu riskleri bilginiz olmadan değişen davranışlardan daha iyidir.
Uyarılar
Bu özellik çoğu API'de işe yarar ancak iş yeri.
Sorunlu olabilecek en düşük tür, daha yeni libc API'leridir. Buradaki diğer
Android API'leri (başlıklarda #if __ANDROID_API__ >= X
ile korunur)
hem de __INTRODUCED_IN(X)
değil. Bu, zayıf beyanın bile
görünür. API düzeyindeki en eski modern NDK desteği r21 olduğundan,
yaygın olarak ihtiyaç duyulan libc API'leri mevcuttur. Her biri için yeni libc API'leri eklenir
(status.md öğesine bakın) ancak ne kadar yeni olursa
yalnızca az sayıda geliştiricinin ihtiyaç duyacağı uç bir örnek olacaktır. Bununla birlikte, paydaşlardan biri
bu geliştiricileri çağırmak için şimdilik dlsym()
uygulamasını kullanmaya devam etmeniz
minSdkVersion
değeri API'den daha eskiyse API'leri etkinleştirin. Bu, çözülebilir bir problemdir.
Ancak bunu yapmak, tüm uygulamalar (tüm uygulamalar) için kaynak uyumluluğunu
libc API'lerinin polyfill'lerini içeren kod
libc ve yerel beyanlarda eşleşmeyen availability
özellikleri), bu nedenle
ya da ne zaman düzelteceğimizden emin değiliz.
Daha fazla geliştiriciyle karşılaşma olasılığı yüksek olan kitaplık,
içeren yeni API, minSdkVersion
değerinizden daha yeni. Yalnızca bu özellik
zayıf sembol referanslarını mümkün kılar; kütüphanenin zayıf olması gibi bir şey yoktur
bir referans noktası olarak kabul edilir. Örneğin, minSdkVersion
değeri 24 ise
libvulkan.so
ve vkBindBufferMemory2
adlı kişiye güvenlikli bir arama yap, çünkü
libvulkan.so
, API 24 ile başlayan cihazlarda kullanılabilir. Diğer yandan
minSdkVersion
metriğiniz 23 ise dlopen
ve dlsym
uygulamalarına geri dönmeniz gerekir
Çünkü kitaplık, yalnızca şu sürümleri destekleyen cihazlarda yer almaz:
API 23. Bu durumu düzeltmek için iyi bir çözüm bilmiyoruz ama uzun vadede
artık kendiliğinden çözülecektir, çünkü (mümkün olduğunda) bundan böyle yeni
Yeni kitaplık oluşturma API'leri.
Kütüphane yazarları için
Android uygulamalarında kullanılmak üzere bir kitaplık geliştiriyorsanız
bu özelliği herkese açık başlıklarınızda kullanmaktan kaçının. Güvenle kullanabileceğiniz yerler şunlardır:
olabilir, ancak hesabınızdaki herhangi bir kodda __builtin_available
kullanırsanız
satır içi fonksiyonlar veya şablon tanımları gibi başlıklar kullanarak tüm öğelerinizi
tüketicilerin bu özelliği etkinleştirmesine yardımcı olur. Aynı nedenlerden dolayı bunu
varsayılan olarak NDK'da kullanmazsanız, müşteriniz adına bu seçimi yapmaktan kaçınmalısınız.
yardımcı olur.
Herkese açık başlıklarınızda bu davranışa ihtiyaç duyarsanız hem de kullanıcılarınızın bu özelliği etkinleştirmeleri gerektiğini bilmelerini ve farkında olmanız gerekir.