NDK研究ノート(1)
Android アプリケーションが Java で記述されていることは誰もが知っていますが、開発に C/C++ を使用する必要がある場合や、基礎となるレイヤーと対話する必要がある場合があります。Java 自体は JNI メソッドを提供しますが、Android 開発には技術的な障害があります。たとえば、プログラムが複雑になり、互換性の保証が難しくなり、Framework API にアクセスできなくなり、デバッグが難しくなります。そこでNDKが誕生しました。NDK の正式名称は Native Development Kit です。NDKのリリースにより、ついに「Java+C」開発手法が肯定的なものとなり、正式にサポートされる開発手法となった。NDK は、C 開発をサポートする Android プラットフォームの始まりとなります。
NDKを使用するメリット
- 移植が容易で、C/C++ で書かれたライブラリは他のプラットフォームでも簡単に再利用できます。
- コード保護。Java 層のコードは逆コンパイルされやすく、C/C++ ライブラリの逆コンパイルはより困難です。
- プログラムの実行効率を向上させるためには、C/C++を用いて高性能なアプリケーションロジックを開発する必要があり、アプリケーションプログラムの実行効率が向上します。
- 既存のオープン ソース ライブラリにアクセスするには、基礎となる API にアクセスするか、C/C++ のみを備えたいくつかのライブラリを参照する必要があります。
NDK環境を構成する
NDK をダウンロードした場合は、そのパス ( [ファイル] -> [プロジェクト構造] でAndroid NDK の場所)を設定できます。デフォルトは SDK パス\ndk-bundle です。ダウンロードされていない場合は、「ダウンロード」をクリックしてダウンロードしてください。もちろん、
他のバージョンをダウンロードして任意の場所に配置し、手動でパスを設定することもできます。
設定が成功すると、NDK パスがプロジェクトのlocal.propertiesファイルに追加されます。
ndk.dir=D\:\\Android\\Sdk\\ndk-bundle
そうでない場合は、手動で追加できます。また、ビルド時に以下のようなエラーが発生した場合は、
Error:(12, 0) Error: NDK integration is deprecated in the current plugin.
Consider trying the new experimental plugin.
For details, see http://tools.android.com/tech-docs/new-build-system/gradle-experimental.
Set "android.useDeprecatedNdk=true" in gradle.properties to continue using the current NDK integration.
これは、NDK バージョンと AS バージョンの間の不一致が原因で発生します。gradle.propertiesファイルの下に次の行を追加するだけですandroid.useDeprecatedNdk=true
。
(コマンド ラインを使用する場合は、システム環境変数を追加し、新しい変数名 ANDROID_NDK_HOME を作成する必要があります。対応する変数値は ndk ルート ディレクトリのアドレス、つまり $SDKpath\ndk-bundle です。次に、ANDROID_NDK_HOME をパス (%ANDROID_NDK_HOME% ; )に追加し、OK を確認します。)
ネイティブ ファイルを自分で作成する
さて、これで最初の NDK アプリケーションを作成できます。最も単純な例では、C は文字列を出力し、それを Android で呼び出して表示します。まずはいつものように New a Project です。次に、新しい Java クラスを作成し、JniUtils という名前を付けます。ここでダイナミックライブラリをロードし、ネイティブメソッドを作成します。(実際には、これを MainActivity で実行することもできますが、コードをより明確に見せるために、やはり個別に記述することにします。また、これはアプリ モジュール内では実行できず、別のモジュールで実行します。)ネイティブ
メソッド、つまり JNI インターフェイスは、C/C++ と Java の間のブリッジです。
public class JniUtils {
static{
System.loadLibrary("ndk");
}
public static native String getStringFromC();
}
この時点では、このメソッドの実装が見つからないため、getStringFromCの色は赤になっていることがわかります。次にこのメソッドを書いていきます。
デフォルトが Android ビューの場合は、プロジェクト ビューに調整します。$ProjectName/app/src/mainの下に、タイプがJNI Folderである新しいフォルダーを作成します。このディレクトリには主に jni ファイル、つまり c/cpp ファイルとヘッダー ファイルが保存されます。次に、C ファイルを作成するには、gradle バージョンに応じて 2 つの方法があります。個別に説明して比較してみましょう。
安定したプラグイン
プロジェクトの下の build.gradle が次のようになっている場合:
dependencies {
classpath 'com.android.tools.build:gradle:2.1.0'
}
プラグインの安定版です。
前の手順が完了したら、プロジェクトをクリーンアップして再ビルドして$ProjectName/app/build/intermediats/classes/ が存在するかどうかを確認し、ターミナルを開いてコマンドを入力します。
cd app/build/intermediates/classes/debug
次に、次のコマンドを入力します。
javah -jni com.example.yaoobs.ndkjnidemo.JniUtils
class/debug の下に追加のcom_example_yaoobs_ndkjnidemo_JniUtils.hがあることがわかります。これを以前に作成した JNI フォルダーに切り取り、新しい C/C++ ソース ファイルを作成し、jniUtils.cという名前を付けます。com_example_yaoobs_ndkjnidemo_JniUtils.hのメソッド名に従ってjniUtils.cを変更します。
実験的プラグイン (Gradle 実験的プラグイン)
Gradle Experimental Plugin は、プロジェクトの構成時間を短縮し、より優れた NDK サポートを提供するために、Gradle の新機能に基づいて開発されています。
(これは単なるプラグインであり、gradle 自体のサポートが必要であり、そのバージョンに対応していることに注意してください。作成者の gradle バージョンは 2.1.0 で、対応する gradle-experimental は 0.7.0 です。)では、最初に をビルドする必要があります
。gradle で gradle 参照を変更します。
buildscript {
...
dependencies {
// classpath 'com.android.tools.build:gradle:2.1.0'
classpath 'com.android.tools.build:gradle-experimental:0.7.0'
}
...
}
次に、モジュールの下の build.gradle を変更します。文法的な違いがいくつかあります。古いものはコメントアウトされています。比較できます。
//apply plugin: 'com.android.application'
apply plugin: 'com.android.model.application'
//
//android {
// compileSdkVersion 23
// buildToolsVersion "23.0.2"
// defaultConfig {
// applicationId "com.example.yaoobs.ndkjnidemo"
// minSdkVersion 14
// targetSdkVersion 23
// versionCode 1
// versionName "1.0"
// ndk {
// moduleName = "ndk"
// abiFilters 'armeabi', 'x86', 'armeabi-v7a', 'x86_64', 'arm64-v8a', 'mips', 'x86_64'
// }
// buildTypes {
// release {
// minifyEnabled false
// proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
// }
// }
//}
model {
android {
compileSdkVersion = 23
buildToolsVersion = "23.0.2"
defaultConfig.with {
applicationId = "com.example.yaoobs.ndkjnidemo"
minSdkVersion.apiLevel = 14
targetSdkVersion.apiLevel = 23
versionCode = 1
versionName = "1.0"
}
ndk {
moduleName = "ndk"
toolchain = 'clang' //必加项,否则c文件报错
CFlags.addAll(['-Wall'])
}
buildTypes {
release {
minifyEnabled = false
proguardFiles.add(file('proguard-rules.txt'))
}
}
productFlavors {
create("arm") {
ndk.abiFilters.add("armeabi")
}
create("arm7") {
ndk.abiFilters.add("armeabi-v7a")
}
create("arm8") {
ndk.abiFilters.add("arm64-v8a")
}
create("x86") {
ndk.abiFilters.add("x86")
}
create("x86-64") {
ndk.abiFilters.add("x86_64")
}
create("mips") {
ndk.abiFilters.add("mips")
}
create("mips-64") {
ndk.abiFilters.add("mips64")
}
create("all")
}
}
}
dependencies {
compile fileTree(dir: 'libs', include: ['*.jar'])
compile 'com.android.support.constraint:constraint-layout:1.0.0-alpha1'
compile 'com.android.support:appcompat-v7:23.0.0'
}
完了後、JniUtils.class にネイティブ メソッド getStringFromC() が見つかります。Alt+Enter を押すと、次の図に示すようにプロンプトが表示されます: Enter を押すと、JNI の下に作成された jniutils.c ファイルが見つかります
。フォルダ。
ネイティブファイルの書き込み
まず、Java のガイド パッケージと同様のヘッダー ファイルを追加します。ここで最初に追加するのは、Java と C/C++ 間の言語変換のコア ファイルである jni.h です。詳細については、D:\ を確認してください。 ndk ディレクトリ \ndk-bundle\platforms\android-23\arch-arm\usr\include\jni.h 内の android-sdk では、ここで別の文字列を処理する必要があるため、文字列のヘッダー ファイルもインクルードする必要があります。 h も上記のディレクトリにあります include .
次に、Java のネイティブ メソッドの実装を追加します。jstring:戻り値の型、Java_com_example_yaoobs_HelloJni_stringFromJNI(JNIEnv *env, jobject jobj):実装メソッド名、固定形式、Java_ 実装するメソッド名をアンダースコアに置き換えたJavaクラスの参照アドレス _ メソッド名(JNI環境変数) env、JNI 環境オブジェクト jobj); このうち、env メソッドと jobj メソッドは使用できませんが、ソース コードで説明されているように、宣言する必要もあります。一般的な機能は、env が JNINativeInterface ポインタとして使用されることです。 Java と C/C++ 間の関数 環境変数の真ん中にあるブリッジですが、最初は深くは説明しません。
完全な jniutils.c:
#include <string.h>
#include <jni.h>
jstring
Java_com_example_yaoobs_ndkjnidemo_JniUtils_getStringFromC( JNIEnv* env, jobject thiz ) {
return (*env)->NewStringUTF(env, "Hello NDK! ");
}
最後にアクティビティを呼び出します。
public class MainActivity extends Activity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
((TextView)findViewById(R.id.txt)).setText(JniUtils.getStringFromC());
}
}
正常に実行されました:
ダイナミックリンクライブラリの直接使用
ほとんどの場合、NDK を使用する場合はネイティブ ファイルを自分で作成する必要はなく、ダイナミック リンク ライブラリ、つまり .so ファイルを直接使用するだけです。多くのサードパーティ SDK は完全なソリューションを提供します。
AS に so ファイルが保存されるディレクトリは、デフォルトでは main/jniLibs の下にありますが、私たちは依然として so ファイルと jar パッケージを app/libs の下に置くことに慣れていますが、次のように gradle に設定を追加する必要があります。
android {
...
sourceSets {
main {
jniLibs.srcDirs = ['libs']
}
}
...
このようにして、システムがロードされると、libs ディレクトリに移動してロードされます。
先ほどの例に従うと、app\build\intermediates\ndk\debug\lib の下に、lib+modulename という名前が付いたさまざまなアーキテクチャの so ファイルが見つかります。
これらをすべて libs の下にコピーし、jniutils.c を削除して、実行時にエラーが発生するかどうかを確認できます。
ソース コードは GitHub にアップロードされています。アドレス: https://github.com/Yaoobs/NdkJniDemo
参考記事:
http://blog.csdn.net/yilip/article/details/45200861
http://www.jianshu.com/p/9aff422204eb
http://www.jianshu.com/p/d8cde65cb4f7
http:// www.taoweiji.cn/2016/08/02/ndk/?hmsr=toutiao.io&utm_medium=toutiao.io&utm_source=toutiao.io
http://www.cnblogs.com/zhuyuliang/p/5007016.html