Android Studio NDK 学习之接受Java传入的字符串
2015-08-18 13:30 雪夜&流星 阅读(2311) 评论(0) 编辑 收藏 举报本博客是基于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要方便很多。