以前接触过NDK的开发,是在Eclipse环境下开发的。今天尝试了下用Android Studio来配置,结果真是处处都是坑,现在总结一下:
一、步骤
1. 首先创建MainActivity,添加native方法:
package com.jackie.hellondk; import android.support.v7.app.AppCompatActivity; import android.os.Bundle; public class MainActivity extends AppCompatActivity { public native String getStringFromNative(); @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); } }注意,我用到了v7兼容包下的AppCompatActivity,下面编译头文件时,可能会用到这点知识。然后,一定要点击下方的绿色箭头,make project,确保生成了.class文件。
2. 在main目录下创建一个jni目录,用来放头文件,可以用下面的方法:
3. 编译头文件
在Terminal中进入到项目的根目录,对于Android Studio来说,切换到app\src\main就行,按照网上的说法,直接执行下面的命令:
javah -d jni com.jackie.hellondk.MainActivity(-d jni 指定头文件生成在jni目录下,如果没有第二步创建jni文件夹,也会自动创建)
很明显,找不到MainActivity的.class字节码文件,然后按照官网的方法,指定MainActivity.class的路径,在Android Studio中,所有的.class文件都生成在app\build\intermediates\classes\debug下面,所以,执行下面的命令:
javah -classpath ..\..\build\intermediates\classes\debug -d jni com.jackie.hellondk.MainActivity
这是什么原因呢?这是由于我们上面的MainActivity继承了AppCompatActivity,这个类是在Android v7的兼容包下,所以还需要v7的兼容包加入到classpath中:
javah -classpath D:\DevTools\studio_sdk\extras\android\support\v7\appcompat\libs\android-support-v7-appcompat.jar;..\..\build\intermediates\classes\debug -d jni com.jackie.hellondk.MainActivity
同样,也需要将v4的兼容包加入到classpath中:
javah -classpath D:\DevTools\studio_sdk\extras\android\support\v4\android-support-v4.jar;D:\DevTools\studio_sdk\extras\android\support\v7\appcompat\libs\android-support-v7-appcompat.jar;..\..\build\intermediates\classes\debug -d jni com.jackie.hellondk.MainActivity
无语了,真是处处都是坑啊,继续把android.jar添加到classpath中呗!
javah -classpath D:\DevTools\studio_sdk\platforms\android-23\android.jar;D:\DevTools\studio_sdk\extras\android\support\v4\android-support-v4.jar;D:\DevTools\studio_sdk\extras\android\support\v7\appcompat\libs\android-support-v7-appcompat.jar;..\..\build\intermediates\classes\debug -d jni com.jackie.hellondk.MainActivity
终于成功生成了头文件,如下:
注意,我上面之所以需要添加如此多的classpath,是由于我用到AppCompatActivity,一般情况下只需要将android.jar和.class文件的目录(app\build\intermediates\classes\debug)添加到classpath中就行,在实际开发中,如果大家出现找不到类的错误,请自行添加classpath就行,注意,中间用分号隔开。另外,如果觉得每次敲这么多命令太麻烦,也可以将上面的classpath配置到系统的环境变量中,至于怎么弄,相信大家都OK啦!这里不作详细讲述。
4. 实现native方法
在jni目录新建一个hello.c文件,实现头文件中的方法:
#include <stdio.h> #include <stdlib.h> #include "com_jackie_hellondk_MainActivity.h" JNIEXPORT jstring JNICALL Java_com_jackie_hellondk_MainActivity_getStringFromNative (JNIEnv *env, jclass jclass) { return (*env)->NewStringUTF(env, "Hello from JNI"); }5. 编写Android.mk文件
LOCAL_PATH := $(call my-dir) include $(CLEAR_VARS) LOCAL_MODULE := hello LOCAL_SRC_FILES := hello.c include $(BUILD_SHARED_LIBRARY)
6. 编译动态链接库so,注意要切换到jni所在的目录,执行ndk-build之前,还需要配置环境变量。
生成目录如下:
6. 引用
package com.jackie.hellondk; import android.os.Bundle; import android.support.v7.app.AppCompatActivity; import android.widget.TextView; public class MainActivity extends AppCompatActivity { static { System.loadLibrary("hello"); } TextView mTextView; public native String getStringFromNative(); @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); mTextView.setText(getStringFromNative()); } }编译会出现下面的错误:
在gradle.properties添加下面一句:
android.useDeprecatedNdk=true
继续编译,错误如下:
配置如下:
编译后,安装完成后,还是会出现java.lang.UnsatisfiedLinkError couldn't find libhello.so的错误。
ndk {
moduleName "hello"
abiFilters "armeabi", "armeabi-v7a", "x86"
}
最后一次运行,终于看到了久违的界面啊!
走了很多弯路,真是处处都是坑,做个记录,希望对你们有所帮助。最后说一点,上面用ndk-build来编译生成动态链接库libhello.so,然后在Java中通过loadLibrary来加载。在实际开发过程中,完全可以省去ndk-build这一步,开发好jni程序后,直接运行程序,Android Studio会自动帮我们编译动态链接库,亲测成功!
二、打印log信息
相信很多人在刚开始学习Android JNI编程的时候,需要输出Log,于是按照下面的方法修改hello.c文件:
#include <stdio.h> #include <stdlib.h> #include "com_jackie_hellondk_MainActivity.h" #include <android/log.h> #define TAG "Jackie" #define LOGV(...) __android_log_print(ANDROID_LOG_VERBOSE, TAG, __VA_ARGS__) JNIEXPORT jstring JNICALL Java_com_jackie_hellondk_MainActivity_getStringFromNative (JNIEnv *env, jclass jclass) { LOGV("log from native"); /** * c语言 */ return (*env)->NewStringUTF(env, "Hello from JNI"); /** * C++ * return env->NewStringUTF("Hello from JNI"); */ }然后修改Android.mk文件:
LOCAL_PATH := $(call my-dir) include $(CLEAR_VARS) LOCAL_MODULE := hello LOCAL_SRC_FILES := hello.c # for logging LOCAL_LDLIBS += -llog include $(BUILD_SHARED_LIBRARY)
运行时,总是出现undefined reference to '__android_log_print'的错误,有的同学可能很奇怪,Android.mk文件和android/log.h头文件都引入了,怎么还会有这个错呢?
Android Studio的Android.mk是自动生成的,就算修改也是没用了,实际Android Studio的Android.mk是根据gradle文件生成的,那么就需要修改gradle文件。如果不修改gradle,直接使用__android_log_print就会报错。
Error:(36) undefined reference to '__android_log_print'
现在只需要在module的build.gradle中添加下面的代码即可实现输出Log:1
添加箭头的部分,就OK啦!