Serie Android C++: resumen del método de NDK para reducir el tamaño de la biblioteca so

¡Acostúmbrate a escribir juntos! Este es el séptimo día de mi participación en el "Nuevo plan diario de Nuggets · Desafío de actualización de abril", haga clic para ver los detalles del evento .

1. Antecedentes

El SDK de voz de enlace completo basado en AVS Device SDK de Amazon finalmente compiló docenas de bibliotecas dinámicas, y el tamaño de una biblioteca dinámica de arquitectura única es de decenas de megabytes. Apenas se ejecutaba en dispositivos Iot antes, pero este volumen es enorme. cantidad para aplicaciones de teléfonos móviles. Lo fatal es que cada módulo requiere mucho tiempo y esfuerzo para optimizar el volumen de unos pocos K. Lo daré directamente a las últimas decenas de megabytes, y la plataforma APP definitivamente no lo aceptará. . Pero una es que hay un requisito comercial y la otra es que quiero llevar el SDK a la aplicación móvil para aumentar la cantidad de usuarios y verificar la estabilidad y la experiencia interactiva del SDK, así que comencé un largo proceso de adelgazamiento. y, finalmente, la arquitectura única se comprimió a menos de cinco billones, aunque todavía es un poco grande, pero es una gran mejora con respecto a lo que solía ser.

2. Eliminar módulos inútiles

AVS Device SDK es un programa de consola que se usa principalmente en audio y el código es multiplataforma, por lo que, en primer lugar, hay mucha redundancia para multiplataforma y, en segundo lugar, hay muchos módulos que no usamos en absoluto. Por ejemplo, se introduce una biblioteca dinámica de Sqlite para el almacenamiento local. No necesitamos almacenamiento local. Podemos poner cosas como la configuración del reloj de alarma en la capa de aplicaciones, e incluso si necesitamos almacenamiento, podemos usar los proporcionados por Android y Plataformas iOS.Sqlite. La eliminación de módulos no utilizados es el espacio de optimización de tamaño de paquete más rápido y más grande.

3. Reemplazo de bibliotecas de terceros para proporcionar capacidades para plataformas Android/iOS

AVS Device SDK implementa un reproductor de audio basado en la decodificación ffmpeg en la plataforma Android. Para nuestro escenario, usamos principalmente el reproductor para reproducir TTS, y TTS es un formato mp3 fijo negociado con el servicio. No hay absolutamente ninguna necesidad de introducir un mp3 método de decodificación Enorme biblioteca ffmpeg. Aquí usamos la biblioteca de medios de capa Jni proporcionada por la plataforma Android para la decodificación de audio. E incluso si la capa JNI de la plataforma Android no lo admite, puede confiar en una biblioteca de decodificación de mp3 en lugar del enorme ffmpeg. Para todo el volumen del paquete, los módulos de terceros suelen ser relativamente grandes.

4. Usar tira

使用NDK toolchain可以把调试的C++ 符号表(Symbol Table)中数据删除,我们一般我们打成APK会自动帮我们做这个工作,当然也可以手动设置:

手动的在链接选项中加入 strip参数,配置如下所示:

SET_TARGET_PROPERTIES(yoga PROPERTIES LINK_FLAGS "-Wl,-s")
复制代码

也可以手动执行ndk提供的aarch64-linux-android-strip命令移除动态库中的调试信息,这种方式除了前面方法外优化体积最高的方式,比如libLibSampleApp.so从48M直接优化到了992k。

4. 设置编译器的优化flag

编译器有个优化flag可以设置,分别是-Os(体积最小),-O3(性能最优)等。这里将编译器的优化flag设置为-Os,以便减少体积。

CMake:

set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Os")
set(CMAKE_CXX_FLAGS "${CMAKE_C_FLAGS}")
复制代码

Android.mk

LOCAL_CPPFLAGS += -Os
LOCAL_CFLAGS += -Os
复制代码

除了直接删除占用体积较大的模块外,编译器优化是排下来优化空间最大的方法。设置完-Os后占用提交较大的前几个库体积对比:

库名 优化前体积 优化后体积
libLibSampleApp.so 48M 33M
libAVSCommon.so 28M 22M
libDefaultClient.so 14M 9.9M

5. 使用 gc-sections去除没有用到的函数

