A distribuição de middleware criado com o NDK gera outros problemas com os quais os desenvolvedores de apps não precisam se preocupar. As bibliotecas pré-compiladas impõem aos usuários algumas das opções de implementação delas.
Como escolher níveis de API e versões do NDK
Seus usuários não podem usar uma minSdkVersion anterior à usada por você. Se os apps dos usuários precisam ser executados na API de nível 21, não é possível criar para a de nível 24. Não há problema em criar uma biblioteca para um nível de API anterior ao dos usuários. Você pode criar para a API de nível 16 e manter a compatibilidade com os usuários de nível 21.
As versões do NDK são amplamente compatíveis umas com as outras, mas às vezes ocorrem mudanças que corrompem a compatibilidade. Se você sabe que todos os seus usuários estão usando a mesma versão do NDK, é melhor usar essa versão. Caso contrário, use a versão mais recente.
Como usar a STL
Se você estiver programando em C++ e usando a STL, a escolha entre libc++_shared
e
libc++_static
afetará seus usuários caso você distribua uma biblioteca compartilhada. Se esse for o caso,
você precisará usar libc++_shared
ou garantir que
os símbolos libc++ não sejam expostos pela biblioteca. A melhor maneira de fazer isso é declarando
explicitamente a superfície da ABI com um script de versão. Isso também ajuda a manter
os detalhes de implementação particulares. Por exemplo, uma biblioteca aritmética simples
pode ter o seguinte script de versão:
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:
*;
};
Um script de versão precisa ser a opção preferencial porque é a maneira mais robusta de controlar a visibilidade do símbolo. Essa é uma prática recomendada para todas as bibliotecas compartilhadas, middleware ou não, porque impede que os detalhes de implementação sejam expostos e melhora o tempo de carregamento.
Outra opção menos robusta é usar -Wl,--exclude-libs,libc++_static.a
-Wl,--exclude-libs,libc++abi.a
na vinculação. Ela é menos robusta porque
oculta apenas os símbolos nas bibliotecas com nome explícito e
nenhum diagnóstico é relatado para bibliotecas que não são usadas.
Um erro de digitação no nome da
biblioteca não é um erro, e fica a cargo do usuário manter a lista de bibliotecas
atualizada. Essa abordagem também não oculta os detalhes de implementação.
Como distribuir bibliotecas nativas em AARs
O Plug-in do Android para Gradle pode importar dependências nativas distribuídas em AARs. Se os usuários estiverem usando o Plug-in do Android para Gradle (AGP, na sigla em inglês), essa será a maneira mais fácil para eles consumirem sua biblioteca.
As bibliotecas nativas podem ser empacotadas em um AAR pelo AGP. Essa será a opção mais fácil caso sua biblioteca já tenha sido criada pelo externalNativeBuild.
Os builds que não são do AGP podem usar ndkports ou executar empacotamento manual seguindo a
documentação da Prefab (link em inglês) para criar o subdiretório prefab/
do AAR.
Middleware Java com bibliotecas JNI
As bibliotecas Java que incluem bibliotecas JNI (ou seja, AARs que contêm jniLibs
)
precisam ter cuidado para que as bibliotecas JNI inclusas não entrem em conflito
com outras bibliotecas no app do usuário. Por exemplo, se o AAR incluir
libc++_shared.so
, mas for uma versão diferente da usada pelo app,
apenas uma será instalada no APK e poderá levar a um comportamento
não confiável.
A solução mais confiável é as bibliotecas Java não incluírem mais do que uma
biblioteca JNI. Essa também é uma boa recomendação para apps. Todas as dependências, incluindo a STL,
serão vinculadas estaticamente à biblioteca de implementação, e um script de versão
precisará ser usado para aplicar a plataforma da ABI. Por exemplo, uma biblioteca Java
com.example.foo
que inclua a biblioteca JNI libfooimpl.so
precisará usar o
seguinte script de versão:
LIBFOOIMPL {
global:
JNI_OnLoad;
local:
*;
};
Esse exemplo usa registerNatives
via JNI_OnLoad
, conforme descrito em Dicas de JNI
para garantir que a plataforma de ABI mínima seja exposta e o tempo de carregamento da biblioteca seja
minimizado.