JNI函数的注册有两种方法,一种是静态方法,需要用javah为每个声明了native函数的java类编译出的class文件生成一个头文件,另一种是动态注册,通过数据结构保存关联关系实现注册,这里主要介绍静态注册。
1. 创建一个Android Activity 项目, 我的项目有三个Class, 主要是用来显是gl surface view的东西。
package com.example.nativeegl;
import android.opengl.GLSurfaceView;
import android.os.Bundle;
import android.app.Activity;
import android.util.Log;
import android.view.Menu;
import android.view.Window;
import android.view.WindowManager;
public class MainActivity extends Activity {
GLSurfaceView mGLView;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
this.requestWindowFeature(Window.FEATURE_NO_TITLE);
getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,
WindowManager.LayoutParams.FLAG_FULLSCREEN); // (NEW)
mGLView = new MyGLSurfaceView(this);
setContentView(mGLView);
}
}
GLSurfaceView在有touch事件时更新颜色。
package com.example.nativeegl;
import android.opengl.GLSurfaceView;
import android.os.Bundle;
import android.app.Activity;
import android.content.Context;
import android.view.Menu;
import android.view.MotionEvent;
import android.view.SurfaceHolder;
public class MyGLSurfaceView extends GLSurfaceView {
GLSurfaceView.Renderer mRenderer;
/**
* TODO Put here a description of what this constructor does.
*
* @param context
*/
public MyGLSurfaceView(Context context) {
super(context);
// TODO Auto-generated constructor stub.
mRenderer = new MyRenderer();
// Set the Renderer for drawing on the GLSurfaceView
setRenderer(mRenderer);
}
@Override
public boolean onTouchEvent(final MotionEvent event) {
// TODO Auto-generated method stub.
queueEvent(new Runnable() {
public void run() {
((MyRenderer)mRenderer).setColor(event.getX() / getWidth(), event.getY() / getHeight(), 1.0f);
}
});
return true;
}
}
Renderer
public class MyRenderer implements GLSurfaceView.Renderer {
private static final String TAG = "bbaiagl";
private float _red = 0.9f;
private float _green = 0.2f;
private float _blue = 0.2f;
//加载库文件
static {
System.loadLibrary("myegl_jni");
}
//声明native方法,返回String
public native String nativeGetHelloString();
@Override
public void onDrawFrame(GL10 gl) {
// onNativeDrawFrame();
//使用native方法
Log.d(TAG, "onDrawFrame "+ nativeGetHelloString());
// define the color we want to be displayed as the "clipping wall"
gl.glClearColor(_red, _green, _blue, 1.0f);
// clear the color buffer to show the ClearColor we called above...
gl.glClear(GL10.GL_COLOR_BUFFER_BIT | GL10.GL_DEPTH_BUFFER_BIT);
}
@Override
public void onSurfaceChanged(GL10 gl, int width, int height) {
//onNativeSurfaceChanged(width,height);
Log.d(TAG, "onSurfaceChanged");
// Sets the current view port to the new size.
gl.glViewport(0, 0, width, height);// OpenGL docs.
// Select the projection matrix
gl.glMatrixMode(GL10.GL_PROJECTION);// OpenGL docs.
// Reset the projection matrix
gl.glLoadIdentity();// OpenGL docs.]
//gl.glOrthof(0, width,0,height,0,10);
// Calculate the aspect ratio of the window
GLU.gluPerspective(gl, 45.0f, (float) width / (float) height, 0.1f, 100.0f);
// Select the modelview matrix
gl.glMatrixMode(GL10.GL_MODELVIEW);// OpenGL docs.
// Reset the modelview matrix
gl.glLoadIdentity();// OpenGL docs.
}
@Override
public void onSurfaceCreated(GL10 gl, EGLConfig config) {
//onNativeSurfaceCreated();
Log.d(TAG, "onSurfaceCreate");
// Set the background color to black ( rgba ).
gl.glClearColor(0.0f, 0.0f, 0.0f, 0.5f); // OpenGL docs.
// Enable Smooth Shading, default not really needed.
gl.glShadeModel(GL10.GL_SMOOTH);// OpenGL docs.
// Depth buffer setup.
gl.glClearDepthf(1.0f);// OpenGL docs.
// Enables depth testing.
gl.glEnable(GL10.GL_DEPTH_TEST);// OpenGL docs.
// The type of depth testing to do.
gl.glDepthFunc(GL10.GL_LEQUAL);// OpenGL docs.
// Really nice perspective calculations.
gl.glHint(GL10.GL_PERSPECTIVE_CORRECTION_HINT, GL10.GL_NICEST);
}
public void setColor(float r, float g, float b) {
_red = r;
_green = g;
_blue = b;
}
}
2. 在项目路径下执行会在项目目录下生成jni文件夹和头文件
javah -classpath bin/classes -d jni com.example.nativeegl.MyRenderer
最后面的参数是 包名.需要native库的类名。3. 根据头文件做cpp文件。
com_example_nativeegl_MyRenderer.h
可以看到自动生成的函数Java_com_example_nativeegl_MyRenderer_nativeGetHelloStrin
Java_ + 包名+类名+接口名:必须按照这个规则来定义JNI接口的函数名#include <jni.h>
/* Header for class com_example_nativeegl_MyRenderer */
#include <string.h>
//因为我们的实现是用cpp,而不是c, 所以要定义__cplusplus宏。
#define __cplusplus
#ifndef _Included_com_example_nativeegl_MyRenderer //这个宏在cpp实现中要删除。 如果不删除的话,当cpp文件包含这个头文件时,因为这个宏在头文件中已经被定义过,所以会导致cpp里面的内容不执行。作用是防止该头文件被重复引用
#define _Included_com_example_nativeegl_MyRenderer
#ifdef __cplusplus
extern "C" {
#endif
/*
* Class: com_example_nativeegl_MyRenderer
* Method: nativeGetHelloString
* Signature: ()Ljava/lang/String;
*/
JNIEXPORT jstring JNICALL Java_com_example_nativeegl_MyRenderer_nativeGetHelloString
(JNIEnv *, jobject);
#ifdef __cplusplus
}
#endif
#endif
在jni文件夹下,
mkdir include
mv a.h include/
cp include/a.h a.cpp
在头文件的基础上修改成cpp
#include "com_example_nativeegl_MyRenderer.h" //添加头文件
extern "C" {
/*
* Class: com_example_nativeegl_MyRenderer
* Method: nativeGetHelloString
* Signature: ()Ljava/lang/String;
*/
JNIEXPORT jstring JNICALL Java_com_example_nativeegl_MyRenderer_nativeGetHelloString
(JNIEnv *env, jobject obj) {
return env->NewStringUTF((char*)" This is calling from JNI suckers!");
}
}
添加Android.mk文件。Android.mk的语法,请查看链接
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_CFLAGS := -Wall //gcc编译的参数。 man gcc 查询
LOCAL_MODULE := myegl_jni
LOCAL_C_INCLUDES :=$(LOCAL_PATH)/include
LOCAL_CPP_EXTENSION := .cpp
LOCAL_SRC_FILES := com_example_nativeegl_MyRenderer.cpp
include $(BUILD_SHARED_LIBRARY)
在项目的根目录执行
ndk-build
编译出动态库。