[jni] 02_JNI的入门-HelloWorld

Android 4.0

JNI的入门-HelloWorld

一、JNI调用C代码HelloWorld步骤:1、创建一个Android工程
2、JAVA代码中写声明native 方法 public native String helloFromJNI();不要写实现,这是在c代码中实现的
3、用javah工具生成头文件
4、在工程创建jni目录,引入头文件,根据头文件实现c代码
jni.h的头文件:C:\android-ndk-r9b\platforms\android-16\arch-arm\usr\include
Hello.c
#include <stdio.h>
#include <jni.h>
#include "cn_zengfansheng_helloworld_DemoActivity.h"
注意:引用当前编译器头文件用<>,引入当前工作目录的头文件用""
//typedef const struct JNINativeInterface* JNIEnv;
//typedef void*  jobject;
//struct JNINativeInterface { jstring  (*NewStringUTF)(JNIEnv*, const char*); }
//注意,包名是_而不是.分隔
jstring Java_cn_zengfansheng_ndkhelloworld_DemoActivity_helloFromJNI(JNIEnv* env,jobject obj){
    //  JNINativeInterface** env 存放的是JNINativeInterface*的地址
    //return (*(*env)).NewStringUTF(env,"hello world from c");
    return (*env)->NewStringUTF(env,"hello world from c");
}
注意1:C语言中,方法命名Java_包名(将包名的点(.)改为下划线(_))_类名_本地native方法,以及两个参数JNIEnv* env,jobject obj
注意2:如果方法名或者类名包名有下划线,那么该怎么命名?
解决2:在下划线后加上一个1,就是_1
jstring Java_cn_zengfansheng_ndkhelloworld_DemoActivity_hello_1from_1c(JNIEnv* env,jobject obj){
    return (*env)->NewStringUTF(env,"hello world from c____");
}
5、编写Android.mk文件
Android.mk
LOCAL_PATH :$(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE    :=Hello //
LOCAL_SRC_FILES :=Hello.c //要编译的文件的名称
include $(BUILD_SHARED_LIBRARY)
6、NDK编译生成动态库
要cygwin进入工程目录创建的jni目录,执行下面指令
$ ndk-build
7、Java代码load 动态库.调用native代码
static {
    // libHello.so。注意,和LOCAL_MODULE :=Hello一致。不要写lib前缀和.so后缀
    System.loadLibrary("Hello");
}
8、运行Android工程测试
二、javah工具的使用-使用javah生成native方法的头文件*.h
自己写native方法的签名很容易写错,所以用javah工具,自动生成签名
a) 没有包名
1、建立一个没有包名的Hello.java类,然后用javac编译.class文件
class Hello{
    public native String h1();
    private native  String h1_1_h();
}
2、然后cmd进入到该目录,执行javah Hello,会生成头文件Hello.h
> javah Hello  

/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class Hello */
 
#ifndef _Included_Hello
#define _Included_Hello
#ifdef __cplusplus
extern "C" {
#endif
/*
 * Class:     Hello
 * Method:    h1
* Signature: ()Ljava/lang/String;
*/

JNIEXPORT jstring JNICALL Java_Hello_h1
  (JNIEnv *, jobject);
 
/*
 * Class:     Hello
 * Method:    h1_1_h
* Signature: ()Ljava/lang/String;
*/

JNIEXPORT jstring JNICALL Java_Hello_h1_11_1h
  (JNIEnv *, jobject);
 
#ifdef __cplusplus
}
#endif
#endif
b)带包名的
1、用javac编译,然后将_Hello.class放于目录/cn/zengfansheng/jni下
package cn.zengfansheng.jni;
class _Hello{
 public native String h1();
 private native  String h1_1_h();
}
2、然后还是切换到cn的上级目录,javah _Hello ,就可以生成带包名的方法头文件执行下面命令
> javah cn.zengfansheng.jni._Hello
那就会在该目录(cn上级目录)生成
JNIEXPORT jstring JNICALL Java_cn_zengfansheng_jni__1Hello_h1
  (JNIEnv *, jobject);
JNIEXPORT jstring JNICALL Java_cn_zengfansheng_jni__1Hello_h1_11_1h
  (JNIEnv *, jobject);

问题2:使用javah时,生成头文件时,出现的错误
错误: 无法访问android.app.Activity
  找不到android.app.Activity的类文件

解决:这个时候,不要在bin/classes目录下进行,而是切换到src目录下

问题1:如果使用的是intel x86的模拟器,会出现下面问题:
11-28 01:02:05.181: E/AndroidRuntime(1629): Caused by: java.lang.UnsatisfiedLinkError: Couldn't load Hello: findLibrary returned null
解决:今天准备把某个android应用的某项功能(什么应用什么功能,这个不是重点啦:)  )采用c实现。因为不是经常用,所以先写了个简单的测试函数,编译,调试,就出现了异常:java.lang.UnsatisfiedLinkError: Couldn’t load***.so
 
异常提示是没能成功加载到某某库;
第一反应,先看看libs下有没有生成该库?答案:有;
第二反应,代码错误?答案:代码没有错;
第三反应,从网络上搜索其他可能情况:有看到说是so的命名标准的,是要“lib”开始,因项目使用ndk编译,所以本来就是生成lib开头的库名的,找不到原因;
第四反应,找了ndk目录下的sample看,还是没有找到区别;
 
最后,在再次启动调试时,eclipse提示要选择模拟器时,发现了可能原因:原来我平常都是用inter atom cpu的模拟器调试的,而默认编译的so是针对arm平台的。。。
 
重新启动arm cpu的模拟器后就没有报错了。。。因为目前android系统还是比较多运行在arm架构上,所以不对inte架构做尝试了