Android_ndk_jni_hello-jni_hacking
/***************************************************************************
* Android_ndk_jni_hello-jni_hacking
* 声明:
* 1. 本文用的是android-2.2_froyo的源代码;
* 2. 本文仅仅是对Android自带的ndk中的samples中的jni_hello示例的解读;
* 3. 本文更多的是想通过这个自带的简单示例来了解jni的框架结构,所以没
* 有提供除此之外的更多的内容解读. :)
*
* 2015-4-19 周日 晴 深圳 南山 西丽平山村 曾剑锋
**************************************************************************/ \\\\\\\\\\\\\\\\\\\\\\\\* 目录 */////////////////////////
| 一. 参考文章: |
| 二. 分析源码来源及文件结构: |
| 三. 解析流程: |
| 四. 解析src/com/example/hellojni/HelloJni.java文件: |
| 五. 解析jni/Android.mk文件: |
| 六. 解析jni/hello-jni.c文件: |
\\\\\\\\\\\\\\\\\\\\\\\\\\\\///////////////////////////// 一. 参考文章:
主要参考ndk自带的文档(ndk根目录下的docs文件夹):
.
|-- ANDROID-MK.TXT
|-- APPLICATION-MK.TXT
|-- CHANGES.TXT
|-- CPU-ARCH-ABIS.TXT
|-- CPU-ARM-NEON.TXT
|-- CPU-FEATURES.TXT
|-- DEVELOPMENT.TXT
|-- HOWTO.TXT
|-- INSTALL.TXT
|-- LICENSES.TXT
|-- NDK-BUILD.TXT
|-- NDK-GDB.TXT
|-- OVERVIEW.TXT
|-- STABLE-APIS.TXT
`-- SYSTEM-ISSUES.TXT 二. 分析源码来源及文件结构:
. 本文是用NDK自带的samples中的hello-jni来分析jni的编写流程;
. NDK提供的hello-jni示例文件结构:
.
|-- AndroidManifest.xml
|-- default.properties
|-- jni
| |-- Android.mk ---> jni makefile
| |-- Android_ndk_jni_hello-jni_hacking.c
| `-- hello-jni.c ---> jni C文件
|-- libs
| `-- armeabi
|-- res
| `-- values
| `-- strings.xml
`-- src
`-- com
`-- example
`-- hellojni
`-- HelloJni.java ---> java源文件
. 从文件结构上分析可知,这是一个Android工程项目; 三. 解析流程:
. 本文采用java程序-->jni接口-->C程序的解析流程,这也比较符合常规需求分析;
. 解析文件流程:
. src/com/example/hellojni/HelloJni.java
. jni/Android.mk
. jni/hello-jni.c 四. 解析src/com/example/hellojni/HelloJni.java文件:
. cat src/com/example/hellojni/HelloJni.java
......
package com.example.hellojni; //javah会利用包名生成jni C函数名 import android.app.Activity;
import android.widget.TextView;
import android.os.Bundle; public class HelloJni extends Activity
{
/** Called when the activity is first created. */
@Override
public void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState); /* Create a TextView and set its content.
* the text is retrieved by calling a native
* function.
*/
TextView tv = new TextView(this);
tv.setText( stringFromJNI() );
setContentView(tv);
} /* A native method that is implemented by the
* 'hello-jni' native library, which is packaged
* with this application.
*/
/**
* 这个本地方法在hello-jni库中已经实现了,
* 并且hello-jni库,已经被打包进这个应用里.
*/
public native String stringFromJNI(); //本地方法声明方式,多加个native /* This is another native method declaration that is *not*
* implemented by 'hello-jni'. This is simply to show that
* you can declare as many native methods in your Java code
* as you want, their implementation is searched in the
* currently loaded native libraries only the first time
* you call them.
*
* Trying to call this function will result in a
* java.lang.UnsatisfiedLinkError exception !
*/
/**
* 这是另一个本地方法,但是没有在hello-jni库中实现,
* 这仅仅是为了告诉你,你可以声明很多本地方法在你的Java
* 代码中,只有当你调用这个函数的时候,才会去查找这个本地
* 是否实现了.
*
* 如果你去调用这个没有实现的本地方法,你将会得到:
* java.lang.UnsatisfiedLinkError异常.
*/
public native String unimplementedStringFromJNI(); /* this is used to load the 'hello-jni' library on application
* startup. The library has already been unpacked into
* /data/data/com.example.HelloJni/lib/libhello-jni.so at
* installation time by the package manager.
*/
/**
* 在应用程序启动的时候加载hello-jni库.这个库在你的安装这个app的
* 时候已经解压放到了/data/data/com.example.HelloJni/lib/libhello-jni.so
*/
static {
System.loadLibrary("hello-jni"); //本地方法所在的库文件名
}
}
. 获取jni头文件的方法:
. 我们一般会觉得,拿着工程中的.class文件就可以直接获取jni头文件: javah HelloJni,
经过测试,结果是不行的.
. 将HelloJni.class要放在com/example/hellojni文件夹下,在android的工程bin目录下
是这么放置的,个人猜测是为了得到包名.
. 在com目录下的同一级目录下执行: javah com.example.hellojni.HelloJni,这样就可以
获得文件: com_example_hellojni_HelloJni.h,也就是我们想要的头文件.
4. 在src目录也是可以直接用javah来获取jni头文件的,尤其是遇到javah 找不到类Android.app.Activity的时候可以一试.
. 五. 解析jni/Android.mk文件:
. cat jni/Android.mk
......
/**
* Android.mk文件,必须在开始就定义LOCAL_PATH变量,指定源文件的目录,
* my-dir这个宏函数,是由系统提供的,会返回当前文件夹的绝对路径
*/
LOCAL_PATH := $(call my-dir) /**
* CLEAR_VARS是由系统提供的变量,会清空绝大多数的的LOCAL_XXX变量,
* 但是LOCAL_PATH是一个例外
*/
include $(CLEAR_VARS) /**
* 定义模块名,生成的共享库,会自动加上lib前缀,和.so后缀
*/
LOCAL_MODULE := hello-jni
/**
* 指定生成模块的所需要的C/C++源文件,不需要列出头文件
*/
LOCAL_SRC_FILES := hello-jni.c /**
* BUILD_SHARED_LIBRARY是由系统提供的变量,这个是用创建出一个动态
* 共享库的方式,还有一种生成静态库的变量是:BUILD_STATIC_LIBRARY
*/
include $(BUILD_SHARED_LIBRARY) 六. 解析jni/hello-jni.c文件:
. cat jni/hello-jni.c
......
#include <string.h>
#include <jni.h> /* This is a trivial JNI example where we use a native method
* to return a new VM String. See the corresponding Java source
* file located at:
*
* apps/samples/hello-jni/project/src/com/example/HelloJni/HelloJni.java
*/
/**
* 在这个简单的JNI示例中,我们是用了一个本地方法返回一个新的VM字符串
*/
jstring
Java_com_example_hellojni_HelloJni_stringFromJNI( JNIEnv* env,
jobject thiz )
{
return (*env)->NewStringUTF(env, "Hello from JNI !");
}
. JNI命名规则:
. 前缀: Java
. 类的包名: 上例是---com.example.hellojni
. 类名: 上例是---HelloJni
. 方法名: 上例是---stringFromJNI
. 第一个参数: JNIEnv* env
. 第二个参数: jobject thiz
. 实际的Java参数: 上例没有
. 返回值的参数: 上例没有
. 如果第一次写jni函数,尤其是当我们很多时候工作中是复制别人的函数内容的时候,
很可能会忘记了给自动生成的jni头文件函数声明的参数起名字,您可能回到如下
错误(这是本人生成另一个共享库犯的低级错误 :) ),错误原因也指出来了:
APLEX@APLEX-PC /cygdrive/e/android_app/myserial
$ $NDK/ndk-build
Android NDK: WARNING: APP_PLATFORM android- is larger than android:minSdkVersion in ./AndroidManifest.xml
[armeabi] Compile thumb : serialPort <= SerialPort.c
jni/SerialPort.c: In function 'Java_com_android_aplex_SerialPort_open':
jni/SerialPort.c::: error: parameter name omitted ---> 省略了参数名称
(JNIEnv *, jclass, jstring, jint, jint)
^
jni/SerialPort.c::: error: parameter name omitted
jni/SerialPort.c::: error: parameter name omitted
jni/SerialPort.c::: error: parameter name omitted
jni/SerialPort.c::: error: parameter name omitted
jni/SerialPort.c: In function 'Java_com_android_aplex_SerialPort_close':
jni/SerialPort.c::: error: parameter name omitted ---> 省略了参数名称
(JNIEnv *, jobject)
^
jni/SerialPort.c::: error: parameter name omitted ---> 省略了参数名称
/cygdrive/d/ndk/android-ndk-r10d/build/core/build-binary.mk:: recipe for target 'obj/local/armeabi/objs/serialPort/SerialPort.o' failed
make: *** [obj/local/armeabi/objs/serialPort/SerialPort.o] Error
随机推荐
- 获取本机ip的shell脚本
ifconfig br0 | grep 'inet addr' | sed 's/^.*addr://g' |sed 's/ Bcast:.*$//g' 注意:br0为网卡名称,需要改成你使用的网卡名 ...
- Qt_2D_画图教程
1. ZC: 看点:相同的API,QPainter.QPainterDevice和QPainterEngine这3个类 Qt学习之2D绘图(画刷和画笔) http://blog.csdn.net/lp ...
- 《剑指offer》第三十九题(数组中出现次数超过一半的数字)
// 面试题39:数组中出现次数超过一半的数字 // 题目:数组中有一个数字出现的次数超过数组长度的一半,请找出这个数字.例 // 如输入一个长度为9的数组{1, 2, 3, 2, 2, 2, 5, ...
- MySQL修改时间函数 1.addDate(date , INTERVAL expr unit) 2.date_format(date,’%Y-%m-%d’) 3.str_to_date(date,’%Y-%m-%d’) 4.DATE_SUB(NOW(), INTERVAL 48 HOUR)
MySQL修改时间函数: 1. addDate(date,INTERVAL expr unit) interval 代表时间间隔 : SELECT NOW(); 2018-06 ...
- URAL 1741 Communication Fiend
URAL 1741 思路: dp 状态:dp[i][1]表示到第i个版本为正版的最少流量花费 dp[i][0]表示到第i个版本为盗版的最少流量花费 初始状态:dp[1][0]=dp[0][0]=0 目 ...
- mapStateToProps,mapDispatchToProps的使用姿势
本文作者:IMWeb 黄qiong 原文出处:IMWeb社区 未经同意,禁止转载 前言 刚接触redux的时候,发现大家对mapDispatchToProps使用有几种方法,而且都跑通了,本文来介绍下 ...
- Python 爬虫-Requests库入门
2017-07-25 10:38:30 response = requests.get(url, params=None, **kwargs) url : 拟获取页面的url链接∙ params : ...
- 二分检索函数lower_bound()和upper_bound()
二分检索函数lower_bound()和upper_bound() 一.说明 头文件:<algorithm> 二分检索函数lower_bound()和upper_bound() lower ...
- scala 与 java 之间的关系
scala来源于java,但又高于java. scala的设计者Martin Odersky就是一个JAVA控,这位牛人设计了javac和编写了jdk中的通用代码.可以说java语言本身就是Marti ...
- 基因家族收缩和扩张分析 & Selective loss pathway & 泛基因组
套路 这通常就是基因组组装后的必做分析,通过比较基因组学的手段进行分析,可以知道所研究物种在进化过程中哪些核心基因家族发生了变化,从而导致了其特殊的适应性机制的形成. 参考: Extremotoler ...