Android JNI入门

前言

先啰嗦一段,从学习Android以来一直会看到这个JNI,偶尔也看到要写c/c++的代码,其实从心里就是有些排斥的,毕竟我学的是Java,我学习一个JNI我还得学会c++,c其实是学了一遍了,但是长期不用基本也就忘了,虽然基本的都是看得懂的,但是编码并不是看得懂就行的,要自己能写,所以其实打心底是排斥JNI的.但是学习Android的时间越长,我发现JNI是支撑Android运行的一大模块,比如我们常见的那些播放音频的功能其实底层就是使用了JNI,这样子就让Java代码可以调用底层的c代码,从而可以操纵硬件的目的,所以其实JNI我们可以理解为Java和(c/c++)之间的桥梁

举例一段源码使用JNI的例子

MediaPlayer m = new MediaPlayer();
		m.start();
/**
* Starts or resumes playback. If playback had previously been paused,
* playback will continue from where it was paused. If playback had
* been stopped, or never started before, playback will start at the
* beginning.
*
* @throws IllegalStateException if it is called in an invalid state
*/
public  void start() throws IllegalStateException {
    stayAwake(true);
    _start();
}

private native void _start() throws IllegalStateException;

这是播放音频控件的开始播放方法的有关源码,我们可以看到,在start()方法中,调用了一个_start()的方法,这个方法是用native关键字标识的,这就是我们以前经常听到的本地方法,意思就是说这个方法的具体实现是通过c/c++代码实现的,这就是JNI的简单案例

ndk环境的搭建,这里亲们就去谷歌官网去下载就行了,下载完毕解压,然后在eclipse里面关联一下即可


下面让我带大家入门

我带大家做一个简单的加法来学习JNI,首先我们得声明一个方法

private native int add(int i,int j);

方法类似于上面的_start()方法是没有方法体的,类似于接口中的方法声明对不对,没有方法体,然后我们在activity的oncreate()方法中调用这个方法来实现

@Override
protected void onCreate(Bundle savedInstanceState) {
	super.onCreate(savedInstanceState);
	setContentView(R.layout.activity_main);
	int result = add(3,5);
	System.out.println("result == " + result);
}

调用的时候就和普通方法一样调用就可以了,这样子就可以了?

很明显这样子不够,因为方法的实现我们还没做呢!我们说实现是需要c/c++来实现的,这里设计到书写c/c++代码了.

编写c/c++代码步骤

1.在项目根目录建立一个文件夹jni,里面用来书写c/c++代码


2.在jni文件夹下新建一个c/c++源码文件


3.里面自然需要实现我们之前定义个那个方法add了

int add(int i, int j){
	return i + j;
}
c中写一个add方法就是上面这样子书写的,但是使用JNI的时候需要加上JNI的一些规范
首先就是方法名字,规范如下:

Java_包名_类名_方法名

所以代码就需要改成这样子

int Java_com_example_day01_MainActivity_add(int i, int j){
	return i + j;
}
都是用下划线隔开的,请注意,然后就是参数列表,这里有两个参数是固定的,必须要写,如下:

int Java_com_example_day01_MainActivity_add(JNIEnv* e, jobject thiz, int i,
		int j) {
	return i + j;
}

可以看到这里多了两个参数,一个是JNIEnv*和jobject

JNIEnv*:这里做一下解释,我们的java代码是运行在虚拟机里面的,所以利用JNI在实现功能的同时,需要用到虚拟机环境的指针,也就是需要环境的支持,并且这是一个指针,指向运行环境

jobject是调用方法的时候的对象,这里也就是我们的MainActivity对象

做完了这一切我们就实现了add方法,当然了别忘了添加一些必要的头文件,完整的c代码是

#include <stdio.h>
#include <stdlib.h>
#include <jni.h>

jint Java_com_example_day01_MainActivity_add(JNIEnv* e, jobject thiz, int i,
		int j) {
	return i + j;
}
这里有一个jni.h头文件是使用jni功能所必需的,还有这里的返回值jint,其实就是谷歌为了让代码更有可读性预定义的一些名字,这里我点进去


可以看到就是一个int,这里也做一个解释,为啥要这样子,直接适应int不是挺好的.

其实这里你用int和jint都是没有什么区别的,但是我们想一下,在c中是没有对象的概念的,加入你java里面传入的是一个数组,或者传入一个对象,在c中都是一个指针而已,指向了你传入的对象,在c中都是void*表示,这时候你写的代码过几天去看,你还知道当初的void*这里的参数,到底是数组呢还是对象,还是字符串呢?你不知道了,你只能去调用的地方去瞧瞧,很明显这里降低了代码的可阅读性,所以谷歌工程师为我们预定义了很多的关键字来表示不同的对象

typedef void*           jobject;
typedef jobject         jclass;
typedef jobject         jstring;
typedef jobject         jarray;
typedef jarray          jobjectArray;
typedef jarray          jbooleanArray;
typedef jarray          jbyteArray;
typedef jarray          jcharArray;
typedef jarray          jshortArray;
typedef jarray          jintArray;
typedef jarray          jlongArray;
typedef jarray          jfloatArray;
typedef jarray          jdoubleArray;
typedef jobject         jthrowable;
typedef jobject         jweak;

可以看到这些的本质的是void*,但是我们可以用jobject和jarray这些来说明参数的含义,这样子就可阅读性很强

最后我们写的c/c++代码最后都会被打包成.so文件,所以我们使用的时候需要在Avtivity里面先加载类库

public class MainActivity extends Activity {

	static {
		System.loadLibrary("hello");
	}

	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_main);
		int result = add(3,5);
		System.out.println("result == " + result);
	}

	private native int add(int i,int j);
}

我们的.so文件要打包成功还需要有一个android.mk,直接从ndk自带的例子里面复制过来就行了

LOCAL_PATH := $(call my-dir)

include $(CLEAR_VARS)

LOCAL_MODULE    := hello
LOCAL_SRC_FILES := hello.c

include $(BUILD_SHARED_LIBRARY)
里面就是这几句话,只要修改hello所在的地方就可以了,你可以把上面的两个hello改成其他的任意名字

利用ndk里面的ndk-build.cmd进行打包我们的c代码

当然了用eclipse关联了ndk之后,直接运行项目就可以了,打包的工作交给eclipse


评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值