Problemi e soluzioni comuni

Questo documento è un elenco parziale dei problemi più comuni che potresti riscontrare quando utilizzi l'NDK e delle relative soluzioni (se disponibili).

Utilizzare _FILE_OFFSET_BITS=64 con livelli API precedenti

Prima delle intestazioni unificate, l'NDK non supportava _FILE_OFFSET_BITS=64. Se lo hai definito durante la creazione dell'app, è stato ignorato. L'opzione _FILE_OFFSET_BITS=64 è ora supportata con le intestazioni unificate, ma nelle versioni precedenti di Android erano disponibili pochissime API off_t come variante off64_t. Di conseguenza, l'utilizzo di questa funzionalità con i livelli API precedenti comporta la disponibilità di un numero inferiore di funzioni.

Questo problema è spiegato in dettaglio nel post del blog r16 e nella documentazione di Bionic.

Problema: la build richiede API che non esistono in minSdkVersion.

Soluzione: disattiva _FILE_OFFSET_BITS=64 o aumenta minSdkVersion.

Definizione implicita o non dichiarata di mmap

Potresti visualizzare il seguente errore in C++:

error: use of undeclared identifier 'mmap'

o il seguente errore in C:

warning: implicit declaration of function 'mmap' is invalid in C99

L'utilizzo di _FILE_OFFSET_BITS=64 indica alla libreria C di utilizzare mmap64 anziché mmap. mmap64 non era disponibile fino ad android-21. Se il valore di minSdkVersion è inferiore a 21, la libreria C non contiene un mmap compatibile con _FILE_OFFSET_BITS=64, quindi la funzione non è disponibile.

minSdkVersion impostato su un valore superiore al livello API del dispositivo

Il livello API su cui esegui la build con l'NDK ha un significato molto diverso da compileSdkVersion per Java. Il livello API dell'NDK è il livello API minimo supportato dall'app. In ndk-build, questa è l'impostazione APP_PLATFORM. Con CMake, è -DANDROID_PLATFORM.

Poiché i riferimenti alle funzioni vengono in genere risolti quando le librerie vengono caricate anziché quando vengono chiamate per la prima volta, non puoi fare riferimento alle API che non sono sempre presenti e proteggere il loro utilizzo con i controlli del livello API. Se vengono chiamate, devono essere presenti.

Problema: il livello API dell'NDK è superiore all'API supportata dal dispositivo.

Soluzione: imposta il livello API dell'NDK (APP_PLATFORM) sulla versione minima di Android supportata dall'app.

Sistema di compilazione Impostazione
ndk-build APP_PLATFORM
CMake ANDROID_PLATFORM
externalNativeBuild android.minSdkVersion

Per altri sistemi di compilazione, vedi Utilizzare l'NDK con altri sistemi di compilazione.

Impossibile individuare i simboli __aeabi

Il seguente messaggio:

UnsatisfiedLinkError: dlopen failed: cannot locate symbol "__aeabi_memcpy"

è un esempio di possibili errori di runtime. Questi errori vengono visualizzati nel log quando tenti di caricare le librerie native. Il simbolo può essere uno qualsiasi di __aeabi_*; __aeabi_memcpy e __aeabi_memclr sembrano essere i più comuni.

Questo problema è documentato in Problema 126

Impossibile individuare il simbolo rand

Per il seguente messaggio di log degli errori:

UnsatisfiedLinkError: dlopen failed: cannot locate symbol "rand"

Consulta questa risposta dettagliata di Stack Overflow answer.

Riferimento non definito a __atomic_*

Problema: alcune ABI richiedono libatomic per fornire alcune implementazioni per operazioni atomiche.

Soluzione: aggiungi -latomic durante il collegamento.

Per il seguente messaggio di errore:

error: undefined reference to '__atomic_exchange_4'

Il simbolo effettivo qui potrebbe essere qualsiasi elemento con il prefisso __atomic_.

RTTI/eccezioni non funzionanti tra i limiti delle librerie

Problema: le eccezioni non vengono rilevate quando vengono generate tra i limiti delle librerie condivise o dynamic_cast non riesce.

Soluzione: aggiungi una funzione chiave ai tuoi tipi. Una funzione chiave è la prima funzione virtuale non pura e out-of-line per un tipo. Per un esempio, consulta la discussione sul problema 533.

L'ABI C++ indica che due oggetti hanno lo stesso tipo se e solo se i relativi puntatori type_info sono identici. Le eccezioni possono essere rilevate solo se type_info per il catch corrisponde all'eccezione generata. La stessa regola si applica a dynamic_cast.

Quando un tipo non ha una funzione chiave, il relativo typeinfo viene emesso come simbolo debole e le informazioni sul tipo corrispondenti vengono unite quando le librerie vengono caricate. Quando le librerie vengono caricate dinamicamente dopo il caricamento dell'eseguibile (in altre parole, tramite dlopen o System.loadLibrary), potrebbe non essere possibile per il caricatore unire le informazioni sul tipo per le librerie caricate. In questo caso, i due tipi non vengono considerati uguali.

Utilizzare librerie predefinite non corrispondenti

L'utilizzo di librerie predefinite, in genere librerie di terze parti, nella tua applicazione richiede un po' di attenzione in più. In generale, tieni presente le seguenti regole:

  • Il livello API minimo dell'app risultante è il massimo dei valori minSdkVersion di tutte le librerie dell'app.

    Se minSdkVersion è 16, ma utilizzi una libreria predefinita creata per 21, il livello API minimo dell'app risultante è 21. Il mancato rispetto di questa regola sarà visibile in fase di tempo di compilazione se la libreria predefinita è statica, ma potrebbe non essere visualizzata fino al runtime per le librerie condivise predefinite.

  • Tutte le librerie devono essere generate con la stessa versione dell'NDK.

    Questa regola è un po' più flessibile della maggior parte, poiché le interruzioni sono rare, ma la compatibilità tra le librerie create con versioni principali diverse dell'NDK non è garantita. L'ABI C++ non è stabile ed è cambiata in passato.

  • Le app con più librerie condivise devono utilizzare una shared STL.

    Come per le STL non corrispondenti, i problemi causati da questa situazione possono essere evitati se si presta molta attenzione, ma è meglio evitare il problema. Il modo migliore per evitare questo problema è evitare di avere più librerie condivise nell'app.