在Android的开发中,有时候我们需要和底层的C代码进行交互,这时我们就需要使用JNI技术了,可能我们不需要去直接编写C代码,但是我们能理解和编写简单的使用JNI也是对我们的开发有很大帮着,例如,有时候我们需要使用一些对象云存储服务的时候,它给我们带来便捷的同时也带了一些危险,如果我们直接把密钥写在Java层,很容易被人家反编译后知道,然后访问我们的云存储,为了降低这种风险,我们可以使用JNI将密钥放在C层,C层的代码是二进制的,这样就可以起到一定的保护作用。
一、JNI开发环境配置
这里我们首先需要下载NDK开发工具,我所使用的是r9d版本的,下载地址为http://tools.android-studio.org/
下载完成后,直接解压放在我们的电脑中,然后配置环境变量,如果我们使用Android Studio进行开发这一步也可以省略。在Android Studio中,我们可以local.properties文件中配置如下:
ndk.dir=F\:\\Java\\sdk\\ndk-bundle
sdk.dir=F\:\\Java\\sdk
这里需要根据自己的实际情况进行配置,然后我们就可以进行JNI开发了。
二、实现简单的函数
这里我们就简单的使用两个方法,通过C层返回一个字符串和在Java层传入连个参数,C层返回它们的和。
我们先在Java层创建一个类,然后在这个类中定义两个Native方法:
public class FirstJni { private static String libName="firstjni"; static { System.loadLibrary(libName); } public static native String getStringFromNative(); public static native int getIntFromNative(int a,int b); }
2.然后我们Make一下我们的工程,在AS中的快捷键是Ctrll+F9,我们就可以在build目录下得到一个class文件。
3.使用javah指令生成头文件,打开AS中自带的控制台,进入main目录下,运行如下代码:
javah -d jni -classpath F:/Java/sdk/platforms/android-16/android.jar:../../build/intermediates/classes/debug dave.com.jnidevelope.FirstJni
这里值得注意的地方是,classpath后面的内容是sdk目录下android.jar的路径,后面紧跟一个冒号,然后就是刚才文件的class文件的路径。
4.这是,我们就发现在main目录下多了一个jni文件夹,目录结构如下:
5.编写C层的代码实现,具体代码如下:
#include "dave_com_jnidevelope_FirstJni.h"
#include <jni.h>
#include <android/log.h>
JNIEXPORT jstring JNICALL Java_dave_com_jnidevelope_FirstJni_getStringFromNative
(JNIEnv *env,jobject obj){
return (*env)->NewStringUTF(env,"i am come from jni native");
}
JNIEXPORT jint JNICALL Java_dave_com_jnidevelope_FirstJni_getIntFromNative
(JNIEnv *env,jobject obj,jint a,jint b){
return a+b;
}
其中第一个方法就是C层生成一个string的方法,该方法的声明包含在jni.h头文件中,我们可以在NDK开发工具中的目录下找到:
F:\Java\sdk\ndk-bundle\platforms\android-9\arch-arm\usr\include
至此,我们就差最后一步了,我们需要生成so库文件,在eclipse中,我们需要在jni的目录下创建一个Android.mk文件,内容大致如下:
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE := firstjni
LOCAL_LDFLAGS := -Wl,--build-id
LOCAL_LDLIBS := \
-llog \
-lz \
-lm \
LOCAL_SRC_FILES := \
H:\Android\JNIDevelope\app\src\main\jni\main.c \
LOCAL_C_INCLUDES += H:\Android\JNIDevelope\app\src\main\jni
LOCAL_C_INCLUDES += H:\Android\JNIDevelope\app\src\debug\jni
include $(BUILD_SHARED_LIBRARY)
但是在AS中我们不需要这么麻烦,只需要在Module下的gradle文件中配置如下就行了:
在这个文件中,如果我们不希望c代码放在jni目录下,我们还可以配置自定义的路径,在android下配置
sourceSets {
main {
jniLibs.srcDirs 'src/main/jniLibs'
}
}
至此,我们重新Make下工程,我们就可以生成so库文件了,那么它在什么地方了:
H:\Android\JNIDevelope\app\build\intermediates\ndk
最后,我们就可以在我们的代码中使用者两个方法了,使用的时候我们需要加载进来:
static {
System.loadLibrary(libName);
}
//libName就是我们gradle里面配置的moduleName
使用方法如下:
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
TextView text = (TextView) findViewById(R.id.text);
int fromNative = FirstJni.getIntFromNative(3, 5);
String stringFromNative = FirstJni.getStringFromNative();
text.setText(stringFromNative+fromNative);
}
}
以上就是JNI开发中的一些基础知识,文中不足之处,希望大家指正!