Android JNI優化

使用共享C++ 運行時庫

在NDK中使用 C++ 時,請使用最新 NDK,並選擇使用 libc++ 共享STL 做爲 C++ STL,這樣可使多個 so 共享一個 C++ STL,由於使用靜態STL將會在每一個 so 庫中出現重複代碼,增長應用大小,而且因爲全局數據和靜態構造函數在內的 STL 將同時存在於兩個庫中。此應用的運行時行爲未定義,所以在實際運行過程當中,應用會常常崩潰。例如:內存在一個庫中分配,而在另外一個庫中釋放,從而致使內存泄漏或堆損壞。java

解決方案

  • ndk-build

在 Application.mk 中添加android

APP_STL := c++_shared
複製代碼
  • cmake

在 build.gradle 中添加ios

externalNativeBuild {
    cmake {
        arguments "-DANDROID_STL=c++_shared"
  }
}
複製代碼

libc++_shared.so 衝突解決

使用 c++_shared 編譯後 aar 中將附帶 libc++_shared.so,若是有多個aar 都附帶了 libc++_shared.so,將致使APP工程中編譯不過,此時能夠採起下面兩種辦法:c++

  • APP 工程中 build.gradle 中添加編譯配置
packagingOptions {
    pickFirst 'lib/*/libc++_shared.so'
}
複製代碼
  • 在編譯 so 庫的工程 build.gradle 中添加配置排除 libc++_shared.so, 由App工程負責添加 libc++_shared.so :
libraryVariants.all { variant ->
    def bundleTask = variant.packageLibrary
    def reBundleTask = tasks.create("reBundle${variant.name.capitalize()}Aar", Zip) {
        def dir = bundleTask.destinationDir
        def path = bundleTask.archivePath
        def name = bundleTask.archiveName
        archiveName name + ".bak"
        destinationDir dir
        from(zipTree(path)) {
            exclude "jni/*/libc++_shared.so"
        }
        doLast {
            delete path
            file("$dir/$name" + ".bak").renameTo(path)
        }
    }
    bundleTask.finalizedBy reBundleTask
    variant.assemble.dependsOn reBundleTask
}
複製代碼

開啓編譯優化

若是使用 GCC 能夠 -Os 打開優化,若是使用 Clang 能夠 -Oz 打開優化小程序

解決方案

  • ndk-build

在 Android.mk 中添加api

LOCAL_CFLAGS += -Os -Oz
LOCAL_CPPFLAGS += -Os -Oz
複製代碼
  • cmake

在 build.gradle 中添加bash

externalNativeBuild {
    cmake {
        cFlags "-Os -Oz"
        cppFlags "-Os -Oz"
    }
}
複製代碼

隱藏符號

-fvisibility=hidden
複製代碼

隱藏elf符號表,能夠減小 so 文件大小,提高性能 注意:須要在提供給 java 層暴露的JNI函數能夠 在方法上添加 JNIEXPORT 宏或者添加屬性 attribute ((visibility ("default")))函數

-fvisibility-inlines-hidden
複製代碼

隱藏全部內聯函數,從而減少導出符號表的大小,既能縮減文件的大小,還能提升運行性能性能

解決方案

  • ndk-build

在 Android.mk 中添加gradle

LOCAL_CFLAGS += -fvisibility=hidden
LOCAL_CPPFLAGS += -fvisibility=hidden -fvisibility-inlines-hidden
複製代碼
  • cmake 在 build.gradle 中添加
externalNativeBuild {
    cmake {
        cFlags "-fvisibility=hidden"
 		cppFlags "-fvisibility=hidden -fvisibility-inlines-hidden"
 	}
}
複製代碼

刪除無用函數

使用 --gc-section 編譯選項減少程序體積

解決方案

  • ndk-build

在 Android.mk 中添加

LOCAL_CPPFLAGS += -ffunction-sections -fdata-sections
LOCAL_CFLAGS += -ffunction-sections -fdata-sections 
LOCAL_LDFLAGS += -Wl,--gc-sections
複製代碼
  • cmake

在 build.gradle 中添加

externalNativeBuild {
    cmake {
        cFlags "-ffunction-sections -fdata-sections"
        cppFlags "-ffunction-sections -fdata-sections"
    }
}
複製代碼

不使用異常和RTTI

在C++ 中使用異常和 RTTI 會顯著增長文件大小,所以儘可能不要在 C++中 使用異常和 RTTI,若是代碼中沒有使用到 異常和 RTTI 請刪除相關 feature 和 flags

解決方案

  • ndk-build
從 LOCAL_CPP_FEATURES 中 刪除 exceptions 和 rtti
從 APP_CPPFLAGS 中 刪除 -frtti 和 -fexceptions
從 LOCAL_CPPFLAGS 中 刪除 -frtti 和 -fexceptions
複製代碼
  • cmake
刪除 cppFlags "-frtti"
刪除 cppFlags "-fexceptions"
複製代碼

不使用iostream

儘可能不要在 C++ 代碼中使用 iostream,如:

#include<iostream>
std::cout << "test" <<std::endl;
複製代碼

其餘相似用法:std::cin,std::cout,std::cerr,std::clog,std::wcin,std::wcout,std::wcerr,std::wclog

這樣沒有什麼做用而且會顯著增長 so 文件大小

解決方案

刪除 iostream 相關代碼調用,若是有打日誌須要可使用 __android_log_print

#define LOG_TAG "native_log"
#define LOGI(...) __android_log_print(ANDROID_LOG_INFO,LOG_TAG,__VA_ARGS__)
#define LOGE(...) __android_log_print(ANDROID_LOG_ERROR,LOG_TAG,__VA_ARGS__)
LOGE("AndroidBitmap_getInfo() failed ! error=%d", ret);
複製代碼
相關文章
相關標籤/搜索