2013-12-19 5 views
47

새로운 Android gradle plugin (0.7)은 NDK에 대한 새로운 지원을 포함하는 것으로 보이지만 설명서에는 거의 언급이 없습니다 (내가 찾은 유일한 참고 자료는 테스트입니다. ndkSanAngeles).Android Gradle 플러그인으로 NDK를 구성하는 방법 0.7

gradle이 내 PATH에 포함 된 NDK를 찾고있는 것처럼 보입니다. 그러나, 프로젝트를 빌드하는 것은 잘못 무엇

  • 와 함께 실패 실행은 작업 실패 'OGLTests : compileDefaultFlavorDebugNdk를'. NDK 내가 Gradle을의 NDK를 구성하는 방법

을 구성하지?

나의 현재 build.gradle은 다음과 같습니다

task nativeLibsToJar(type: Zip, description: 'create a jar with native libs') { 
    destinationDir file("$buildDir/native-libs") 
    baseName 'native-libs' 
    extension 'jar' 
    from fileTree(dir: 'src/main/libs', include: '**/*.so') 
    from fileTree(dir: 'src/main/libs', include: '**/gdb*') 
    into 'lib/' 
} 

tasks.withType(JavaCompile) { 
    compileTask -> compileTask.dependsOn nativeLibsToJar 
} 

dependencies { 
    compile fileTree(dir: "$buildDir/native-libs", include: '*.jar') 
} 

android { 
    compileSdkVersion 19 
    buildToolsVersion '19.0.0' 

    defaultConfig { 
     minSdkVersion 14 
     targetSdkVersion 19 
     versionCode 1 
     versionName "0.1" 

    } 
    buildTypes { 
     release { 
      runProguard false 
     } 
     debug { 
      // jniDebugBuild true 
      runProguard false 
      debuggable true 
     } 
    } 
    productFlavors { 
     defaultFlavor { 
      proguardFile 'proguard-rules.txt' 
     } 
    } 
} 

감사합니다.

답변

34

답을 찾았습니다. 파일 local.propertiesndk.dir=path/to/ndk을 포함시키는 것이 트릭을 만들었습니다.

업데이트 : 최신 Android Studio 버전에서는 Project Structure> SDK 위치에서 직접 값을 설정할 수 있습니다. ndk.dir을 추가하기 전에 주석으로 당신은 또한 local.properties에 =

+0

답 – orip

+5

수동으로'local.properties' 파일에 넣어 뭔가 좋은 생각이인가 동의하는 것을 잊지 마세요 local.properties에 NDK하는 경로를 포함하도록 제안 "이 파일은 Android Studio에서 자동으로 생성됩니다.이 파일을 수정하지 마십시오 - 변경 사항이 지워집니다!" ? –

15