有些时候代码量比较大的时候我们没办法手动发现无用的函数,这个时候可以可以开启编译器的gc-sections选项,让编译器自动的帮你做到这一点。

编译器可以配置自动去除未使用的函数和变量,以下是配置方式:

CMake:

# 去除未使用函数与变量
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -ffunction-sections -fdata-sections")
set(CMAKE_CXX_FLAGS "${CMAKE_C_FLAGS}")
# 设置去除未使用代码的链接flag
SET_TARGET_PROPERTIES(yoga PROPERTIES LINK_FLAGS "-Wl,--gc-sections")        
复制代码

Android.mk:

OCAL_CPPFLAGS += -ffunction-sections -fdata-sections
LOCAL_CFLAGS += -ffunction-sections -fdata-sections 
LOCAL_LDFLAGS += -Wl,--gc-sections
复制代码

6. 设置编译器的 Visibility Feature

Visibility Feature就是用来控制在哪些函数可以在符号表中被输入,由于C++并不是完全面向对象的,非类的方法并没有public这种修饰符,因此,要用Visibility Feature来控制哪些函数可以被外部调用。而JNI提供了一个宏-JNIEXPORT来控制这点。所以只要对函数加上这个宏,像这样:

// JNIEXPORT就是控制可见的宏
// JNICALL在NDK这里没有什么意义,只是个标识宏
JNIEXPORT void JNICALL Java_ClassName_MethodName(JNIEnv *env, jobject obj, jstring javaString)
复制代码

然后在编译器的FLAGS选项开启 -fvisibility = hidden 就可以。这样,不仅可以控制函数的可见性,并且可以减少包体的大小。

7. 去除C++代码中的iostream等直接IO相关代码

使用STL中的iostream相关库会明显的增加包的体积,而Android本身是有预编译库(android/log.h)可以代替输入到控制台的工具的。在我们的SDK中由于之前是控制台程序所以用到了输入输出,编译的时候没有把这块排除出去,造成了一定的体积冗余。

8. STL的使用方式

对于C++的library,引用方式有2种:

  • 静态方式(static)
  • 动态方式(shared)

其中,静态方式在编译时会将用到的相关代码直接复制到目的文件中;而动态方式则会将相关的代码打成so文件,以便多次引用。由于编译器在编译时并不能知道所有被引用的地方,所以同时会打入了很多不相关的代码。

所以,如果项目中引用library的函数较多时,用动态方式可以避免多次拷贝,节省空间。相反,则直接使用静态方式会更节省空间。由于我们SDK的模块特别多,再加上整体APK里面已经有其他业务引入了动态库,所以我们用动态库的方式。

9. 不使用Exception和RTTI

关于这两点在网上看到的没有实践过,不过拿过来可以作为包体积持续优化的参考。

RTTI

通过RTTI,能够通过基类的指针或引用来检索其所指对象的实际类型,即运行时获取对象的实际类型。C++通过下面两个操作符提供RTTI。

(1)typeid:返回指针或引用所指对象的实际类型。

(2)dynamic_cast:将基类类型的指针或引用安全的转换为派生类型的指针或引用。

RTTI的选项是默认关闭的的,而代码中其实并没有用到相关的功能,这里可以直接关闭。

Exception

El uso de excepciones de C++ aumentará el tamaño del paquete, y la compatibilidad actual de JNI para las excepciones de C++ tiene errores. Por ejemplo, el siguiente código hará que el programa se bloquee (para versiones inferiores de Android NDK). Por lo tanto, para introducir excepciones en el programa, debe implementar la lógica relevante usted mismo, pero esto aumentará el tamaño del cuerpo del paquete. Para los desarrolladores, las excepciones pueden ayudar a localizar problemas rápidamente, pero para los usuarios no son tan importantes, por lo que pueden eliminarse aquí.

10 Resumen

Este artículo presenta la eliminación de módulos inútiles, el reemplazo de bibliotecas de terceros con capacidades de plataforma, el uso de strip, la configuración de indicadores de optimización del compilador, el uso de secciones gc para eliminar funciones no utilizadas, la configuración de visibilidad y la eliminación de iostreams que ayudan a optimizar el volumen dinámico de la biblioteca.

Supongo que te gusta

Origin juejin.im/post/7084238491089027079
Recomendado
Clasificación