转载请注明出处:http://blog.csdn.net/jack_chen_00/article/details/37522883
Demo所使用的源码地址:http://download.csdn.net/detail/chenjianjk/7604969
第一次接触JNI,网上相关资料零零散散,经过几天的实践终于算是会用了,总结下今天的经验分享给大家,希望对初学者有所帮助。
环境搭建
早期NDK编译环境的搭建是需要Cygwin + NDK配合才行,最初我也是按照这个方案来执行,环境搭建之繁琐真是让人无语,还好找到了更好的环境搭建方法,只需要用到 NDK 就可以,以下是我的搭建方法:1.下载NDK。
在Google的官方下载最新版的NDK,以下是下载网址http://developer.android.com
www.android-doc.com
www.toolib.net
2.Eclips的安装与配置。
我使用的IDE工具是Eclips,为了方便开发,需要安装CDT/ADT插件,如果你还没有学会怎么使用Eclips,你可以网上搜索以下Eclips的安装、使用方法,这里不做细节的介绍。3.JNI 数据类型。
JNI的目的是实现Java调用本地的C/C++方法,但是Java中的数据类型与C/C++的数据类型存在差异,不能直接使用,所以需要统一定义Java与C/C++的数据类型。JNI数据类型对照表参考网址:http://blog.csdn.net/zjc0888/article/details/6288602
4.Android.mk
在Android工程下的JNI目录中需要有一个Android.mk的文件,用来规定C/C++的编译规则,Android.mk与Linux下的Makefile功能类似,Android.mk是GNU Makefile的一部分,用过Makefile的人都知道,它是管理、编译、维护一个大型的源码工程必不可少的工具。Android.mk是Android定义的一种代码编译方式,它也有自己的一套编写规范,代码编译的规则都存放在Android.mk的文件里。想要使用JNI,就必须了解最基本的Android.mk的编写规范,读者可以参数下面的网址自学如果编写Android.mk文件。
参考网址:http://blog.csdn.net/cuijpus/article/details/5627248
创建Android工程
1. 创建应用程序。
在Eclips创建一个名称为JNI_DEMO的Android应用程序,在布局文件中新增一个按钮方便触发JNI 调用。
public class MainActivity extends Activity implements OnClickListener {
Button mButton;
private String TAG = "TEST";
int i = 1;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mButton = (Button)findViewById(R.id.button1);
mButton.setOnClickListener(this);
}
@Override
public void onClick(View v) {
Log.d(TAG, NativeUtils.getLocomotiveName());
Log.d(TAG, NativeUtils.init(38400,"SD")+"");
Log.d(TAG, NativeUtils.unInit()+"");
Log.d(TAG, NativeUtils.start(0)+"");
Log.d(TAG, NativeUtils.stop(0)+"");
Log.d(TAG, NativeUtils.getStatus(1, 2)+"");
}
}
2.声明本地方法
package com.android.sms.util;
public class NativeUtils {
static {
//读取代码库,需要先读取代码库才能调用库文件中的本地方法
System.loadLibrary("test-jni");
}
public static native String getLocomotiveName();
public static native int init(int baudRate, String storagePath);
public static native int unInit();
public static native int start(int fileId);
public static native int stop(int fileId);
public static native int getStatus(int cmd,int data);
}
3.编写本地代码(C/C++)
在app工程目录下新建一个jni目录,在jni目录中新建testjni.c
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <jni.h>
#include <assert.h>
#include<android/log.h>
#define TAG "myDemo-jni" // 这个是自定义的LOG的标识
#define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG,TAG ,__VA_ARGS__) // 定义LOGD类型
#define LOGI(...) __android_log_print(ANDROID_LOG_INFO,TAG ,__VA_ARGS__) // 定义LOGI类型
#define LOGW(...) __android_log_print(ANDROID_LOG_WARN,TAG ,__VA_ARGS__) // 定义LOGW类型
#define LOGE(...) __android_log_print(ANDROID_LOG_ERROR,TAG ,__VA_ARGS__) // 定义LOGE类型
#define LOGF(...) __android_log_print(ANDROID_LOG_FATAL,TAG ,__VA_ARGS__) // 定义LOGF类型
JNIEXPORT jstring JNICALL native_getLocomotiveName(JNIEnv *env, jclass clazz)
{
LOGD("native_getLocomotiveName.\n");
return (*env)->NewStringUTF(env, "Hello world");
}
JNIEXPORT jint JNICALL native_init(JNIEnv *env, jclass clazz,jint baudRate, jstring storagePath)
{
LOGD("native_init.\n");
return baudRate;
}
JNIEXPORT jint JNICALL native_unInit(JNIEnv *env, jclass clazz)
{
LOGD("native_unInit.\n");
return 2;
}
JNIEXPORT jint JNICALL native_start(JNIEnv *env, jclass clazz, jint fileid)
{
LOGD("native_start.\n");
return 3;
}
JNIEXPORT jint JNICALL native_stop(JNIEnv *env, jclass clazz, jint fileid)
{
LOGD("native_stop.\n");
return 4;
}
JNIEXPORT jint JNICALL native_getStatus(JNIEnv *env, jclass clazz, jint cmd, jint data)
{
LOGD("native_getStatus.\n");
return 5;
}
#define JNIREG_CLASS "com/android/sms/util/NativeUtils"
/**
* Table of methods associated with a single class.
*/
static JNINativeMethod gMethods[] = {
{ "getLocomotiveName", "()Ljava/lang/String;", (void*)native_getLocomotiveName },
{ "init", "(ILjava/lang/String;)I", (void*)native_init },
{ "unInit", "()I", (void*)native_unInit },
{ "start", "(I)I", (void*)native_start },
{ "stop", "(I)I", (void*)native_stop },
{ "getStatus", "(II)I", (void*)native_getStatus },
};
/*
* Register several native methods for one class.
*/
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;
}
/*
* Register native methods for all classes we know about.
*/
static int registerNatives(JNIEnv* env)
{
if (!registerNativeMethods(env, JNIREG_CLASS, gMethods,
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(vm, (void**) &env, JNI_VERSION_1_4) != JNI_OK) {
return -1;
}
assert(env != NULL);
if (!registerNatives(env)) {
return -1;
}
/* success -- return valid version number */
result = JNI_VERSION_1_4;
return result;
}
4.编写Android.mk
在jni目录中新建Android.mk文件。
#
# Copyright (C) 2008 The Android Open Source Project
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
# This makefile supplies the rules for building a library of JNI code for
# use by our example of how to bundle a shared library with an APK.
LOCAL_PATH:= $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE_TAGS := optional
LOCAL_MODULE:= libtest-jni
LOCAL_SRC_FILES:= testjni.c
LOCAL_LDLIBS :=-llog
LOCAL_SHARED_LIBRARIES := \
libutils
LOCAL_STATIC_LIBRARIES :=
LOCAL_C_INCLUDES += \
$(JNI_H_INCLUDE)
LOCAL_PRELINK_MODULE := false
include $(BUILD_SHARED_LIBRARY)
配置Eclips编译环境
1.解压已经下载好NDK压缩包
2.新建一个编译器
新建的编译器用于编译C/C++本地代码,新建编译器方法选中app工程目录 -> 右键选择Properties -> 选中Builders -> 点击New -> 双击Program -> 定义编译器。
3.编译代码
如果NDK编译器构建成功,在编译app的时候会在工程目录下自动生成一个obj的文件,里面有生成的*.so的动态库和一个编译生成的*.o中中间文件。如果配置不成功会有相应的错误提示。