89
Help us understand the problem. What are the problem?

More than 5 years have passed since last update.

posted at

updated at

AndroidStudioでNDK(JNI)を活用する 準備編

目的

  • セキュリティの向上
    • Androidのapkパッケージは、ProGuardで難読化が行われます。しかし、難読化は暗号化ではないので、ソースコード上に、暗証番号などを記載していた場合、リバースエンジニアリングですぐに見破られてしまいます。
    • Native(C,C++)のソースコード内に暗証番号を記載し、それをJavaから呼び出すことで、隠蔽することができます。
  • 高速化
    • AndroidのDalvik VM(仮想マシンで)は、VMであるがゆえに処理速度で越えられない壁が存在します。この越えられない壁を越えなければいけない状況に陥った時に、OSが直接実行できるバイトコードにできるNDKが有効です。
    • ほとんどの場合、Javaのプログラムの最適化で済むと思いますが、端末内で画像処理を行うなどの場合には、選択肢に入ってくると思います。
  • iOS,Androidでのライブラリの共通化
    • C,C++でソースコードを書くことができるので、iOS(Objective-C)からも読み込むことができますし、AndroidでもJNIの型にマッチさせれば、読み込むことができます。内部ロジックはそのままで、インターフェイスの書き換え(もしくは、ラップ)のみで両OSから、利用することができます。
    • 注意点としては、環境によってバイトオーダー(ビッグエンディアン, リトルエンディアン)が異なります。

環境構築(2015/10/10現在)

Android NDK Preview
Android Studio 1.3以上、Gradle 2.5以上が必須のようです。
今回は、以下の環境で試しました。

  • Android Studio 1.4
  • Gradle 2.5
  • Android NDK r10e

Gradleのバージョンアップ

Android Studio 1.4で、新規プロジェクトを作成した場合、Gradleのバージョンは、2.4でした。
1. Android Studioを起動
2. 左メニュー(Project)の『app』を<<右クリック>>、『Open Module Settings』を選択します。
3. 『Project』の”Gradle version”を最新のGradleにします。今回は、『2.5』と入力しました。
4. "Gradle project sync in progress..."と表示され、自動的にバージョンアップが行われます。
5. 5分ほどで完了します。

Android NDKのダウンロード

  1. AndroidStudioを起動
  2. 左メニュー(Project)の『app』を<<右クリック>>、『SDK Location』を選択します。
  3. "Android SDK Location:"の下にある『Download Android NDK』をクリックします。
  4. 20分ほどで完了します。

Experimental Pluginの適用

Experimental Plugin User Guide

『gradle-wrapper.properties』修正

  • 変更前
/gradle/wrapper/gradle-wrapper.properties
distributionUrl=https\://services.gradle.org/distributions/gradle-2.4-all.zip
  • 変更後
/gradle/wrapper/gradle-wrapper.properties
distributionUrl=https\://services.gradle.org/distributions/gradle-2.5-all.zip

プロジェクトの『build.gradle』修正

  • 変更前
/build.gradle
buildscript {
    repositories {
        jcenter()
    }
    dependencies {
        classpath 'com.android.tools.build:gradle:1.3.0'
    }
}

  • 変更後(実験的……。まぁ、実験好きですけど)
/build.gradle
buildscript {
    repositories {
        jcenter()
    }
    dependencies {
        classpath 'com.android.tools.build:gradle-experimental:0.2.0'
    }
}

appの『build.gradle』の修正

  • 変更前
/app/build.gradle
apply plugin: 'com.android.application'

android {
    compileSdkVersion 23
    buildToolsVersion "23.0.1"

    defaultConfig {
        applicationId "info.*****.sbx.ndk01"
        minSdkVersion 22
        targetSdkVersion 23
        versionCode 1
        versionName "1.0"
    }
    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
        }
    }
}

dependencies {
    compile fileTree(dir: 'libs', include: ['*.jar'])
    testCompile 'junit:junit:4.12'
    compile 'com.android.support:appcompat-v7:23.0.1'
    compile 'com.android.support:design:23.0.1'
}
  • 変更後
    • pluginが変わるので、そもそものDSL(記述方法)が変わります。
    • 今回は、『hello.c』というファイルを『libhello.so』というダイナミックリンクライブラリにコンパイルして、Android内から利用する想定です。
