web-dev-qa-db-ja.com

Android Gradle plugin 0.7でNDKを設定する方法

新しいAndroid gradleプラグイン(0.7)にはNDKの新しいサポートが含まれているようですが、ドキュメントでは言及されていません(私が見つけた唯一の参照はテストと呼ばれます- ndkSanAngeles )。

GradleはNDKを探しているようです。NDKはPATHに含まれています。ただし、プロジェクトのビルドは失敗します

  • 問題点:タスク ':OGLTests:compileDefaultFlavorDebugNdk'の実行に失敗しました。 NDKが構成されていません

NDKをgradleで設定するにはどうすればよいですか?

私の現在の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'
        }
    }
}

ありがとう。

48
user1906

答えを見つけました。 ndk.dir=path/to/ndkファイルにlocal.propertiesを含めるとうまくいきました。

更新: Android Studioの最新バージョンでは、プロジェクト構造> SDKの場所で値を直接設定できます。

34
user1906

Gradleプラグインコードを調べてみると、NDKとビルド済みネイティブライブラリの両方を使用するのに役立つ次のことがわかりました。

事前構築済みネイティブライブラリ内のリンクを単純にするには、ndkセクションをタスクに追加するだけです。たとえば、productFlavorsに追加しました。 abiFilterは、ライブラリが保存されるフォルダー名です。abiFiltersは、コンマ区切りリストの両方のライブラリが最終APKに追加されることを意味します(したがって、理論的には "armeabi"、 "armeabi-v7a"、 "x86"、および " mips」はすべて1つのAPKに含まれ、O/Sはインストール時にサポートされているアーキテクチャライブラリを選択します):

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

この例では、arm buildはV5およびV7A arm libを含むAPKを作成し、x86 buildはx86 libのみを含む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

次に、次のようにJavaでロードできます。

static
{
    loadLibrary("myNative");
}

ここで、あるネイティブライブラリが別のネイティブライブラリに依存しているとします。 (最小APIをAPI 17以下に設定する場合)最初に依存ライブラリをロードする必要があります。

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

DefaultConfigまたはbuildType(debugやreleaseなど、使用できるもの)にndk {}セクションを配置することもできます。例えば:

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

事前ビルドとは、ダウンロードしたサードパーティのライブラリ、またはNDKツールチェーンまたは独自のARMツールチェーン(ndk-buildスクリプト自体ではない)を使用してビルドしたライブラリを意味します。

API 18では、アプリケーションのlibディレクトリ(セキュリティ上の理由など)を知らなかったため、ネイティブlibローダーが依存関係を「自動的に」ロードできないという長年のアーキテクチャ上の問題を修正しました。 API 18以降では、myNativeが上記のmyDependencyに依存する場合、loadLibrary( "myNative")を呼び出すだけで、OSがmyDependencyのロードを処理します。ただし、API 17以下を実行するデバイスの市場への浸透が許容できる数に達するまで、これに依存しないでください。


明示的にソースからNDKライブラリをビルドの現在のバージョンAndroid Studioでは、次の操作を実行できます。

前述のとおり、local.propertiesのndk.dir値をNDKホームを指すように設定します。 local.propertiesでenv変数を直接使用できるかどうか誰もが知っていますか? :)

Build.gradleファイルで、次のようなものをタスクに追加します(これもdefaultConfig、debug、release、productFlavorなど)。

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

これは、現在サポートされているタイプ(moduleName、stl、ldLibs、およびcFlags)の基本構造です。私は見たが、これ以上は見つけられなかった。 ldLibsでは、上記の各フィールドの前に「-l」が自動的に追加されるため、問題があると考えられます。 ldLibs "log -lz -lm -Wl、-whole-archive -l/path/to/someOtherLib -Wl、-no-whole-archive"と言って、あなたはそれをだますことができます(私はしなければなりませんでした)。

この行では、最初のパラメーターの最後にタグを付けて、-lで始まらないパラメーターを追加しているだけなので、今のところは先に進んでください。上記のケースでは、Java内から使用するために、静的ライブラリ全体をNDKモジュールにリンクしています。 Google開発者に追加機能を追加して、独自のAndroid.mkファイルをNDKビルドプロセスにマージする機能を追加するよう依頼しましたが、これはすべて新しいため、しばらく時間がかかる場合があります。

現在、build.gradleに配置するものはすべてtempビルドディレクトリを削除し、毎回再作成します。したがって、gradle Androidプラグインソースコード(これは楽しいです)をダウンロードして変更する場合を除き、ビルドにコピーするために必要な「make due」がいくつかあります。このndkサポートを本質的に提供するAndroid gradleスクリプトは、Android.mkファイルを生成し、一時ディレクトリにあるNDKシステム。

しばらく脇道に。 moduleNameは、プロジェクトのjniディレクトリの下のcまたはcppファイルと一致する必要があります。

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

