最近这段时间,利用空余时间学习下JNI开发相关知识,把学习相关知识的做个笔记。首先我们Android JNI开发,要用到的C语言常见术语有:
库函数:
|- 为了代码重用,在C语言中提供了一些常用的、用于执行一些标准任务(如输入/出)的函数,这些函数事先被编译,并生成目标代码,然后将生成的目标代码打包成一个库文件,以供再次使用。 库文件中的函数被称为库函数,库文件被称为函数库。
|- 在Windows中C语言库函数中的中间代码都是以.obj为后缀的,Linux中是以 .o为后缀。
提示:单个目标代码是无法直接执行的,目标代码在运行之前需要使用连接程序将目标代码和其他库函数连接在一起后生成可执行的文件。 Windows下.dll的文件 , linux下 .so .a的文件.
头文件(.h文件):
|- 头文件中存放的是对某个库中所定义的函数、宏、类型、全局变量等进行声明,它类似于一份仓库清单。若用户程序中需要使用某个库中的函数,则只需要将该库所对应的头文件include到程序中即可。
|- 头文件中定义的是库中所有函数的函数原型。而函数的具体实现则是在库文件中。
|- 简单的说:头文件是给编译器用的,库文件是给连接器用的。
|- 在连接器连接程序时,会依据用户程序中导入的头文件,将对应的库函数导入到程序中。头文件以.h为后缀名。
函数库:
|- 动态库(.so库):在编译用户程序时不会将用户程序内使用的库函数连接到用户程序的目标代码中,只有在运行时,且用户程序执行到相关函数时才会调用该函数库里的相应函数,因此动态函数库所产生的可执行文件比较小。
|- 静态库(.a库):在编译用户程序时会将其内使用的库函数连接到目标代码中,程序运行时不再需要静态库。使用静态库生成可执行文件比较大。
在Linux中:
|- 静态库命名一般为:lib+库名+.a 。
|- 如:libcxy.a 其中lib说明此文件是一个库文件,cxy是库的名称,.a说明是静态的。
|- 动态库命名一般为:lib+库名+.so 。.so说明是动态的。
交叉编译:
|- 将中间代码连接成当前计算机可执行的二进制程序时,连接程序会根据当前计算机的CPU、操作系统的类型来转换。
根据运行的设备的不同,可以将cpu分为:
|- arm结构 :主要在移动手持、嵌入式设备上。
|- x86结构 : 主要在台式机、笔记本上使用。如Intel和AMD的CPU 。
若想在使用了基于x86结构CPU的操作系统中编译出可以在基于arm结构CPU的操作系统上运行的代码,就必须使用交叉编译。
交叉编译:在一个平台下编译出在另一个平台中可以执行的二进制代码。Google提出的NDK就可以完成交叉编译的工作。
使用JNI技术,其实就是在Java程序中,调用C语言的函数库中提供的函数,来完成一些Java语言无法完成的任务。由于Java语言和C语言结构完全不相同,
因此若想让它们二者交互,则需要制定一系列的规范。JNI就是这组规范,此时 Java只和JNI交互,而由JNI去和C语言交互。
JNI技术分为两部分:Java端和C语言端。且以Java端为主导。
|- 首先,Java程序员在Java端定义一些native方法,并将这些方法以C语言头文件的方式提供给C程序员。
|- 然后,C程序员使用C语言,来实现Java程序员提供的头文件中定义的函数。
|- 接着,C程序员将函数打包成一个库文件,并将库文件交给Java程序员。
|- 最后,Java程序员在Java程序中导入库文件,然后调用native方法。
在Java程序执行的时候,若在某个类中调用了native方法,则虚拟机会通过JNI来转调用库文件中的C语言代码。提示:C代码最终是在Linux进程中执行的,而不是在虚拟机中。
锅炉压力监控就是通过C语言获取设备的压力数据,然后我们JAVA通过JNI获取到C中的数据,然后在Android界面以某种试展现出来。
—–主界面代码,定时获取C中的压力数据,在界面 展示—–
public class TestActivity extends Activity {
public native int getPressure();
// 定时器
private Timer timer;
// 定时任务
private TimerTask task;
// 报警时显示提示
private TextView tv;
// 压力值
private int pressure;
// 通过Handler来刷新界面
private Handler handler = new Handler() {
@Override
public void handleMessage(Message msg) {
int pressure = (Integer) msg.obj;
int color = getColor(pressure);
if (color == 404) {
tv.setTextColor(Color.RED);
tv.setTextSize(30);
tv.setText("锅炉快要爆炸了 快跑吧!");// 现实生活中肯定是有报警提示装置的
setContentView(tv);
timer.cancel();
return;
}
super.handleMessage(msg);
}
};
static {
System.loadLibrary("Hello");
}
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
pressure = getPressure() % 300;
PressView myview = new PressView(TestActivity.this, pressure,
getColor(pressure));
tv = new TextView(TestActivity.this);
setContentView(myview);
// 获取锅炉压力 ,根据压力显示不同的内容
timer = new Timer();
task = new TimerTask() {
@Override
public void run() {
// 把压力显示到ui界面上
Message msg = new Message();
msg.obj = pressure;
handler.sendMessage(msg);
}
};
timer.schedule(task, 1000, 2000);
}
/**
* 根据锅炉压力 获取应该显示的颜色
*
* @description:
* @author ldm
* @date 2016-7-1 上午9:14:42
*/
public int getColor(int pressure) {
if (pressure < 100) {
return Color.GREEN;
} else if (pressure < 200) {
return Color.YELLOW;
} else if (pressure < 260) {
return Color.RED;
} else {
return 404;
}
}
}
—–自定义View,不同压力不同显示—–
/**
* 自定义View,根据随机产生的压力数据发生界面上的变化
*
* @description:
* @author ldm
* @date 2016-7-1 上午9:09:54
*/
public class PressView extends View {
private int bottom;
// 画笔
private Paint paint;
public PressView(Context context, int bottom, int color) {
super(context);
this.bottom = bottom;
// 初始化画笔
paint = new Paint();
// 设置画笔颜色
paint.setColor(color);
// 设置绘制线条的宽度
paint.setStrokeWidth(10);
}
// 通过OnDraw()方法绘制界面
@Override
protected void onDraw(Canvas canvas) {
// 绘制矩形条
canvas.drawRect(20, 20, 30, bottom, paint);
super.onDraw(canvas);
}
}
—–C语言简单代码—–
#include<stdio.h>
#include<jni.h>
#include<stdlib.h>
#include"com_ldm_jni_TestActivity.h"
int getpressure(){
// c语言中的随机数
return rand();
}
JNIEXPORT jint JNICALL Java_com_ldm_jni_TestActivity_getPressure
(JNIEnv * env, jobject obj){
return getpressure();//返回产生的随机数字
}
jni开发的具体步骤可以参考:http://blog.csdn.net/true100/article/details/51593476