Make Java API available on Android (#3030)

This commit is contained in:
daquexian 2020-02-28 00:23:50 +08:00 коммит произвёл GitHub
Родитель ca2ed17ba7
Коммит 37a905f557
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: 4AEE18F83AFDEB23
10 изменённых файлов: 145 добавлений и 17 удалений

Просмотреть файл

@ -274,18 +274,18 @@ To build ONNX Runtime with the NN API EP, first install Android NDK (see [Androi
#### Build Instructions
The basic build commands are below. There are also some other parameters for building the Android version under See [Android Build instructions](#android) for more details.
The basic build commands are below. There are also some other parameters for building the Android version. See [Android Build instructions](#android) for more details.
##### Cross compiling on Windows
```bash
./build.bat --android --android_ndk_path <android ndk path> --dnnlibrary
./build.bat --android --android_sdk_path <android sdk path> --android_ndk_path <android ndk path> --dnnlibrary
```
##### Cross compiling on Linux
```bash
./build.sh --android --android_ndk_path <android ndk path> --dnnlibrary
./build.sh --android --android_sdk_path <android sdk path> --android_ndk_path <android ndk path> --dnnlibrary
```
---
@ -580,21 +580,22 @@ ls -l /code/onnxruntime/build/Linux/MinSizeRel/dist/*.whl
#### Pre-Requisites
Install Android NDK from https://developer.android.com/ndk/downloads
Install Android NDK in Android Studio or https://developer.android.com/ndk/downloads
#### Build Instructions
##### Cross compiling on Windows
```bash
./build.bat --android --android_ndk_path <android ndk path> --android_abi <android abi, e.g., arm64-v8a (default) or armeabi-v7a> --android_api <android api level, e.g., 27 (default)>
./build.bat --android --android_sdk_path <android sdk path> --android_ndk_path <android ndk path> --android_abi <android abi, e.g., arm64-v8a (default) or armeabi-v7a> --android_api <android api level, e.g., 27 (default)>
```
##### Cross compiling on Linux
```bash
./build.sh --android --android_ndk_path <android ndk path> --android_abi <android abi, e.g., arm64-v8a (default) or armeabi-v7a> --android_api <android api level, e.g., 27 (default)>
./build.sh --android --android_sdk_path <android sdk path> --android_ndk_path <android ndk path> --android_abi <android abi, e.g., arm64-v8a (default) or armeabi-v7a> --android_api <android api level, e.g., 27 (default)>
```
If you want to use NNAPI Execution Provider on Android, see [docs/execution_providers/NNAPI-ExecutionProvider.md](/docs/execution_providers/NNAPI-ExecutionProvider.md).
Android Archive (AAR) files, which can be imported directly in Android Studio, will be generated in your_build_dir/java/build/outputs/aar.
If you want to use NNAPI Execution Provider on Android, see [docs/execution_providers/NNAPI-ExecutionProvider.md](/docs/execution_providers/NNAPI-ExecutionProvider.md).

Просмотреть файл

@ -279,7 +279,6 @@ if(onnxruntime_BUILD_SHARED_LIB OR onnxruntime_ENABLE_PYTHON)
find_package(PythonLibs 3.5 REQUIRED)
else()
find_package(PythonInterp 3.4 REQUIRED)
find_package(PythonLibs 3.4 REQUIRED)
endif()
endif()

Просмотреть файл

@ -6,9 +6,11 @@
# Setup Java compilation
include(FindJava)
find_package(Java REQUIRED)
find_package(JNI REQUIRED)
include(UseJava)
include_directories(${JNI_INCLUDE_DIRS})
if (NOT CMAKE_SYSTEM_NAME STREQUAL "Android")
find_package(JNI REQUIRED)
include_directories(${JNI_INCLUDE_DIRS})
endif()
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -std=c11")
set(JAVA_ROOT ${REPO_ROOT}/java)
@ -81,8 +83,13 @@ onnxruntime_add_include_to_target(onnxruntime4j_jni onnxruntime_session)
target_include_directories(onnxruntime4j_jni PRIVATE ${REPO_ROOT}/include ${JAVA_ROOT}/build/headers)
target_link_libraries(onnxruntime4j_jni PUBLIC onnxruntime)
set(JAVA_PACKAGE_OUTPUT_DIR ${JAVA_OUTPUT_DIR}/build)
file(MAKE_DIRECTORY ${JAVA_PACKAGE_OUTPUT_DIR})
if (CMAKE_SYSTEM_NAME STREQUAL "Android")
set(ANDROID_PACKAGE_OUTPUT_DIR ${JAVA_PACKAGE_OUTPUT_DIR}/android)
file(MAKE_DIRECTORY ${ANDROID_PACKAGE_OUTPUT_DIR})
endif()
# expose native libraries to the gradle build process
file(MAKE_DIRECTORY ${JAVA_OUTPUT_DIR}/build)
set(JAVA_PACKAGE_DIR ai/onnxruntime/native/)
set(JAVA_NATIVE_LIB_DIR ${JAVA_OUTPUT_DIR}/native-lib)
set(JAVA_NATIVE_JNI_DIR ${JAVA_OUTPUT_DIR}/native-jni)
@ -90,7 +97,20 @@ set(JAVA_PACKAGE_LIB_DIR ${JAVA_NATIVE_LIB_DIR}/${JAVA_PACKAGE_DIR})
set(JAVA_PACKAGE_JNI_DIR ${JAVA_NATIVE_JNI_DIR}/${JAVA_PACKAGE_DIR})
file(MAKE_DIRECTORY ${JAVA_PACKAGE_LIB_DIR})
file(MAKE_DIRECTORY ${JAVA_PACKAGE_JNI_DIR})
if (CMAKE_SYSTEM_NAME STREQUAL "Android")
set(ANDROID_PACKAGE_JNILIBS_DIR ${JAVA_OUTPUT_DIR}/android)
set(ANDROID_PACKAGE_ABI_DIR ${ANDROID_PACKAGE_JNILIBS_DIR}/${ANDROID_ABI})
file(MAKE_DIRECTORY ${ANDROID_PACKAGE_JNILIBS_DIR})
file(MAKE_DIRECTORY ${ANDROID_PACKAGE_ABI_DIR})
endif()
add_custom_command(TARGET onnxruntime4j_jni POST_BUILD COMMAND ${CMAKE_COMMAND} -E create_symlink $<TARGET_FILE:onnxruntime> ${JAVA_PACKAGE_LIB_DIR}/$<TARGET_LINKER_FILE_NAME:onnxruntime>)
add_custom_command(TARGET onnxruntime4j_jni POST_BUILD COMMAND ${CMAKE_COMMAND} -E create_symlink $<TARGET_FILE:onnxruntime4j_jni> ${JAVA_PACKAGE_JNI_DIR}/$<TARGET_LINKER_FILE_NAME:onnxruntime4j_jni>)
if (CMAKE_SYSTEM_NAME STREQUAL "Android")
add_custom_command(TARGET onnxruntime4j_jni POST_BUILD COMMAND ${CMAKE_COMMAND} -E create_symlink $<TARGET_FILE:onnxruntime> ${ANDROID_PACKAGE_ABI_DIR}/$<TARGET_LINKER_FILE_NAME:onnxruntime>)
add_custom_command(TARGET onnxruntime4j_jni POST_BUILD COMMAND ${CMAKE_COMMAND} -E create_symlink $<TARGET_FILE:onnxruntime4j_jni> ${ANDROID_PACKAGE_ABI_DIR}/$<TARGET_LINKER_FILE_NAME:onnxruntime4j_jni>)
endif()
# run the build process (this copies the results back into CMAKE_CURRENT_BINARY_DIR)
add_custom_command(TARGET onnxruntime4j_jni POST_BUILD COMMAND ${GRADLE_EXECUTABLE} cmakeBuild -DcmakeBuildDir=${CMAKE_CURRENT_BINARY_DIR} WORKING_DIRECTORY ${JAVA_ROOT})
if (CMAKE_SYSTEM_NAME STREQUAL "Android")
add_custom_command(TARGET onnxruntime4j_jni POST_BUILD COMMAND ${GRADLE_EXECUTABLE} -b build-android.gradle -c settings-android.gradle build -DjniLibsDir=${ANDROID_PACKAGE_JNILIBS_DIR} -DbuildDir=${ANDROID_PACKAGE_OUTPUT_DIR} WORKING_DIRECTORY ${JAVA_ROOT})
endif()

Просмотреть файл

@ -574,6 +574,13 @@ add_test(NAME onnx_test_pytorch_converted
add_test(NAME onnx_test_pytorch_operator
COMMAND onnx_test_runner ${PROJECT_SOURCE_DIR}/external/onnx/onnx/backend/test/data/pytorch-operator)
if (CMAKE_SYSTEM_NAME STREQUAL "Android")
list(APPEND android_shared_libs log android)
if (onnxruntime_USE_NNAPI)
list(APPEND android_shared_libs neuralnetworks)
endif()
endif()
#perf test runner
set(onnxruntime_perf_test_src_dir ${TEST_SRC_DIR}/perftest)
set(onnxruntime_perf_test_src_patterns
@ -611,6 +618,9 @@ if (onnxruntime_BUILD_SHARED_LIB)
if(NOT WIN32)
list(APPEND onnxruntime_perf_test_libs nsync_cpp)
endif()
if (CMAKE_SYSTEM_NAME STREQUAL "Android")
list(APPEND onnxruntime_perf_test_libs ${android_shared_libs})
endif()
target_link_libraries(onnxruntime_perf_test PRIVATE ${onnxruntime_perf_test_libs} Threads::Threads)
if(WIN32)
target_link_libraries(onnxruntime_perf_test PRIVATE debug dbghelp advapi32)
@ -650,6 +660,9 @@ if (onnxruntime_BUILD_SHARED_LIB)
if(NOT WIN32)
list(APPEND onnxruntime_shared_lib_test_LIBS nsync_cpp)
endif()
if (CMAKE_SYSTEM_NAME STREQUAL "Android")
list(APPEND onnxruntime_shared_lib_test_LIBS ${android_shared_libs})
endif()
AddTest(DYN
TARGET onnxruntime_shared_lib_test
SOURCES ${onnxruntime_shared_lib_test_SRC} ${TEST_SRC_DIR}/providers/test_main.cc

69
java/build-android.gradle Normal file
Просмотреть файл

@ -0,0 +1,69 @@
apply plugin: 'com.android.library'
def jniLibsDir = System.properties['jniLibsDir']
def buildDir = System.properties['buildDir']
project.buildDir = buildDir
buildscript {
repositories {
google()
jcenter()
}
dependencies {
classpath 'com.android.tools.build:gradle:3.5.3'
// NOTE: Do not place your application dependencies here; they belong
// in the individual module build.gradle files
}
}
allprojects {
repositories {
google()
jcenter()
}
}
android {
compileSdkVersion 29
buildToolsVersion "29.0.2"
defaultConfig {
minSdkVersion 24
targetSdkVersion 28
versionCode 1
versionName "1.0"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
}
android {
lintOptions {
abortOnError false
}
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
}
}
compileOptions {
sourceCompatibility = 1.8
targetCompatibility = 1.8
}
sourceSets {
main {
jniLibs.srcDirs = [jniLibsDir]
}
}
}
dependencies {
testImplementation 'org.junit.jupiter:junit-jupiter-api:5.1.1'
testRuntimeOnly 'org.junit.jupiter:junit-jupiter-engine:5.1.1'
testImplementation 'com.google.protobuf:protobuf-java:3.10.0'
}

Просмотреть файл

@ -0,0 +1,2 @@
rootProject.name = 'onnxruntime'
rootProject.buildFileName = 'build-android.gradle'

Просмотреть файл

@ -0,0 +1,2 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="ai.onnxruntime" />

Просмотреть файл

@ -44,14 +44,16 @@ final class OnnxRuntime {
if (loaded) {
return;
}
Path tempDirectory = Files.createTempDirectory("onnxruntime-java");
Path tempDirectory = isAndroid() ? null : Files.createTempDirectory("onnxruntime-java");
try {
load(tempDirectory, ONNXRUNTIME_LIBRARY_NAME);
load(tempDirectory, ONNXRUNTIME_JNI_LIBRARY_NAME);
ortApiHandle = initialiseAPIBase(ORT_API_VERSION_1);
loaded = true;
} finally {
cleanUp(tempDirectory.toFile());
if (!isAndroid()) {
cleanUp(tempDirectory.toFile());
}
}
}
@ -72,6 +74,15 @@ final class OnnxRuntime {
}
}
private static boolean isAndroid() {
try {
Class.forName("android.app.Activity");
return true;
} catch (ClassNotFoundException e) {
return false;
}
}
/**
* Load a shared library by name.
*
@ -80,6 +91,12 @@ final class OnnxRuntime {
* @throws IOException If the file failed to read or write.
*/
private static void load(Path tempDirectory, String library) throws IOException {
// On Android, we simply use System.loadLibrary
if (isAndroid()) {
System.loadLibrary("onnxruntime4j_jni");
return;
}
// 1) The user may skip loading of this library:
String skip = System.getProperty("onnxruntime.native." + library + ".skip");
if (Boolean.TRUE.toString().equalsIgnoreCase(skip)) {

Просмотреть файл

@ -120,6 +120,7 @@ Use the individual flags to only run the specified stages.
help='')
parser.add_argument("--android_api", type=int, default=27,
help='Android API Level, e.g. 21')
parser.add_argument("--android_sdk_path", type=str, help='Path to the Android SDK')
parser.add_argument("--android_ndk_path", default="", help="Path to the Android NDK")
# Arguments needed by CI
@ -443,7 +444,7 @@ def clean_targets(cmake_path, build_dir, configs):
run_subprocess(cmd_args)
def build_targets(cmake_path, build_dir, configs, parallel):
def build_targets(args, cmake_path, build_dir, configs, parallel):
for config in configs:
log.info("Building targets for %s configuration", config)
build_dir2 = get_config_build_dir(build_dir, config)
@ -463,7 +464,11 @@ def build_targets(cmake_path, build_dir, configs, parallel):
cmd_args += [ "--" ]
cmd_args += build_tool_args
run_subprocess(cmd_args)
env = {}
if args.android:
env['ANDROID_SDK_ROOT']=args.android_sdk_path
run_subprocess(cmd_args, env=env)
def add_dir_if_exists(dir, dir_list):
if (os.path.isdir(dir)):
@ -994,7 +999,7 @@ def main():
setup_dml_build(args, cmake_path, build_dir, configs)
if (args.build):
build_targets(cmake_path, build_dir, configs, args.parallel)
build_targets(args, cmake_path, build_dir, configs, args.parallel)
if args.test :
run_onnxruntime_tests(args, source_dir, ctest_path, build_dir, configs,

Просмотреть файл

@ -10,6 +10,6 @@ jobs:
displayName: Clone submodules
- script: echo "y" | $ANDROID_HOME/tools/bin/sdkmanager --install 'ndk-bundle'
displayName: Install Android NDK
- script: tools/ci_build/build.py --android --build_dir build --android_ndk $ANDROID_HOME/ndk-bundle --android_abi=x86_64 --skip_submodule_sync --parallel --use_dnnlibrary
- script: tools/ci_build/build.py --android --build_dir build --android_sdk_path $ANDROID_HOME --android_ndk_path $ANDROID_HOME/ndk-bundle --android_abi=x86_64 --skip_submodule_sync --parallel --use_dnnlibrary
displayName: Build and Test on Android Emulator