C++のstlportライブラリを使用する場合は、stl値を「stlport_shared」または「stlport_static」の値に設定する必要があります。拡張C++サポートが必要ない場合は、stlを省略できます。 Androidはデフォルトで非常に基本的なC++サポートを提供します。他のサポートされているC++ライブラリについては、ダウンロードしたNDKのNDKドキュメントガイドラインを参照してください。ここでstlport_sharedに設定すると、gradleはlibstlport_sharedをコピーします) NDKのsources/cxx-stl/stlport/libsディレクトリからAPKのlibディレクトリへのlibです。また、コンパイラのインクルードパスも処理します(技術的にはgradleはこのすべてを行いませんが、Android NDK build system)。したがって、独自のstlportのコピーをjniLibsディレクトリに入れないでください。

最後に、cFlagsはかなり明白だと思います。

Mac OSXでAndroid_NDK_HOMEを設定することはできません(以下を参照)が、私がやったいくつかの調査から、これは他のOSでもまだ動作するようです。ただし、削除されます。

コメントしたかったのですが、まだ評判がありません。 Dennis、環境変数は、単にオーバーライドされるのではなく、完全に無視されます。実際、環境変数を取得することはありません。私が知ることができることから、Android Studio IDEいくつかの特定の環境変数を使用して独自の環境を作成します(System.getenv()をチェックして出力します) gradleスクリプトから)。

Env varsを使用するとcmd行からうまくビルドできるので、これをバグとしてここに書きました。
https://code.google.com/p/Android/issues/detail?id=6521

しかし、ご覧のとおり、GoogleはIDEによって環境変数が使用されることを望んでいないと判断しました。私はまだその決定をフェンスで守っています。 local.propertiesを更新して、gradleスクリプトに読み込むことができる絶対パスを指すようにしますが、これはまだ方法を理解していません(しかし、それほど難しくは見ていません)。私と同じパスを使用し、リンクを再生し、レポジトリを取得するたびにすべて入力するか、自動化スクリプトを追加します。環境変数に依存する開発者にとっては時間がかかる悪い判断だと思いますマイクロレベルでは小さく、マクロレベルでは巨大になります。

グラウンドループ、IDEはNDKフォルダーパスをプロジェクトに追加する機能ですぐに更新され、それからlocal.propertiesファイルを自動生成します(少なくともそうではありません)彼らがこれを考えていなかったならば、tは理にかなっています)。

Googleの詳細な例については、最新の例をご覧ください(jniまたはndkを検索): https://docs.google.com/viewer?a=v&pid=sites&srcid=YW5kcm9pZC5jb218dG9vbHN8Z3g6NDYzNTVjMjNmM2YwMjhhNA


NDKを使用したクロスプラットフォームのファットAPK:

最後に、gradleを使用し、独自のAndroid.mkファイルを提供できないため、サードパーティのネイティブライブラリで単一のアーキテクチャからNDKにしかリンクできないという欠点があります。 「リンクイン」と言ったことに注意してください。 「abiFilters」コマンドを使用して、いくつかのアーキテクチャでNDKモジュール(上記のmoduleName)を構築できます。これらのモジュールは、同じAPKを複数のアーキテクチャで使用できるようにアプリに配置されます。独自のサードパーティライブラリをリンクする必要がある場合、またはアーキテクチャに応じてcFlagsの値が異なる場合でも、簡単な方法はありません。

次のことを試してみたところ、最初は動作しているように見えましたが、2つのndkセクションからすべてを追加することでNDKを構築するだけでした(またはそのようなもので、何らかの方法で複数のアーキテクチャライブラリを構築しましたしかし):

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
            }
        }
    }
}

Gradleとネイティブのサードパーティライブラリを使用してクリーンなマナーでファットバイナリを作成しようとして多くの悲しみを味わった後、私はついにGoogle Playの組み込みAPK用マルチアーキテクチャサポートが本当に最適なルートであるという結論に達しました各アーキテクチャの個別のAPK。

そこで、製品フレーバーを含まない複数のbuildTypeを作成し、次のコードを追加して、各タイプのバージョンコードを生成しました。

// 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を掛けてください。バージョンコードに入力できる最大値は不明です。

私の場合、かなり複雑なビルドシステムがあります。 9つのアーキテクチャ(うち3つはAndroid)向けにCPythonを構築し、独自のライブラリを多数構築し、それらをすべてアーキテクチャごとに1つのライブラリにリンクします。 Android.mkファイルの代わりに、ndkコマンドラインビルドツール、automake、およびpythonを使用してすべてをビルドします。最終的なライブラリは、上記のmyNativeCPPModuleと呼ばれる単一のJNIインターフェイスcppファイルにリンクされます)。ボタンを1回クリックするだけで、すべてが一度に構築され、非常に素晴らしいAndroid Studio。

76
reactive-core

android_NDK_HOME環境変数を設定することもできます

15
stefan.nsk

Build.gradleでndkを設定するのに多くの時間を費やしました。 良いブログ 問題を解決しました。

1
peacepassion

前にコメントしたように、local.propertiesにndk.dir =を追加すると役立ちます。興味深いことに、local.propertiesが環境変数Android_NDK_HOMEに設定された値を上書きすることがわかりましたlocal.propertiesでndk.dirが構成されていない場合でも。 (少なくともgradle Android plugin v 0.7.3の場合)。

これはAndroid Studioはlocal.propertiesを上書きでき、ndk.dirを設定する方法を提供していないようです:(

0
groundloop

Androidスタジオでは、ndkへのパスをlocal.propertiesに含めることを提案しています

0
Twinkle Mishra