Советы поставщикам промежуточного программного обеспечения

Распространение промежуточного программного обеспечения, созданного с помощью NDK, вызывает некоторые дополнительные проблемы, о которых разработчикам приложений не нужно беспокоиться. Готовые библиотеки навязывают пользователям некоторые варианты реализации.

Выбор уровней API и версий NDK

Ваши пользователи не могут использовать версию minSdkVersion ниже вашей. Если приложения ваших пользователей должны работать на API 21, вы не можете создавать их для API 24. Можно создавать библиотеку для более низкого уровня API, чем у ваших пользователей. Вы можете создавать приложения для API 16 и оставаться совместимыми с пользователями API 21.

Версии NDK в значительной степени совместимы друг с другом, но иногда происходят изменения, нарушающие совместимость. Если вы знаете, что все ваши пользователи используют одну и ту же версию NDK, лучше всего использовать ту же версию, что и они. В противном случае используйте новейшую версию.

Использование STL

Если вы пишете C++ и используете STL, ваш выбор между libc++_shared и libc++_static повлияет на ваших пользователей, если вы распространяете общую библиотеку. Если вы распространяете общую библиотеку, вы должны либо использовать libc++_shared , либо гарантировать, что символы libc++ не будут доступны вашей библиотеке. Лучший способ сделать это — явно объявить поверхность ABI с помощью сценария версии (это также помогает сохранить конфиденциальность деталей реализации). Например, простая арифметическая библиотека может иметь следующую версию сценария:

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:
    *;
};

Сценарий версии должен быть предпочтительным вариантом, поскольку это наиболее надежный способ управления видимостью символов. Это лучшая практика для всех общих библиотек, независимо от того, промежуточное программное обеспечение или нет, поскольку оно предотвращает раскрытие деталей вашей реализации и сокращает время загрузки.

Другой, менее надежный вариант — использовать -Wl,--exclude-libs,libc++_static.a -Wl,--exclude-libs,libc++abi.a при связывании. Это менее надежно, поскольку при этом скрываются только символы в библиотеках, которым явно присвоены имена, а для неиспользуемых библиотек не выдается диагностическая информация (опечатка в имени библиотеки не является ошибкой, и пользователь должен выполнить эту задачу самостоятельно). поддерживать список библиотеки в актуальном состоянии). Этот подход также не скрывает ваши собственные детали реализации.

Распространение собственных библиотек в AAR

Плагин Android Gradle может импортировать собственные зависимости, распространяемые в AAR . Если ваши пользователи используют плагин Android Gradle, для них это будет самый простой способ использовать вашу библиотеку.

Собственные библиотеки могут быть упакованы в AAR с помощью AGP . Это будет самый простой вариант, если ваша библиотека уже собрана с помощью externalNativeBuild .

Сборки без AGP могут использовать ndkports или выполнять упаковку вручную, следуя документации Prefab , чтобы создать подкаталог prefab/ их AAR.

Промежуточное программное обеспечение Java с библиотеками JNI

Библиотеки Java, включающие библиотеки JNI (другими словами, AAR, содержащие jniLibs ), должны быть осторожны, чтобы включенные в них библиотеки JNI не конфликтовали с другими библиотеками в пользовательском приложении. Например, если AAR включает libc++_shared.so , но версию libc++_shared.so отличную от той, которую использует приложение, в APK будет установлена ​​только одна версия, и это может привести к ненадежному поведению.

Наиболее надежным решением является включение в библиотеки Java не более одной библиотеки JNI (это хороший совет и для приложений). Все зависимости, включая STL, должны быть статически связаны с библиотекой реализации, а для обеспечения реализации поверхности ABI следует использовать сценарий версии. Например, библиотека Java com.example.foo , включающая библиотеку JNI libfooimpl.so должна использовать следующую версию сценария:

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

В этом примере используется registerNatives через JNI_OnLoad , как описано в разделе «Советы по JNI», чтобы обеспечить доступность минимальной поверхности ABI и минимизировать время загрузки библиотеки.