Consulenza per fornitori di middleware

La distribuzione del middleware creato con l'NDK solleva alcuni problemi aggiuntivi di cui gli sviluppatori di app non devono preoccuparsi. Le librerie predefinite applicano alcune delle scelte di implementazione agli utenti.

Scelta dei livelli API e delle versioni NDK

I tuoi utenti non possono utilizzare un valore minSdkVersion inferiore al tuo. Se le app degli utenti devono essere eseguite sull'API 21, non puoi creare per l'API 24. Puoi creare la tua libreria per un livello API inferiore rispetto a quello degli utenti. Puoi creare per l'API 16 e mantenere la compatibilità con gli utenti dell'API 21.

Le versioni NDK sono per lo più compatibili tra loro, ma a volte ci sono modifiche che danneggiano la compatibilità. Se sai che tutti i tuoi utenti utilizzano la stessa versione di NDK, ti consigliamo di utilizzare la stessa versione. In caso contrario, utilizza la versione più recente.

Uso del codice STL

Se stai scrivendo in C++ e utilizzando il codice STL, la tua scelta tra libc++_shared e libc++_static inciderà sui tuoi utenti se distribuisci una libreria condivisa. Se distribuisci una libreria condivisa, devi usare libc++_shared o assicurarti che i simboli di libc++ non siano esposti alla libreria. Il modo migliore per farlo è dichiarare esplicitamente la tua piattaforma ABI con uno script di versione (questo consente anche di mantenere privati i dettagli di implementazione). Ad esempio, una semplice libreria aritmetica potrebbe avere il seguente script di versione:

LIBMYMATH {
global:
    add;
    sub;
    mul;
    div;
    # C++ symbols in an extern block will be mangled automatically. See
    # https://stackoverflow.com/a/21845178/632035 for more examples.
    extern "C++" {
        "pow(int, int)";
    }
local:
    *;
};

Uno script di versione dovrebbe essere l'opzione preferita perché è il modo più affidabile per controllare la visibilità dei simboli. Questa è una best practice per tutte le librerie condivise, il middleware o meno, in quanto impedisce la visualizzazione dei dettagli dell'implementazione e migliora il tempo di caricamento.

Un'altra opzione meno efficace è utilizzare -Wl,--exclude-libs,libc++_static.a -Wl,--exclude-libs,libc++abi.a durante il collegamento. Questa soluzione è meno efficace perché nasconderà solo i simboli nelle librerie con nomi espliciti e non verrà segnalata alcuna diagnostica per le librerie che non vengono utilizzate (un errore ortografico nel nome della libreria non è un errore e l'utente è responsabile di mantenere aggiornato l'elenco delle librerie). Inoltre, questo approccio non nasconde i dettagli dell'implementazione.

Distribuzione di librerie native in AAR

Il plug-in Android per Gradle può importare dipendenze native distribuite in AAR. Se i tuoi utenti utilizzano il plug-in Android per Gradle, questo sarà il modo più semplice per consumare la tua libreria.

Le librerie native possono essere pacchettizzate in un AAR da AGP. Questa sarà l'opzione più semplice se la tua libreria è già stata creata da external NativeBuild.

Le build non AGP possono utilizzare ndkports o eseguire la pacchettizzazione manuale seguendo la documentazione Prefabbricata per creare la sottodirectory prefab/ del relativo AAR.

Middleware Java con librerie JNI

Le librerie Java che includono librerie JNI (in altre parole, AAR che contengono jniLibs) devono fare attenzione che le librerie JNI incluse non collidano altre librerie nell'app dell'utente. Ad esempio, se l'AAR include libc++_shared.so, ma una versione di libc++_shared.so diversa da quella utilizzata dall'app, ne verrà installata solo una nell'APK e ciò potrebbe comportare un comportamento inaffidabile.

La soluzione più affidabile è che le librerie Java includano non più di una libreria JNI (questo è un buon consiglio anche per le app). Tutte le dipendenze, incluso il codice STL, devono essere collegate in modo statico alla libreria di implementazione ed è necessario utilizzare uno script di versione per applicare la piattaforma ABI. Ad esempio, una libreria Java com.example.foo che include la libreria JNI libfooimpl.so deve utilizzare il seguente script di versione:

LIBFOOIMPL {
global:
    JNI_OnLoad;
local:
    *;
};

Questo esempio utilizza registerNatives tramite JNI_OnLoad come descritto in Suggerimenti JNI per garantire che la superficie ABI minima sia esposta e che il tempo di caricamento della libreria sia ridotto al minimo.