转载请注明出处: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 := optionalLOCAL_MODULE:= libtest-jniLOCAL_SRC_FILES:= testjni.cLOCAL_LDLIBS :=-llog LOCAL_SHARED_LIBRARIES := \ libutilsLOCAL_STATIC_LIBRARIES :=LOCAL_C_INCLUDES += \ $(JNI_H_INCLUDE)LOCAL_PRELINK_MODULE := falseinclude $(BUILD_SHARED_LIBRARY)
配置Eclips编译环境
1.解压已经下载好NDK压缩包
2.新建一个编译器
新建的编译器用于编译C/C++本地代码,新建编译器方法选中app工程目录 -> 右键选择Properties -> 选中Builders -> 点击New -> 双击Program -> 定义编译器。
3.编译代码
如果NDK编译器构建成功,在编译app的时候会在工程目录下自动生成一个obj的文件,里面有生成的*.so的动态库和一个编译生成的*.o中中间文件。如果配置不成功会有相应的错误提示。