/app/build.gradle
apply plugin: 'com.android.model.application'

model {
    android {
        compileSdkVersion = 23
        buildToolsVersion = "23.0.1"

        defaultConfig.with {
            applicationId = "info.*****.sbx.ndk01"
            minSdkVersion.apiLevel = 22
            targetSdkVersion.apiLevel = 23
        }
    }

    /*
     * native build settings
     */
    android.ndk {
        moduleName = "hello"
        /*
         * Other ndk flags configurable here are
         * cppFlags += "-fno-rtti"
         * cppFlags += "-fno-exceptions"
         * ldLibs    = ["android", "log"]
         * stl       = "system"
         */
    }
    android.buildTypes {
        release {
            minifyEnabled = false
            proguardFiles  += file('proguard-rules.txt')
        }
    }
    android.productFlavors {
        create("arm") {
            ndk.abiFilters += "armeabi"
        }
        create("arm7") {
            ndk.abiFilters += "armeabi-v7a"
        }
        create("arm8") {
            ndk.abiFilters += "arm64-v8a"
        }
        create("x86") {
            ndk.abiFilters += "x86"
        }
        create("x86-64") {
            ndk.abiFilters += "x86_64"
        }
        create("mips") {
            ndk.abiFilters += "mips"
        }
        create("mips-64") {
            ndk.abiFilters += "mips64"
        }
        // To include all cpu architectures, leaves abiFilters empty
        create("all")
    }

}
dependencies {
    compile fileTree(dir: 'libs', include: ['*.jar'])
    compile 'com.android.support:appcompat-v7:23.0.1'
    compile 'com.android.support:design:23.0.1'
}
  • "Sync Now!"

JNI(Java Native Interface)で、Hello World!

ライブラリファイルの作成

  • JNI用のフォルダを作成します
    • app/src/main/jni/
  • hello.cを作成します
    • ヘッダファイル(.h)は不要。
    • この時重要なのは関数名です。これを間違えると、時間だけが浪費されます。
    • JNI(Java Native Interface)の名が示す通り、ビルド時に命名規則に従ってインターフェイスをマップしてくれます。
    • Java_{プロジェクトディレクトリ}{呼び出し元クラス名}{メソッド名}
app/src/main/jni/hello.c
#include <string.h>
#include <jni.h>

jstring
Java_info_*****_sbx_ndk01_MainActivity_stringFromJNI( JNIEnv* env, jobject thiz ) {
    return (*env)->NewStringUTF(env, "Hello JNI !");
}

MainActivityからの呼び出し

MainActivity.java
public class MainActivity extends AppCompatActivity {

    // hello.soの読み込み
    static {
        System.loadLibrary("hello");
    }

    // helloライブラリにあるメソッドの定義
    public native String  stringFromJNI();

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        // メソッドの呼び出し
        // Hello JNI !と表示される
        Log.i("", stringFromJNI());
    }
}

参照

ちょっといっぷく

  • 実際のダイナミックリンクライブラリはどこにあるのか?
    • デバッグビルドだと以下のディレクトリにあるようです。
    • app/build/intermediates/binaries/debug/all/obj/armeabi-v7a/libhello.so
  • Eclipse + NDK
    • 3~4年前、実際の製品を作った時には、Windows + Eclipse + NDKで、Cソースを書き換えるたびにCygwinでビルドして、その後、Eclipseでビルドして、と手間が多かったですが、AndroidStudioでは、二つのビルドを一気にやってくれます。
    • 基本的に、『.so』ファイルに変更がなければ、そのままリリースしていましたが、何かの拍子に同封し忘れて(警告はでない)すとんと落ちていたということがありました。AndroidStudioでは、ワンストップなので、きっとそんなこともなくなるでしょう。
  • 使い所
    • Googleのサンプルを見る限り、やはりマルチメディア(音声、画像、3Dモデリング)の扱いが多いようです。

つづく

  • JNIの型とデータ構造(予定)
  • デバッグ方法(予定)
  • NDKを利用した暗証番号の隠蔽(予定)
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Sign upLogin
89
Help us understand the problem. What are the problem?