도움이됩니다. 흥미롭게도 local.properties에 ndk.dir이 구성되어 있지 않더라도 local.properties가 환경 변수 ANDROID_NDK_HOME 인 에 설정된 모든 값을 무시한다는 것을 발견했습니다.. (적어도 gradle android plugin v 0.7.3). 안드로이드 스튜디오 local.properties을 덮어 쓸 수 있고,이 Gradle을 플러그인 코드를 찾고 ndk.dir :(

+0

내가 한 것은 아니지만 응용 프로그램에 반영하는 방법은 무엇입니까? 여전히 동일한 오류가 발생합니다. – CoDe

+0

이것은 최신 빌드 도구에서 작동하지 않습니다. 2.2+ –

0

변수 ANDROID_NDK_HOME 환경을 설정할 수 있습니다

+0

불특정 다수에 대해 말하는 것을 이해하지 못합니다. "local.properties에 ndk.dir이 구성되어 있지 않아도 local.properties의 ndk.dir이 모든 값을 덮어 씁니다."--- 뭐라구?!?! 지정되지 않은 값은 환경 변수를 대체합니다 .... 환경 변수가 무시된다는 것을 실제로 말하고 있습니까? 아니면 나는 무엇을 놓치고 있습니까? – Dennis

+0

그래, @Dennis에 동의한다. local.properties에서 빠져 나간다해도 아무런 문제가 없다. – Merk

76

를 구성 할 수있는 방법을 제공하지 않는 것으로

는 혼란, 내가 도움이 다음을 발견 나를 NDK 및 사전 구축 된 네이티브 라이브러리를 모두 사용 : 미리 만들어진 네이티브 라이브러리 단순히 링크에

를, 당신의 작업에 NDK 섹션을 추가 예를 들어, 내가 productFlavors 아래를 추가 한 abiFilter 폴더가 libs와 이름입니다.. . abiFilters는 쉼표로 구분 된 목록의 두 라이브러리가 최종 APK에 추가된다는 것을 의미합니다 (따라서 이론적으로 "armeabi", "armeabi- V7A ","86 "및"한 APK에서 MIPS "모든 및 O/S 설치에 지원되는 아키텍처 LIB)를 선택합니다 :

productFlavors { 
    arm { 
     ndk { 
      abiFilters "armeabi", "armeabi-v7a" 
     } 
    } 
    x86 { 
     ndk { 
      abiFilter "x86" 
     } 
    } 
} 

이 예를 팔 빌드를 만들 것 APK와 V5 및 V7A arm lib가 포함되어 있으며 x86 빌드는 x86 라이브러리가있는 APK를 생성합니다. 그러면 프로젝트 jniLibs 디렉토리의 기본 라이브러리가 검색됩니다. jniLibs 디렉토리는 이전 jni 디렉토리 (예 :다음과 같이

[project]/[app]/src/main/jniLibs/armeabi/libmyNative.so 
[project]/[app]/src/main/jniLibs/armeabi-v7a/libmyNative.so 
[project]/[app]/src/main/jniLibs/x86/libmyNative.so 

그런 다음 당신은 자바에로드 할 수 있습니다 :

static 
{ 
    loadLibrary("myNative"); 
} 

을 이제, 하나의 네이티브 라이브러리가 다른 하나에 따라 가정 해 봅시다. 당신 MUST 먼저 종속 라이브러리를로드 (API 17 이하로 분 API를 설정하는 경우) :

static 
{ 
    loadLibrary("myDependency"); 
    loadLibrary("myNative"); 
} 

당신은 또한 당신의 defaultConfig에서 NDK {} 섹션 또는 디버그 등의 buildType을 (배치하거나 해제 또는 무엇이든 할 수 그렇지 않으면 사용할 수 있습니다). 예를 들어 : 사전 구축함으로써

buildTypes { 
    debug { 
     ndk { 
      abiFilters "armeabi", "armeabi-v7a" 
     } 
    } 
} 

, 난 당신이 다운로드 한 제 3 자 libs와 의미하거나 NDK 툴체인 또는 자신의 ARM 툴체인 (안 NDK 빌드 스크립트 자체)를 사용하여 내장 된 라이브러리입니다.

API 18에서는 원래 lib loader가 응용 프로그램의 lib 디렉토리 (보안 이유 등)에 대해 알지 못했기 때문에 종속성을 "자동으로"로드하지 못하게하는 오랜 기간의 아키텍처 문제가 수정되었습니다. API 18 이상에서 myNative가 위의 myDependency에 종속되면 loadLibrary ("myNative")를 호출하면 OS에서 myDependency로드를 처리합니다. API 17 이하 버전을 사용하는 기기의 시장 침투가 허용 될 때까지는이 사실을 믿지 마십시오.


명시 적으로 안드로이드 스튜디오의 현재 버전의 소스에서 NDK 라이브러리를 작성하려면 다음을 수행 할 수 있습니다

세트를 가리 키도록 local.properties의 ndk.dir 값 이전에 언급 한 NDK 집. 누구든지 local.properties에서 직접 env를 사용할 수 있는지 알고 있습니까? :) 당신의 build.gradle 파일에서

, 당신의 작업에이 같은 추가 (다시, defaultConfig, 디버그, 릴리스하는 productFlavor 등이 있습니다) :

ndk { 
    moduleName "myNDKModule" 
    stl "stlport_shared" 
    ldLibs "log", "z", "m" 
    cFlags "-I/some/include/path" 
} 

이와 기본 구조입니다 현재 지원되는 유형 (moduleName, stl, ldLibs 및 cFlags). 나는 보았고 이것 이상을 찾지 못했습니다. 위의 각 필드 앞에 "-l"이 자동으로 추가되므로 ldLibs와 관련하여 문제가 있습니다. 당신은 그것을 말하면서 (나는해야만한다.) ldLibs "log -lz -lm -Wl, -whole-archive -l/path/to/someOtherLib -Wl, -no-whole-archive"

이 줄에서는 첫 번째 매개 변수의 끝 부분에 태그를 달아서 -l로 시작하지 않는 매개 변수를 추가하므로 지금 당장 얻을 수 있습니다. 위의 경우, 전체 정적 라이브러리를 Java 내에서 사용할 NDK 모듈에 연결합니다. Google 개발자가 자신의 Android.mk 파일을 NDK 빌드 프로세스에 병합하는 기능을 추가 할 수 있도록 추가 기능을 추가하도록 요청했지만이 모든 것이 새로운 것이기 때문에 다소 시간이 걸릴 수 있습니다.

현재 build.gradle에 입력 한 내용은 임시 빌드 디렉토리를 삭제하고 매번 다시 작성하므로 gradle android 플러그인 소스 코드 (재미있을 것입니다)를 다운로드하고 수정하지 않는 한 "make due "는 빌드에 복사 된 내용을 가져 오기 위해 필요합니다. 본질적으로이 ndk 지원을 제공하는 android gradle 스크립트는 Android.mk 파일을 생성하고 임시 디렉토리에 NDK 시스템을 사용하여 빌드합니다.

1 초 동안 사변 추적.모듈 이름이 같은 JNI 디렉토리에 프로젝트에 ac 또는 CPP 파일과 일치해야합니다

[project]/[app]/src/main/jni/myNDKModule.cpp 

STL과 값의 값으로 설정해야한다 "stlport_static" "stlport_shared"또는 당신의 된 STLport 라이브러리를 사용하려는 경우 C++. 확장 된 C++ 지원이 필요하지 않으면 stl을 그대로 둘 수 있습니다. Android는 기본적으로 매우 기본적인 C++ 지원을 제공합니다. 지원되는 다른 C++ 라이브러리의 경우, 다운로드 한 NDK에서 NDK 문서 가이드 라인을보십시오. 여기에 stlport_shared로 설정하면, gradle은 NDK의 sources/cxx-stl/stlport/libs 디렉토리에서 libstlport_shared.so lib를 APK의 lib 디렉토리로 복사합니다. 또한 컴파일러에서 포함 경로를 처리합니다. (기술적으로 gradle은이 모든 것을 수행하지 않지만 Android NDK 빌드 시스템을 사용합니다). 따라서 stlport 사본을 jniLibs 디렉토리에 두지 마십시오.

마지막으로, 나는 cFlags가 꽤 분명하다고 생각합니다.

Mac OSX (아래 참조)에서는 ANDROID_NDK_HOME을 설정할 수 없지만 일부 연구 결과는 다른 OS에서도 여전히 작동 할 수 있습니다. 그것은 비록 제거됩니다.

나는 논평하고 싶었지만 아직 명성을 얻지 못했다. Dennis, 환경 변수는 무시되며 오버플로되지 않습니다. 사실, 환경 변수를 얻지 못합니다. 내가 말할 수있는 것부터, Android Studio IDE는 몇 가지 특정 환경 변수 (System.getenv()를 확인하고 gradle 스크립트에서 출력)를 사용하여 자체 환경을 생성합니다.
https://code.google.com/p/android/issues/detail?id=65213

그러나 당신이 볼 수 있듯이, 구글은 그들이 환경 변수에서 IDE 사용하고 싶지 않았다 결정 :

나는 ENV를 사용하여 바르 때문에 cmd를 라인에서 좋은 빌드 여기에 버그로를 썼다 모든; 나는 그 결정에 울타리에 아직도있다. 그것은 내 인생을 괴롭 히 내 gradle 스크립트에로드 할 수있는 절대 경로를 가리 키도록 local.properties를 업데이트해야한다는 것을 어렵게 만듭니다. 그래도 나는 아직 어떻게해야 할지를 알지 못했지만 (그러나 그렇게 열심히 보지 않았습니다). 즉, 팀원에게 나와 같은 경로를 사용하거나, 링크를 가지고 놀거나, Repo를 가져올 때마다 모든 유형을 지정하거나 자동화 스크립트를 추가하도록 강요합니다. 마이크로 레벨에서는 작지만 매크로 레벨에서는 거대 할 수있는 환경에 의존하는 개발자에게는 시간이 걸릴 것이라는 잘못된 결정이라고 생각합니다.

groundloop, 나는 IDE가 프로젝트에 NDK 폴더 경로를 추가 할 수있는 기능이 곧 업데이트 될 것이라고 생각하며,이 파일에서 local.properties 파일을 자동 생성합니다 (적어도 이것을 생각하지 않았다). 구글에서 더 자세한 예를 들어

, 여기에 최근의 예는 (JNI 또는 NDK를 검색)은 다음과 같습니다 NDK 사용 https://docs.google.com/viewer?a=v&pid=sites&srcid=YW5kcm9pZC5jb218dG9vbHN8Z3g6NDYzNTVjMjNmM2YwMjhhNA


크로스 플랫폼 지방의 APK :

마지막으로, 거기 gradle을 사용하고 자신의 Android.mk 파일을 제공 할 수 없기 때문에 단 하나의 아키텍처에서 타사 네이티브 라이브러리 만 NDK로 연결할 수 있다는 단점이 있습니다. 나는 "link in"이라고 말했다. "abiFilters"명령을 사용하여 여러 아키텍처에서 NDK 모듈 (위의 moduleName)을 빌드 할 수 있으며, 동일한 APK를 여러 아키텍처에서 사용할 수 있도록 앱에 배치됩니다. 자신의 타사 라이브러리에 링크해야하거나 아키텍처에 따라 cFlags에 대해 다른 값을 사용해야하는 경우에는 간단한 방법이 아닙니다.

나는 다음을 시도해 보았지만 처음에는 작동하는 것처럼 보였다. 그렇지만 두 NDT 섹션 (또는 그런 식으로 여러 가지 아키텍처 라이브러리를 구축했다)에서 모든 것을 함께 추가하여 NDK를 구축했다는 것을 알게되었다. : 많은 슬픔이 Gradle을 네이티브 3 자 libs와 깨끗한 매너 지방 바이너리를 만들려고

android { 
    compileSdkVersion 23 
    buildToolsVersion '23.0.1' 
    defaultConfig { 
     minSdkVersion 14 
     targetSdkVersion 23 
     versionCode 28 
     versionName "3.0" 
    } 
    buildTypes { 
     def commonLibs = " -lfoo -lbar -lwhatever" 
     def armV7LibsDir = "/whatever/armv7a/libs" 
     def armX86LibsDir = "/whatever/x86/libs" 
     def armV7IncDir = "/whatever/armv7a/include" 
     def x86IncDir = "/whatever/x86/include" 
     debug { 
      ndk { 
       cFlags = "-I" + armV7IncDir 
       moduleName "myNativeCPPModule" 
       stl "stlport_shared" 
       abiFilter "armeabi-v7a" 
       ldLibs "log -L" + armV7LibsDir + commonLibs 
      } 
      ndk { 
       cFlags = "-I" + armX86IncDir 
       moduleName "myNativeCPPModule" 
       stl "stlport_shared" 
       abiFilter "x86" 
       ldLibs "log -L" + armX86LibsDir + commonLibs 
      } 
     } 
    } 
} 

후, 나는 마침내 구글 플레이의 내장 된 것을 APK가 멀티 아키텍처 지원 결론에 도달 정말입니다 어쨌든 가장 좋은 경로이므로 각 아키텍처에 대해 개별 APK를 만드십시오.

그래서 여러 가지 buildTypes 및 제품 맛을 만들지 않았으며 다음 코드를 추가하여 각 유형의 버전 코드를 생성했습니다.

// This is somewhat nasty, but we need to put a "2" in front of all ARMEABI-V7A builds, a "3" in front of 64-bit ARM, etc. 
// Google Play chooses the best APK based on version code, so if a device supports both X86 and 
// ARM, it will choose the X86 APK (preferred because Inky ARM running on an X86 with Houdini ARM Emulator crashes in our case) 
android.applicationVariants.all { variant -> 
    if (variant.buildType.name.equals('release')) { 
     variant.mergedFlavor.versionCode = 2000 + defaultConfig.versionCode 
    } else if (variant.buildType.name.equals('debug')) { 
     variant.mergedFlavor.versionCode = 2000 + defaultConfig.versionCode 
    } else if (variant.buildType.name.equals('debugArmV8a')) { 
     variant.mergedFlavor.versionCode = 3000 + defaultConfig.versionCode 
    } else if (variant.buildType.name.equals('releaseArmV8a')) { 
     variant.mergedFlavor.versionCode = 3000 + defaultConfig.versionCode 
    } else if (variant.buildType.name.equals('debugMips')) { 
     variant.mergedFlavor.versionCode = 5000 + defaultConfig.versionCode 
    } else if (variant.buildType.name.equals('releaseMips')) { 
     variant.mergedFlavor.versionCode = 5000 + defaultConfig.versionCode 
    } else if (variant.buildType.name.equals('debugMips64')) { 
     variant.mergedFlavor.versionCode = 6000 + defaultConfig.versionCode 
    } else if (variant.buildType.name.equals('releaseMips64')) { 
     variant.mergedFlavor.versionCode = 6000 + defaultConfig.versionCode 
    } else if (variant.buildType.name.equals('debugX86')) { 
     variant.mergedFlavor.versionCode = 8000 + defaultConfig.versionCode 
    } else if (variant.buildType.name.equals('releaseX86')) { 
     variant.mergedFlavor.versionCode = 8000 + defaultConfig.versionCode 
    } else if (variant.buildType.name.equals('debugX86_64')) { 
     variant.mergedFlavor.versionCode = 9000 + defaultConfig.versionCode 
    } else if (variant.buildType.name.equals('releaseX86_64')) { 
     variant.mergedFlavor.versionCode = 9000 + defaultConfig.versionCode 
    } 
} 

지금 모든 당신은 당신이 일반적으로하는 것처럼, 당신의 defaultConfig 객체에 versionCode를 값을 설정해야하고,이 빌드 유형에 따라, 아키텍처 별 버전 문자열의 말미에 추가합니다. 버전 문자열은 모든 빌드에서 동일하게 유지되지만 ARM에서 X86_64까지의 우선 순위를 제공하도록 코드를 변경합니다. 조금 익숙하지 않거나 하드 코딩되었지만 작업이 완료됩니다. 이 버전은 최대 999 가지 버전을 제공하므로 더 많은 정보가 필요하면 위의 숫자에 10을 곱하고 버전 코드에 넣을 수있는 최대 값이 무엇인지 잘 모릅니다.

제 경우에는 상당히 복잡한 빌드 시스템이 있습니다. 우리는 안드로이드 인 3 개의 아키텍처를위한 CPython을 빌드 한 다음, 우리 자신의 라이브러리를 만들고 각 아키텍처마다 단일 라이브러리로 모두 연결합니다. 우리는 Android.mk 파일 대신에 ndk 명령 행 빌드 도구, automake 및 python을 사용하여 모든 것을 빌드합니다. 최종 라이브러리는 단일 JNI 인터페이스 cpp 파일 (위의 myNativeCPPModule)에 링크됩니다. 클릭 한 번만하면 모든 것이 한꺼번에 제작되며, 아주 멋진 Android Studio입니다.

+0

매우 도움이됩니다. 정말 고마워. BTW, 사용자 정적 라이브러리에 대한 기본 검색 디렉토리를 알고 계십니까? 가능하면 ldLibs에서 "-I"의 트릭을 사용하지 않기로합니다. – weidongxu

+0

여기에 나열된 파일을 탐색/다운로드하는 방법은 다음과 같습니다. https://docs.google.com/viewer?a=v&pid=sites&srcid=YW5kcm9pZC5jb218dG9vbHN8Z3g6NDYzNTVjMjNmM2YwMjhhNA – Cliff

+0

예제 다운로드를 찾았지만 지금은 Gradle 1.11과 관련된 다른 문제에 직면하고 있습니다. 생성 된 Androud.mk 파일은 절대 경로를 사용합니다. http://stackoverflow.com/questions/23344567/ndk-dev-in-an-android-library-project-with-gradle-android-studio – Cliff

1

build.gradle에서 ndk를 설정하는 데 많은 시간을 보냈습니다. 내 문제를 해결 good blog 있어요.

0

안드로이드 스튜디오는 말한다 있기 때문에,