1. Como fazer referência a arquivos AAR no Android.mk
No desenvolvimento Android, muitas vezes é necessário usar bibliotecas de terceiros, que geralmente são fornecidas na forma de AAR ou JAR. Neste artigo, detalharemos como fazer referência a arquivos AAR em arquivos Android.mk.
Introdução ao arquivo AAR
Os arquivos AAR (Android Archive) são o formato de empacotamento dos projetos de biblioteca Android. Eles incluem classes Java, arquivos de recursos, arquivos de manifesto e arquivos de biblioteca local opcionais. Os arquivos AAR são uma maneira conveniente de distribuir bibliotecas Android porque contêm todos os recursos e códigos exigidos pela biblioteca.
Faça referência a arquivos AAR em Android.mk
Para referenciar um arquivo AAR no Android.mk, você precisa usar LOCAL_STATIC_JAVA_AAR_LIBRARIES
variáveis para especificar o alias do arquivo AAR, depois usar LOCAL_PREBUILT_STATIC_JAVA_LIBRARIES
variáveis para especificar a localização do arquivo AAR e, finalmente, usá-las include $(BUILD_MULTI_PREBUILT)
para compilar o arquivo AAR.
Se o arquivo AAR contiver um arquivo de biblioteca local (.so), você também precisará LOCAL_JNI_SHARED_LIBRARIES
especificar o nome do módulo do arquivo de biblioteca local na variável e LOCAL_MULTILIB
especificar o número de bits (32 ou 64) do arquivo de biblioteca local na variável .
Se o arquivo AAR contiver outros arquivos JAR, você também precisará LOCAL_STATIC_JAVA_LIBRARIES
especificar o alias do arquivo JAR na variável e LOCAL_PREBUILT_STATIC_JAVA_LIBRARIES
o local do arquivo JAR na variável.
Aqui está um exemplo detalhado de um Android.mk referenciando um arquivo AAR:
# 定义当前模块的相对路径
LOCAL_PATH := $(call my-dir)
# 清除一些变量的值,但是LOCAL_PATH除外
include $(CLEAR_VARS)
# 指定当前待编译模块的名称
LOCAL_MODULE := my-app
# 指定当前模块的源文件
LOCAL_SRC_FILES := MainActivity.java
# 指定当前模块依赖的aar文件的别名
LOCAL_STATIC_JAVA_AAR_LIBRARIES := my-lib
# 指定当前模块依赖的其他模块
LOCAL_SHARED_LIBRARIES := liblog
# 指定当前模块的编译选项
LOCAL_CFLAGS := -Wall -Werror
# 指定当前模块的链接选项
LOCAL_LDFLAGS := -Wl,-z,nocopyreloc
# 指定当前模块的目标类型,可以是静态库、动态库、可执行文件或者Java库等
include $(BUILD_PACKAGE)
# 声明aar文件的位置
include $(CLEAR_VARS)
LOCAL_PREBUILT_STATIC_JAVA_LIBRARIES += my-lib:libs/my-lib.aar
include $(BUILD_MULTI_PREBUILT)
# 如果aar文件包含了本地库文件(.so),还需要在下面声明本地库文件的模块名、源文件和路径,例如:
include $(CLEAR_VARS)
LOCAL_MODULE_TAGS := optional
LOCAL_MODULE_CLASS := SHARED_LIBRARIES
LOCAL_MODULE_SUFFIX := .so
LOCAL_MODULE := libmy-lib
LOCAL_SRC_FILES := libs/armeabi-v7a/libmy-lib.so
LOCAL_MODULE_PATH := $(TARGET_OUT)/lib
include $(BUILD_PREBUILT)
Gerar arquivo AAR
Se quiser gerar um arquivo AAR, você pode compilar o arquivo JAR primeiro e depois compilar o arquivo AAR. Aqui está um exemplo:
make out/target/common/obj/JAVA_LIBRARIES/YourLibName_intermediates/javalib.jar
make out/target/common/obj/JAVA_LIBRARIES/YourLibName_intermediates/javalib.aar
Após executar o comando acima, você pode out/target/common/obj/JAVA_LIBRARIES/YourLibName_intermediates/
encontrar o arquivo AAR gerado no diretório.
2. Métodos para referenciar e gerar arquivos SO em Android.mk
No desenvolvimento Android, bibliotecas de terceiros são frequentemente usadas, e essas bibliotecas geralmente são fornecidas na forma de SO (Shared Object). Neste artigo, detalharemos como referenciar e gerar arquivos SO em arquivos Android.mk.
Introdução aos arquivos SO
Os arquivos SO são arquivos de biblioteca de vínculo dinâmico, que podem ser compartilhados por vários aplicativos e também podem conter código local e outros arquivos SO. Um arquivo SO é uma maneira conveniente de distribuir uma biblioteca Android porque contém todos os recursos e códigos exigidos pela biblioteca.
Gere arquivo SO em Android.mk
Para gerar um arquivo SO no Android.mk, você precisa usar LOCAL_SRC_FILES
variáveis para especificar o arquivo de origem, depois usar LOCAL_MODULE
variáveis para especificar o nome do módulo e, finalmente, usá-las include $(BUILD_SHARED_LIBRARY)
para compilar o arquivo SO.
@lib maneira de referenciar arquivos SO
@lib
O método é um método que especifica como carregar o arquivo SO no apk quando o apk está em execução. Ele pode evitar a operação de descompactar o arquivo SO no apk e copiá-lo para o diretório lib. No entanto, se houver vários arquivos SO no apk e nem todos os arquivos SO suportarem a mesma plataforma (como arm64-v8a ou armeabi-v7a), o método pode fazer com que o arquivo SO correspondente não seja encontrado e um erro @lib
ocorrerá ser relatado.
Para resolver este problema, existem duas maneiras possíveis:
- Método 1: Especifique em Android.mk apenas as plataformas suportadas para compilação, por exemplo:
ifeq ($(TARGET_ARCH), arm)
LOCAL_PREBUILT_JNI_LIBS := \
@lib/armeabi-v7a/liba.so \
@lib/armeabi-v7a/libb.so
else ifeq ($(TARGET_ARCH),arm64)
LOCAL_PREBUILT_JNI_LIBS := \
@lib/arm64-v8a/liba.so \
@lib/arm64-v8a/libb.so
endif
LOCAL_PREBUILT_JNI_LIBS
Método 2: Use variáveis em Android.mk para especificarLOCAL_PATH
o caminho relativo ao arquivo SO em vez de usar@
símbolos, por exemplo:
LOCAL_PREBUILT_JNI_LIBS := \
lib/arm64-v8a/liba.so \
lib/arm64-v8a/libb.so
Faça referência a arquivos SO em Android.mk
Para referenciar um arquivo SO no Android.mk, você precisa usar LOCAL_SHARED_LIBRARIES
variáveis para especificar o nome do módulo do arquivo SO, depois usar LOCAL_PREBUILT_JNI_LIBS
variáveis para especificar a localização do arquivo SO e, finalmente, usá-las include $(BUILD_PREBUILT)
para compilar o arquivo SO.
Se o arquivo SO for de 32 ou 64 bits, você também precisará LOCAL_MULTILIB
especificar o número de bits na variável, por exemplo:
LOCAL_MULTILIB := 32
ou
LOCAL_MULTILIB := 64
Aqui está um exemplo detalhado de um Android.mk referenciando um arquivo SO:
# 定义当前模块的相对路径
LOCAL_PATH := $(call my-dir)
# 清除一些变量的值,但是LOCAL_PATH除外
include $(CLEAR_VARS)
# 指定当前待编译模块的名称
LOCAL_MODULE := my-app
# 指定当前模块的源文件
LOCAL_SRC_FILES := MainActivity.java
# 指定当前模块依赖的so文件的模块名
LOCAL_SHARED_LIBRARIES := libtest1 libtest2 libtest3
# 指定当前模块的编译选项
LOCAL_CFLAGS := -Wall -Werror
# 指定当前模块的链接选项
LOCAL_LDFLAGS := -Wl,-z,nocopyreloc
# 指定当前模块的目标类型,可以是静态库、动态库、可执行文件或者Java库等
include $(BUILD_PACKAGE)
# 声明so文件的位置,有两种方式:
# 方式一:使用LOCAL_PREBUILT_JNI_LIBS变量,指定so文件相对于LOCAL_PATH的路径,例如:
include $(CLEAR_VARS)
LOCAL_PREBUILT_JNI_LIBS := \
lib/arm64-v8a/libtest1.so \
lib/arm64-v8a/libtest2.so \
lib/arm64-v8a/libtest3.so
include $(BUILD_MULTI_PREBUILT)
# 方式二:使用LOCAL_MODULE和LOCAL_SRC_FILES变量,指定so文件的模块名和源文件,然后使用include $(BUILD_PREBUILT)来编译so文件,例如:
include $(CLEAR_VARS)
LOCAL_MODULE_TAGS := optional
LOCAL_MODULE_CLASS := SHARED_LIBRARIES
LOCAL_MODULE_SUFFIX := .so
LOCAL_MODULE := libtest1
LOCAL_SRC_FILES := libs/armeabi-v7a/libtest1.so
LOCAL_MODULE_PATH := $(TARGET_OUT)/lib
include $(BUILD_PREBUILT)
include $(CLEAR_VARS)
LOCAL_MODULE_TAGS := optional
LOCAL_MODULE_CLASS := SHARED
_LIBRARIES
LOCAL_MODULE_SUFFIX := .so
LOCAL_MODULE := libtest2
LOCAL_SRC_FILES := libs/armeabi-v7a/libtest2.so
LOCAL_MODULE_PATH := $(TARGET_OUT)/lib
include $(BUILD_PREBUILT)
include $(CLEAR_VARS)
LOCAL_MODULE_TAGS := optional
LOCAL_MODULE_CLASS := SHARED_LIBRARIES
LOCAL_MODULE_SUFFIX := .so
LOCAL_MODULE := libtest3
LOCAL_SRC_FILES := libs/armeabi-v7a/libtest3.so
LOCAL_MODULE_PATH := $(TARGET_OUT)/lib
include $(BUILD_PREBUILT)
Como lidar com vários arquivos SO em um APK
. No desenvolvimento Android, um APK pode conter vários arquivos SO, e esses arquivos SO podem ser distribuídos em diretórios diferentes. Pode ser problemático processar esses arquivos SO. No entanto, existem alguns métodos que você pode usar para simplificar esse processo.
Método 1: use instruções de loop para descompactar automaticamente todos os arquivos SO
No Android.mk, você pode usar uma instrução de loop para iterar por todos os arquivos SO no APK e usar LOCAL_PREBUILT_JNI_LIBS
variáveis para especificar LOCAL_PATH
o caminho relativo ao arquivo SO. Aqui está um exemplo:
# 清空临时变量JNI_LIBS
JNI_LIBS :=
# 当前目录递归搜索
$(foreach FILE,$(shell find $(LOCAL_PATH)/lib/ -name *.so), $(eval JNI_LIBS += $(FILE)))
# 获取搜索文件目录集(相对目录)
LOCAL_PREBUILT_JNI_LIBS := $(subst $(LOCAL_PATH),,$(JNI_LIBS))
Desta forma, todos os arquivos SO do APK podem ser descompactados automaticamente.
Método 2: use o símbolo @ para evitar a descompactação de todos os arquivos SO
No Android.mk, você também pode usar @
símbolos para especificar que o arquivo SO no APK será carregado quando o APK estiver em execução, sem descompactá-lo. Aqui está um exemplo:
ifeq ($(TARGET_ARCH), arm)
LOCAL_PREBUILT_JNI_LIBS := \
@lib/armeabi-v7a/liba.so \
@lib/armeabi-v7a/libb.so \
...
else ifeq ($(TARGET_ARCH),arm64)
LOCAL_PREBUILT_JNI_LIBS := \
@lib/arm64-v8a/liba.so \
@lib/arm64-v8a/libb.so \
...
endif
Desta forma, você pode evitar a descompactação de todos os arquivos SO no APK.
A diferença entre usar o símbolo @ e não usar o símbolo @
No Android.mk, existem algumas diferenças entre usar @
símbolos e não usar símbolos:@
- O uso
@
de símbolos pode melhorar o desempenho porque não há necessidade de descompactar e copiar arquivos SO e também pode economizar espaço porque não há necessidade de armazenar arquivos SO redundantes. - Não usar
@
símbolos pode melhorar a compatibilidade, porque os arquivos SO correspondentes podem ser fornecidos para diferentes plataformas sem considerar se há arquivos SO para as plataformas correspondentes no APK.
3. APK predefinido, arquivos SO e assinaturas em Android.mk
No desenvolvimento de sistemas Android, muitas vezes é necessário predefinir alguns arquivos de aplicativos (APK) e bibliotecas (SO) e especificar assinaturas para esses arquivos de aplicativos e bibliotecas. Essas operações geralmente são feitas no arquivo Android.mk. Este artigo apresentará detalhadamente os métodos e precauções para essas operações.
APK predefinido
Existem duas maneiras de predefinir o APK: uma é predefinir o aplicativo com código-fonte e a outra é predefinir o APK sem código-fonte.
Aplicativo predefinido com código-fonte
O método de pré-configurar um aplicativo com código-fonte é colocar o código-fonte do aplicativo em algum lugar da árvore de origem, como o diretório vendor/xxx/apps
ou e packages/apps
, em seguida, criar um arquivo Android.mk no diretório do aplicativo e especificar as variáveis relevantes e regras de compilação . Por exemplo:
# 定义当前模块的相对路径
LOCAL_PATH := $(call my-dir)
# 清除一些变量的值,但是LOCAL_PATH除外
include $(CLEAR_VARS)
# 指定当前待编译模块的名称
LOCAL_MODULE := my-app
# 指定当前模块的源文件
LOCAL_SRC_FILES := MainActivity.java
# 指定当前模块依赖的其他模块
LOCAL_SHARED_LIBRARIES := liblog
# 指定当前模块的编译选项
LOCAL_CFLAGS := -Wall -Werror
# 指定当前模块的链接选项
LOCAL_LDFLAGS := -Wl,-z,nocopyreloc
# 指定当前模块的目标类型,可以是静态库、动态库、可执行文件或者Java库等
include $(BUILD_PACKAGE)
APK predefinido sem código fonte
A maneira de predefinir um apk sem código-fonte é colocar o apk em algum lugar da árvore de origem, como vendor/xxx/apps
um diretório, e então criar um arquivo Android.mk no diretório apk, usar LOCAL_PREBUILT_STATIC_JAVA_LIBRARIES
variáveis para especificar o caminho relativo ao apk LOCAL_PATH
e use LOCAL_STATIC_JAVA_LIBRARIES
variáveis para definir o alias do apk. Por exemplo:
# 定义当前模块的相对路径
LOCAL_PATH := $(call my-dir)
# 清除一些变量的值,但是LOCAL_PATH除外
include $(CLEAR_VARS)
# 指定当前待编译模块的名称
LOCAL_MODULE := my-app
# 指定当前模块依赖的预置apk文件
LOCAL_STATIC_JAVA_LIBRARIES := my-app-prebuilt
# 指定当前模块的目标类型,可以是静态库、动态库、可执行文件或者Java库等
include $(BUILD_PACKAGE)
# 声明预置apk文件的位置
include $(CLEAR_VARS)
LOCAL_PREBUILT_STATIC_JAVA_LIBRARIES += my-app-prebuilt:my-app.apk
include $(BUILD_MULTI_PREBUILT)
Arquivo SO predefinido
Existem duas maneiras de predefinir arquivos SO, uma é predefinir com código-fonte e a outra é predefinir sem código-fonte.
Predefinido assim com o código-fonte
O método para predefinir isso com o código-fonte é colocar o código-fonte em algum lugar da árvore de origem, como o diretório vendor/xxx/libs
ou external/xxx
e, em seguida, criar um arquivo Android.mk no diretório so e especificar as variáveis relevantes e as regras de compilação. Por exemplo:
# 定义当前模块的相对路径
LOCAL_PATH := $(call my-dir)
# 清除一些变量的值,但是LOCAL_PATH除外
include $(CLEAR_VARS)
# 指定当前待编译模块的名称
LOCAL_MODULE := libtest
# 指定当前模块的源文件
LOCAL_SRC_FILES := test.c
# 指定当前模块依赖的其他模块
LOCAL_SHARED_LIBRARIES := libcutils liblog
# 指定当前模块的编译选项
LOCAL_CFLAGS := -Wall -Werror
# 指定当前模块的链接选项
LOCAL_LDFLAGS := -Wl,-z,nocopyreloc
# 指定当前模块的目标类型,可以是静态库、动态库、可执行文件或者Java库等
include $(BUILD_SHARED_LIBRARY)
Predefinido sem código fonte
A maneira de predefinir so sem código-fonte é colocá-lo em algum lugar da árvore de origem, como vendor/xxx/libs
um diretório, e então criar um arquivo Android.mk no diretório so e usar LOCAL_PREBUILT_JNI_LIBS
variáveis para especificar o caminho relativo ao arquivo so LOCAL_PATH
, ou use @
o símbolo para especificar o arquivo so no apk a ser carregado quando o apk for executado. Por exemplo:
# 定义当前模块的相对路径
LOCAL_PATH := $(call my-dir)
# 清除一些变量的值,但是LOCAL_PATH除外
include $(CLEAR_VARS)
# 指定当前待编译模块的名称
LOCAL_MODULE := my-app
# 指定当前模块依赖的预置so文件
LOCAL_SHARED_LIBRARIES := libtest1 libtest2 libtest3
# 指定当前模块的目标类型,可以是静态库、动态库、可执行文件或者Java库等
include $(BUILD_PACKAGE)
# 声明预置so文件的位置,有两种方式:
# 方式一:使用LOCAL_PREBUILT_JNI_LIBS变量,指定so文件相对于LOCAL_PATH的路径,例如:
include $(CLEAR_VARS)
LOCAL_PREBUILT_JNI_LIBS := \
lib/arm64-v8a/libtest1.so \
lib/arm64-v8a/libtest2.so \
lib/arm64-v8a/libtest3.so
include $(BUILD_MULTI_PREBUILT)
# 方式二:使用@符号,指定apk运行时加载apk中的so文件,例如:
ifeq ($(TARGET_ARCH), arm)
LOCAL_PREBUILT_JNI_LIBS := \
@lib/armeabi-v7a/libtest1.so \
@lib/armeabi-v7a/libtest2.so \
@lib/armeabi-v7a/libtest3.so
else ifeq ($(TARGET_ARCH),arm64)
LOCAL_PREBUILT_JNI_LIBS := \
@lib/arm64-v8a/libtest1.so \
@lib/arm64-v8a/libtest2.so \
@lib/arm64-v8a/libtest3.so
endif
Diretório de arquivos APK e SO predefinidos
Os diretórios de arquivos APK e SO predefinidos podem ser divididos em três tipos: diretório system/app, diretório system/priv-app e diretório data/app.
diretório sistema/aplicativo
Os arquivos APK e SO predefinidos no diretório system/app são aplicativos e bibliotecas comuns do sistema. Os usuários não podem desinstalá-los, mas podem atualizá-los. APKs e arquivos SO predefinidos no diretório system/app precisam especificar variáveis no arquivo Android.mk LOCAL_MODULE_PATH := $ (TARGET_OUT_APPS)
e adicionar esta frase ao arquivo device/xxxx/device.mk PRODUCT_PACKAGES += <app_name>
para especificar o nome do aplicativo a ser empacotado na imagem do sistema .
diretório system/priv-app
Os arquivos APK e SO predefinidos no diretório system/priv-app são aplicativos e bibliotecas privilegiadas do sistema. Os usuários não podem desinstalá-los, mas podem atualizá-los e ter permissões especiais. Os arquivos APK e SO predefinidos para o diretório system/priv-app precisam especificar variáveis no arquivo Android.mk LOCAL_MODULE_PATH := $ (TARGET_OUT_PRIVILEGED_APPS)
e adicionar esta frase no arquivo device///device.mk PRODUCT_PACKAGES += <app_name>
para especificar o nome do aplicativo a ser empacotado na imagem do sistema .
diretório de dados/aplicativo
Os arquivos APK e SO predefinidos no diretório data/app são aplicativos e bibliotecas comuns, que podem ser desinstalados e atualizados pelos usuários e serão limpos após a restauração das configurações de fábrica. Os arquivos APK e SO predefinidos no diretório data/app precisam especificar variáveis no arquivo Android.mk LOCAL_MODULE_PATH := $ (TARGET_OUT_DATA_APPS)
e adicionar esta frase no arquivo device///device.mk PRODUCT_PACKAGES += <app_name>
para especificar o nome do aplicativo a ser empacotado na imagem do sistema .
Especifique a assinatura
As assinaturas que podem ser especificadas no arquivo Android.mk são as seguintes:
- Plataforma: Indica o uso de arquivos de assinatura de plataforma, geralmente utilizados para aplicações de sistema ou aplicações privilegiadas.
- compartilhado: Indica o uso de um arquivo de assinatura compartilhado, geralmente usado para aplicativos que compartilham IDs de usuário.
- media: Indica o uso de arquivos de assinatura de mídia, geralmente usados para aplicações relacionadas à mídia.
- testkey: indica a utilização de arquivos de assinatura de teste, geralmente utilizados para aplicações em fase de desenvolvimento.
- PRÉ-ASSINADO: Indica que um arquivo apk pré-assinado é usado e não precisa ser assinado novamente.
- <keystore_file>: Indica o uso do arquivo de assinatura especificado, que pode ser um caminho relativo ou absoluto.
A maneira de especificar uma assinatura é usar uma variável no arquivo Android.mk LOCAL_CERTIFICATE
para especificar o nome ou caminho do arquivo de assinatura. Por exemplo:
# 使用平台签名
LOCAL_CERTIFICATE := platform
# 使用共享签名
LOCAL_CERTIFICATE := shared
# 使用媒体签名
LOCAL_CERTIFICATE := media
# 使用测试签名
LOCAL_CERTIFICATE := testkey
# 使用预先签名的apk
LOCAL_CERTIFICATE := PRESIGNED
# 使用自定义签名
LOCAL_CERTIFICATE := mykey.keystore
Se nenhuma assinatura for especificada, o padrão será assinatura testkey.
A diferença entre assinaturas
A assinatura especificada no arquivo Android.mk é usada para assinar o arquivo apk predefinido para que ele possa ser executado no sistema ou acessar permissões específicas.
- A assinatura da plataforma é usada para assinar os aplicativos principais da plataforma. Esses aplicativos completam as funções principais do sistema. O UID do processo no qual eles estão localizados é o sistema e eles podem acessar APIs no nível do sistema. Por exemplo, aplicativo de configurações, aplicativo de telefone, etc.
- A assinatura de mídia é usada para assinar aplicativos relacionados à mídia. Esses aplicativos fazem parte de mídia/download. O UID do processo no qual eles estão localizados é android.media e eles podem acessar APIs relacionadas à mídia. Por exemplo, aplicativo de câmera, aplicativo de música, etc.
- Assinaturas compartilhadas são usadas para assinar aplicativos que podem compartilhar dados com o processo inicial/contatos. O UID do processo em que estão localizados é android.uid.shared, e eles podem acessar APIs relacionadas aos dados compartilhados. Por exemplo, aplicativo de desktop, aplicativo de contato, etc.
- As assinaturas Testkey são usadas para assinar aplicativos em fase de teste. Elas não requerem UIDs ou permissões de processo específicas e só podem ser executadas na versão de desenvolvimento do sistema. Por exemplo, aplicativo HelloWorld, etc.
- A assinatura PRESIGNED é usada para assinar arquivos apk pré-assinados. Eles não precisam ser assinados novamente e podem ser instalados diretamente no sistema. Por exemplo, aplicativos de terceiros, etc.
- A assinatura <keystore_file> é usada para assinar arquivos de assinatura personalizados. Eles podem especificar qualquer arquivo ou caminho de keystore. Por exemplo, mykey.keystore, etc.
Resumir
No desenvolvimento de sistemas Android, a predefinição de arquivos APK e SO e a especificação de assinaturas são operações comuns. Compreender os métodos e precauções para essas operações pode ajudá-lo a desenvolver melhor o sistema Android. Este artigo explica como provisionar arquivos APK e SO no arquivo Android.mk e como especificar diferentes tipos de assinaturas. Espero que este artigo seja útil para você. Se você tiver alguma dúvida, deixe-a na área de comentários.