使用AndroidStudio编译NDK的方法及错误解决方式
參考资料:
【android ndk】macos环境下Android Studio中利用gradle编译jni模块及配置:http://demo.netfoucs.com/ashqal/article/details/21869151
ANDROID STUDIO, GRADLE AND NDK INTEGRATION:http://ph0b.com/android-studio-gradle-and-ndk-integration/
Gradle Plugin User Guide:http://tools.android.com/tech-docs/new-build-system/user-guide
New Build System:http://tools.android.com/tech-docs/new-build-system
实践证明:
0.4.2仅仅有在gradle1.10版本号下创建仅仅包括AndroidLibrary模块的project时才干正常编译,gradle1.9版本号不能够。
0.4.6使用gradle1.10能够。
0.5.0不管是gradle1.10还是gradle1.11版本号都能够生成so库。
0.5.5的不能编译NDK,不管是gradle1.10还是gradle1.11版本号都不能生成so库,屙血尿脓。
下载AndroidStudio:
AndroidStudio的历史版本号下载列表:http://tools.android.com/download/studio/canary
下载NDK:
下载链接:http://developer.android.com/tools/sdk/ndk/index.html,注意NDK一定要r9+版本号的,否则编译时会出现例如以下错误:
Execution failed for task ':hellojni:compileDebugNdk'. > com.android.ide.common.internal.LoggedErrorException: Failed to run command: D:\ndk\ndk-build.cmd NDK_PROJECT_PATH=null APP_BUILD_SCRIPT=F:\androidstudio\test\hellojni\build\ndk\debug\Android.mk APP_PLATFORM=android-19 NDK_OUT=F:\androidstudio\test\hellojni\build\ndk\debug\obj NDK_LIBS_OUT=F:\androidstudio\test\hellojni\build\ndk\debug\lib APP_ABI=armeabi,armeabi-v7a Error Code: 2 Output: D:/ndk/build/core/setup-app.mk:63: *** Android NDK: Aborting . Stop.
下载gradle:
gradle-1.9-all.zip:http://download.csdn.net/detail/xxhongdev/6834859
gradle-1.10-all.zip:http://download.csdn.net/detail/xinghuacheng/7026815
gradle-1.11-all.zip:http://download.csdn.net/detail/d1387968/7097249
通过“AndroidStudio历史版本号下载列表”下载的历史版本号一般是绿色的压缩包,能够直接解压缩使用,可是不包括SDK,须要额外下载SDK,因为之前下载了ADT(版本号:adt20131030),所以后面直接使用ADT文件夹下的SDK。通过http://developer.android.com/sdk/installing/studio.html首页下载的AndroidStudio为安装版本号,包括了SDK,能够下载后直接安装,首次使用创建项目会比較慢,能够參考“AndroidStudio创建项目时一直处于building“project
name”gradle project info的解决的方法”来解决。
创建项目:
执行AndroidStudio后,创建新项目,新项目会有一个默认的Module,这里项目名称为JNIDemo,Module为app。
然后通过向导完毕项目的创建。
AndroidStudio还是很慢的,长时间处于这样的状态:
经过漫长的等待后最终完毕项目的创建,然后在这个项目下创建一个Module,New Module->Android Library:
不勾选“Create activity”然后点击“Finish”完毕创建,此时项目结构如图:
app和hellojni均为JNIDemo下的两个Module,这里把hellojni作为生成so库的NDK开发层,把app作为调用so库的APK引用开发层。
在hellojni模块的src/main下创建jni文件夹,并在jni文件夹下新建文件main.cpp,代码例如以下:
#include <stdio.h> #include <stdlib.h> #include <jni.h> #include <assert.h> #include <sys/types.h> #include <android/log.h> #define LOG_TAG "Hellojni" #define LOGE(...) __android_log_print(ANDROID_LOG_ERROR, LOG_TAG, __VA_ARGS__) #define LOGI(...) __android_log_print(ANDROID_LOG_INFO, LOG_TAG, __VA_ARGS__) //注冊native api的类#define JNIREG_CLASS "com/example/test9/app/MainActivity" extern "C" { JNIEXPORT void msg(JNIEnv *env, jobject clazz, jstring str); }; //jstring to char* char* jstringTostring(JNIEnv* env, jstring jstr) { char* rtn = NULL; jclass clsstring = env->FindClass("java/lang/String"); jstring strencode = env->NewStringUTF("utf-8"); jmethodID mid = env->GetMethodID(clsstring, "getBytes", "(Ljava/lang/String;)[B"); jbyteArray barr= (jbyteArray)env->CallObjectMethod(jstr, mid, strencode); jsize alen = env->GetArrayLength(barr); jbyte* ba = env->GetByteArrayElements(barr, JNI_FALSE); if (alen > 0) { rtn = (char*)malloc(alen + 1); memcpy(rtn, ba, alen); rtn[alen] = 0; } env->ReleaseByteArrayElements(barr, ba, 0); return rtn; } JNIEXPORT void msg(JNIEnv *env, jobject clazz, jstring str) { char *pszstr = NULL; pszstr = jstringTostring(env, str); LOGI("%s", pszstr); free(pszstr); } /** * Table of methods associated with a single class. */static JNINativeMethod gMethods[] = { { "msg", "(Ljava/lang/String;)V", (void*)msg}, }; /* * Register native methods for all classes we know about. */static int registerNativeMethods(JNIEnv* env) { int nError = 0; jclass clazz = NULL; clazz = env->FindClass(JNIREG_CLASS); if (clazz == NULL) { LOGE("clazz is null"); return JNI_FALSE; } nError = env->RegisterNatives(clazz, gMethods, sizeof(gMethods) / sizeof(gMethods[0]) ); if ( nError < 0 ) { LOGE("RegisterNatives error: %d num: %d",nError, sizeof(gMethods) / sizeof(gMethods[0]) ); return JNI_FALSE; } return JNI_TRUE; } /* * Set some test stuff up. * * Returns the JNI version on success, -1 on failure. */ JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM* vm, void* reserved) { JNIEnv* env = NULL; jint result = -1; if(vm->GetEnv((void**) &env,JNI_VERSION_1_6) != JNI_OK){ return -1; } assert(env != NULL); if (!registerNativeMethods(env)) { LOGE("registerNativeMethods failed"); return -1; } /* success -- return valid version number */ result = JNI_VERSION_1_6; return result; }
打开local.properties,设置正确的SDK路径和NDK路径:
sdk.dir=D\:/adt20131030/sdk ndk.dir=D\:/ndk
打开项目gradle/wrapper文件夹下的gradle-wrapper.properties文件,改动:
#Wed Apr 10 15:27:10 PDT 2013 distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists distributionUrl=http\://services.gradle.org/distributions/gradle-1.9-all.zip
#Wed Apr 10 15:27:10 PDT 2013 distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists distributionUrl=http\://services.gradle.org/distributions/gradle-1.10-all.zip
// Top-level build file where you can add configuration options common to all sub-projects/modules. buildscript { repositories { mavenCentral() } dependencies { classpath 'com.android.tools.build:gradle:0.7.+' } } allprojects { repositories { mavenCentral() } }
// Top-level build file where you can add configuration options common to all sub-projects/modules. buildscript { repositories { mavenCentral() } dependencies { classpath 'com.android.tools.build:gradle:0.9.+' } } allprojects { repositories { mavenCentral() } }
0.7.0 Requires Gradle 1.9 Requires Studio 0.4.0
0.9.0 Compatible with Gradle 1.10 and 1.11 Using Gradle 1.11 requires Android Studio 0.5.0
另外还须要注意的是gradle1.9下没有buildTypes标签,须要将debug、release标签直接放在android标签内,在gradle1.10下debug、release须要放在buildTypes标签内,buildTypes在android内。这里hellojni配置的build.gradle文件内容例如以下:
assert gradle.gradleVersion >= "1.10" apply plugin: 'android-library' android { compileSdkVersion 19 buildToolsVersion "19.0.3" defaultConfig { minSdkVersion 8 targetSdkVersion 16 versionCode 1 versionName "1.0" } buildTypes { release { runProguard false proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.txt' ndk { moduleName "hellojni" abiFilters "armeabi", "armeabi-v7a", "x86" } } debug { ndk { moduleName "hellojni" //stl "stlport_shared" ldLibs "log", "z", "m" //cFlags "-Wall -Wextra -I " + projectDir + "/src/main/jni/include" abiFilters "armeabi", "armeabi-v7a", "x86" } } } productFlavors { x86 { versionCode Integer.parseInt("6" + defaultConfig.versionCode) ndk { abiFilter "x86" } } mips { versionCode Integer.parseInt("4" + defaultConfig.versionCode) ndk { abiFilter "mips" } } armv7 { versionCode Integer.parseInt("2" + defaultConfig.versionCode) ndk { abiFilter "armeabi-v7a" } } arm { versionCode Integer.parseInt("1" + defaultConfig.versionCode) ndk { abiFilters "armeabi", "armeabi-v7a" } } fat } } dependencies { compile 'com.android.support:appcompat-v7:19.+' compile fileTree(dir: 'libs', include: ['*.jar']) }
注意这里的Android.mk文件每次编译都会又一次由工具自己主动生成,而非手动编辑的,我认为这一点设计就比較差劲。比如假设想要使用log输出函数__android_log_print,须要加入“LOCAL_LDLIBS := -llog”,则在build.gradle文件里加入例如以下的配置:
debug { ndk { ldLibs "log" } }
右键project选择Open Module Settings,选择Modules-app,打开Dependencies选项卡点击“+”号,选择Module dependency,在打开的对话框中选择hellojni。
可是測试发现设置依赖没有效果,假设直接编译app,hellojni并没有编译,仍须要手动编译hellojni。
调用native函数:
app项目中,在MainActivity类中声明native函数:
public native void msg(String str);
static {
System.loadLibrary("hellojni");
}
在MainActivity::onCreate中调用native函数打印一句log:
@Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); msg("MainActivity onCreate"); }
还须要将hellojni生成的so库文件打包进apk,仍须要配置build.gradle文件,加入:
task copyNativeLibs(type: Copy) { from fileTree(dir: '../hellojni/build/ndk/arm/debug/lib', include: 'armeabi/*.so') into 'build/lib' } tasks.withType(Compile) { compileTask -> compileTask.dependsOn copyNativeLibs } clean.dependsOn 'cleanCopyNativeLibs' tasks.withType(com.android.build.gradle.tasks.PackageApplication) { pkgTask -> pkgTask.jniFolders = [new File(buildDir, 'lib')] }
參考:“Android Studio加入so库”http://blog.csdn.net/caesardadi/article/details/18264399
当中copyNativeLibs任务是从相对app的项目路径'../hellojni/build/ndk/arm/debug/lib'下复制全部armeabi子文件夹的so文件到本项目build文件夹下的lib文件夹中,运行效果:这样最后打包生成的apk包才会包括有hellojni的so库文件。
測试:
编译执行app,apk安装完成执行时输出log信息:
后面列出了可能出现的gradle错误以及解决方式,以供參考。
错误:
Execution failed for task ':hellojni:compileDebugNdk'. > com.android.ide.common.internal.LoggedErrorException: Failed to run command: D:\ndk\ndk-build.cmd NDK_PROJECT_PATH=null APP_BUILD_SCRIPT=F:\androidstudio\test\hellojni\build\ndk\debug\Android.mk APP_PLATFORM=android-19 NDK_OUT=F:\androidstudio\test\hellojni\build\ndk\debug\obj NDK_LIBS_OUT=F:\androidstudio\test\hellojni\build\ndk\debug\lib APP_ABI=armeabi,armeabi-v7a Error Code: 2 Output: make.exe: *** No rule to make target `F:\androidstudio\test\hellojni\build\ndk\debug\obj/local/armeabi/objs/jnimain/F_\androidstudio\test\hellojni\src\main\jni', needed by `F:\androidstudio\test\hellojni\build\ndk\debug\obj/local/armeabi/objs/jnimain/F_\androidstudio\test\hellojni\src\main\jni\hellojni.o'. Stop.
这是NDK在Windows下一个bug,当仅仅编译一个文件时出现,解决方法就是再加入一个空的文件就可以。
This may come from a current NDK bug on Windows, when there is only one source file to compile. You only need to add one empty source to make it work again.
错误:
Could not determine the dependencies of task ':hellojni:compileArmDebugJava'. > failed to find Build Tools revision 19.0.3
解决方式:
这个Build Tools是指“Android SDK Build-tools”,打开SDK Manager勾选对应版本号(比如这里是19.0.3)安装就可以。
错误:
FAILURE: Build failed with an exception. * What went wrong: Task 'assembleArmDebug' not found in project ':hellojni'. Some candidates are: 'assembleDebug'. * Try: Run gradle tasks to get a list of available tasks. Run with --stacktrace option to get the stack trace. Run with --info or --debug option to get more log output.
解决方式:
在android { }中加入:
productFlavors{ arm { } }
productFlavors { x86 { versionCode Integer.parseInt("6" + defaultConfig.versionCode) ndk { abiFilter "x86" } } mips { versionCode Integer.parseInt("4" + defaultConfig.versionCode) ndk { abiFilter "mips" } } armv7 { versionCode Integer.parseInt("2" + defaultConfig.versionCode) ndk { abiFilter "armeabi-v7a" } } arm { versionCode Integer.parseInt("1" + defaultConfig.versionCode) ndk { abiFilter "armeabi" //abiFilters "armeabi", "armeabi-v7a" } } fat }
错误:
Execution failed for task ':hellojni:compileDebugNdk'. > java.io.IOException: Cannot run program "D:\ndk\ndk-build": CreateProcess error=193, %1 ??????Ч?? Win32 ??ó
在使用gradle1.9版本号时遇到,使用gradle1.10版本号来解决。
错误:
A problem occurred evaluating project ':app'. > Could not create plugin of type 'AppPlugin'.
Don’t use latest Gradle (version 1.10), downgrade to 1.9。參考:http://blog.vyvazil.eu/tag/android-studio/
可是假设我们使用gradle1.9版本号的话又会出现错误:
Execution failed for task ':hellojni:compileDebugNdk'. > java.io.IOException: Cannot run program "D:\ndk\ndk-build": CreateProcess error=193, %1 ??????Ч?? Win32 ??ó
不管使用哪个版本号都有问题,后来细致查看了下'AppPlugin'这个错误是出如今‘app’模块上的而非‘hellojni’模块上,于是考虑新建project项目而且仅仅在该project下建立一个库模块,不再创建app模块,如图:
这里不勾选“Create custom launcher icon”和“Create activity”,直接finish完毕,其它配置參考前述,最后编译后能够生成so库文件:
错误:
这个错误忘记记录了囧
解决方式:
File-Settings-Gradle-Gradle VM options:-Xmx512m