建立一个JNI工程的步骤:
- 一。新建Android程序工程。
- 二。生成.h头文件,编写.c文件。
- 三。编写mk文件,使用Cygwin编译出so文件。
- 四。加载so文件,运行程序。
- {方法一:} 自动生成jni文件
-
(1)新建android工程
(2) 添加NDK路径
(3) 添加本地支持
(4) 给本地库起名
此时工程目录下会自动生成jni文件夹
此时Makefile也自动生成
(5) 编写准备生成本地库的cpp文件:hello.cppLOCAL_PATH := $(call my-dir) include $(CLEAR_VARS) LOCAL_MODULE := hello LOCAL_SRC_FILES := hello.cpp include $(BUILD_SHARED_LIBRARY)
注意#include <jni.h> #include <string.h> extern "C" { JNIEXPORT jstring JNICALL Java_com_snail_helloworld_MainActivity_myhello(JNIEnv * env, jobject obj); }; JNIEXPORT jstring JNICALL Java_com_snail_helloworld_MainActivity_myhello(JNIEnv * env, jobject obj) { return env->NewStringUTF("Hello From CPP"); }
Java_com_snail_hello_MainActivity_myhello
Java 为固定的
com_snail_helloworld 为包名
MainActivity 为Activity名称
myhello 为上层调用的函数名
这几个命名一定要对应,否则上层调用会出现错误:
“java.lang.UnsatisfiedLinkError: Native method not found”
(1)JNIEnv * env:这个env可以看做是Jni接口本身的一个对象,在上一篇中提到的jni.h头文件中存在着大量被封装好的函数,这些函数也是Jni编程中经常被使用到的,要想调用这些函数就需要使用JNIEnv这个对象。例如:env->GetObjectClass()。(详情请查看jni.h)(2)jobject obj:如在Test类的main方法中有这样一段代码:
Test t=new Test(); t.firstTest();
这个jobject需要两种情况分析。上段代码中firstTest方法是一个非静态方法,在Java中要想调用它必须先实例化对象,然后再用对象调用它,那这个时候jobject就可以看做Java类的一个实例化对象,也就是obj就是t。如果firstTest是一个静态方法,那么在Java中,它不是属于一个对象的,而是属于一个类的,Java中用Test.firstTest()这样的方式来调用,这个时候jobject就可以看做是java类的本身,也就是obj就是Test.class。
(6)编译本地cpp生成库文件
在cmd下进入工程目录:
执行 ndk-build 进行编译(前提是NDK路径已经加入到Path环境变量中)
(6)在Activity中调用本地函数库
运行,OK了.package com.snail.helloworld; import android.app.Activity; import android.os.Bundle; import android.view.Menu; import android.widget.TextView; public class MainActivity extends Activity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); TextView myTextField = (TextView)findViewById(R.id.myText); myTextField.setText(myhello()); } public native String myhello(); static { System.loadLibrary("hello"); } @Override public boolean onCreateOptionsMenu(Menu menu) { // Inflate the menu; this adds items to the action bar if it is present. getMenuInflater().inflate(R.menu.main, menu); return true; } }
{方法二:} 手动生成jni文件
(1) 新建Android工程.
(2) 生成.h头文件
编译工程之后,进入工程目录文件夹下,在控制台运行:
javah -classpath bin/classes -d jni com.example.hellojni.MainAcitivity ,然后就会在工程目录下生成一个jni文件夹,里面就会有.h的头文件了。网上很多都这么说,但实际上这里会遇到一个问题:
1. 有人提出方法,在工程目录下运行命令:javah -classpath src -d jni com.example.hellojni.MainAcitivity
就OK了。是的,这个是OK的。
2.还有一种方法:在工程目录下,上述命令中加上android.jar的classpath。也是可行的。也就是运行如下命令:javah -classpath F:\JNI\env\platforms\android-17\android.jar;bin/classes -d jni com.example.hellojni.MainActivity
这个命令,就是把android.jar的路径加了进去。这样,就能找到Activity类了。
头文件生成好了,刷新一下工程,就会看到如下的目录结构:
(3) 接下来编写.c的实现文件。
在jni文件夹右击,选择file,命名文件名字为HelloJni.c,确定。
然后打开新建的HelloJni.c文件,输入如下语句并保存:
这里要说明 一下,在方法一中是CPP文件,方法二中是.C文件.它们在使用JNIEnv时是有区别的,即#include <stdio.h> #include "HelloJni.h" jstring Java_com_example_hellojni_MainActivity_getHelloJniString (JNIEnv * env, jobject clazz) { return (*env)->NewStringUTF(env, "Hello Jni!"); }
- (*env)->NewStringUTF(env, "Hello from JNI!"); //.c
- env->NewStringUTF("Hello from JNI!"); //.cpp
-
原因:参见jni.h中对于JNIEnv的定义:
#if defined(__cplusplus) typedef _JNIEnv JNIEnv; #else typedef const struct JNINativeInterface* JNIEnv; #endif
- (4)编写mk文件,使用Cygwin编译出so文件。
下面开始编写mk文件。在jni目录下创建一个Android.mk的文件.加入如下代码并保存.
LOCAL_PATH := $(call my-dir) include $(CLEAR_VARS) LOCAL_MODULE := HelloJni LOCAL_SRC_FILES := HelloJni.c include $(BUILD_SHARED_LIBRARY)
这里,主要注意两个地方:
LOCAL_MODULE := HelloJni 。这里的HelloJni就是你的库的名字,也就是你loadLibrary的字符串。生成出来的库是libHelloJni.so。
LOCAL_SRC_FILES := HelloJni.c 。这里的HelloJni.c就是c文件的名字。下面打开Cygwin,进入到工程目录下,输入如下命令,并回车:
$NDK/ndk-build
然后我们刷新工程,会发现,多了一个obj文件夹,和libs文件夹里面多了armeabi文件夹和里面的libHelloJni.so库。obj文件夹是没有用的,可以删掉了。
(5)加载so文件,运行程序
同方法一.
-
-
遇到的问题:
1. Eclipse 中没有NDK 标签
解决:要加入一个jar包到 Eclipse文件夹下的 “plugins”下面
http://jingyan.baidu.com/article/4d58d5413000a09dd4e9c0fe.html
2. 在xx.cpp中加入 jni方面的代码 就报错,到处是红线
- Type 'JNIEnv' could not be resolved
- Type 'JNICALL' could not be resolved
解决:是由于没有将jni.h 导入的缘故,而这个文件在ndk的目录下面。所以,参照以下步骤:
Project Properties -> C/C++ General -> Path and Symbols
选择include标签,Add -> $Android_NDK_HOME/platforms/android-14/arch-arm/usr/include
且选中All languages.最后Apply -> OK
这样错误就解决了。
-
3. uint16 uint8 Type 'uint16' could not be resolved
解决: http://bbs.csdn.net/topics/330246620