本博客是基於Android Studio 1.3 preview版本,且默認你已經安裝了Android SDK, Android NDK。
用Android Studio新建一個工程叫Prompt,其目錄結構如下:
├── Prompt.iml
├── app
│ ├── app.iml
│ ├── build
│ ├── build.gradle
│ ├── libs
│ ├── proguard-rules.pro
│ └── src
├── build
│ └── intermediates
├── build.gradle
├── gradle
│ └── wrapper
├── gradle.properties
├── gradlew
├── gradlew.bat
├── local.properties
└── settings.gradle
切換到android視圖,可見如下目錄:
第一步,編寫JNI代碼:
1、新建jni文件夾,在jni文件夾下創建logger.h,用來打印輸出日志的,其內容如下:
#ifndef PROMPT_LOGGER_H_H #define PROMPT_LOGGER_H_H #include <jni.h> #include <android/log.h> /** * 定義log標簽 */ #define TAG "jni_logger" /** * 定義info信息 */ #define LOGI(...) __android_log_print(ANDROID_LOG_INFO, TAG, __VA_ARGS__) /** * 定義debug信息 */ #define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG, TAG, __VA_ARGS__) /** * 定義error信息 */ #define LOGE(...) __android_log_print(ANDROID_LOG_ERROR, TAG, __VA_ARGS__) #endif //PROMPT_LOGGER_H_H
2、接着創建prompt_jni.c主要用來注冊綁定java函數和native函數,以及java函數在c中相應函數的具體實現, 內容如下:
#include "logger.h" #ifndef NULL #define NULL ((void *) 0) #endif /** * 獲取數組的大小 */ #define NELEM(x) ((int) (sizeof(x) / sizeof((x)[0]))) #define JNIREG_CLASS "com/ndk/clarck/prompt/MainActivity" /** * 返回字符串 */ JNIEXPORT jstring JNICALL native_getLine(JNIEnv *env, jobject obj, jstring prompt) { char outbuf[128]; int len = (*env)->GetStringLength(env, prompt); (*env)->GetStringUTFRegion(env, prompt, 0, len, outbuf); return (*env)->NewStringUTF(env, outbuf); } /** * Java和JNI函數綁定 */ static JNINativeMethod method_table[] = { {"getLine", "(Ljava/lang/String;)Ljava/lang/String;", (void*)native_getLine}, }; /** * 注冊native方法到java中 */ static int registerNativeMethods(JNIEnv *env, const char* className, JNINativeMethod* gMethods, int numMethods) { jclass clazz; clazz = (*env)->FindClass(env, className); if (clazz == NULL) { return JNI_FALSE; } if ((*env)->RegisterNatives(env, clazz, gMethods, numMethods) < 0) { return JNI_FALSE; } return JNI_TRUE; } /** * 調用注冊方法 */ int register_ndk_load(JNIEnv *env) { return registerNativeMethods(env, JNIREG_CLASS, method_table, NELEM(method_table)); } JNIEXPORT jint JNI_OnLoad(JavaVM *vm, void* reserved) { JNIEnv* env = NULL; jint result = -1; if ((*vm)->GetEnv(vm, (void**)&env, JNI_VERSION_1_4) != JNI_OK) { return result; } register_ndk_load(env); return JNI_VERSION_1_4; }
3、java層調用如下:
package com.ndk.clarck.prompt; import android.app.Activity; import android.os.Bundle; import android.util.Log; public class MainActivity extends Activity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); String getLine = getLine("Type a line:"); Log.d("Test", "getLine: " + getLine); } public native String getLine(String prompt); static { System.loadLibrary("prompt_jni"); } }
第二步,配置如下環境,執行編譯命令:
1、在local.properties配置SDK和NDK路徑如下:
sdk.dir=xxxx
ndk.dir=xxx
2、打開gradle-wrapper.properties,將其配置修改為使用Gradle 2.5來編譯(詳情參考:http://www.cnblogs.com/tanlon/p/4731283.html):
#Mon Aug 17 20:34:50 HKT 2015 distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists distributionUrl=https\://services.gradle.org/distributions/gradle-2.5-all.zip
3、配置Project下面的build.gradle,其內容如下:
// Top-level build file where you can add configuration options common to all sub-projects/modules. buildscript { repositories { jcenter() } dependencies { classpath 'com.android.tools.build:gradle-experimental:0.2.0' // NOTE: Do not place your application dependencies here; they belong // in the individual module build.gradle files } } allprojects { repositories { jcenter() } }
4、配置Module下面的build.gradle,其內容如下:
apply plugin: 'com.android.model.application' model { android { compileSdkVersion = 21 buildToolsVersion = "22.0.1" defaultConfig.with { applicationId = "com.ndk.clarck.prompt" minSdkVersion.apiLevel = 15 targetSdkVersion.apiLevel = 21 } } android.ndk { moduleName = "prompt_jni" ldLibs += ["log"] } android.buildTypes { release { minifyEnabled = false proguardFiles += file('proguard-rules.pro') } } android.productFlavors { create ("arm7") { ndk.abiFilters += "armeabi-v7a" } create ("arm8") { ndk.abiFilters += "arm64-v8a" } create ("x86-32") { ndk.abiFilters += "x86" } // for detailed abiFilter descriptions, refer to "Supported ABIs" @ // https://developer.android.com/ndk/guides/abis.html#sa // build one including all cpu architectures create("all") } }
5、執行Build->Make Project,得到如下輸出:
1:27:09 PM Executing tasks: [:app:compileAllDebugSources, :app:compileAllDebugAndroidTestSources]
1:27:10 PM Gradle build finished in 779ms
6、執行Run,即可運行項目了。
經驗:
1、遇到Gradle sync failed: Cause: org.gradle.api.internal.ExtensibleDynamicObject這種錯誤的解決辦法:
將Module下的build.gradle所有編譯參數賦值都是采用xxx = xxx這種方式,而不能采用 xxx xxx這種賦值的方式
2、Android Studio Gradle編譯確實比Eclipse中采用Android Makefile要方便很多。