JNI NDK开发 例子

 

1、什么是JNI

     JNI全称 Java Native Interface , java本地化接口 。简单的说,他就是JAVA语言与C/C++语言的桥梁,JAVA函数的API可以通过JNI映射到Native层C/C++的API接口,反向调用亦可。他们的调用只是一个简单映射关系,仍然在一个进程中。

2、NDK

     NDK(Native Development Kit): 开发JNI必备工具,就是模拟其他平台特性类编译代码的工具. 交叉编译工具。使用NDK可以在window平台下直接去开发和编译Native层代码,方便调试。(NDK工具需下载,并配置环境)

3、动态方式实现JNI

      可以仿照原生JNI书写方式,写一个函数API对应表,这样就可以不依赖于eclipse去生成.h头文件。编译jni也可以直接用ndk-build -C 指令编译成.so库。

   JNI_OnLoad是java jni技术的一个实现,每次java层加载 System.loadLibrary 之后,自动会查找改库一个叫 JNI_OnLoad 的函数,动态注册的时候,cpp可以通过实现 JNI_OnLoad 函数完成jni的动态注册。

    Jni中本地类型和java类型对应表,参考 chuekup blog  https://blog.csdn.net/chuekup/article/details/8030038 

 

   直接举个例子吧,这个例子包含了(代码较为简单,有注释)

  • JAVA -> JNI函数调用;
  • JNI -> JAVA 方向的回调;

3.1 在JAVA 文件中创建需要调用到jni的函数

    JAVA层 定义了三个函数,如下:

   public native void startTest();

    public native void stopTest();

    public native void testfunc1();

    MainActivity.java

package com.example.jnitest;
import android.app.Activity;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.Button;

public class MainActivity extends Activity {
    
    private Button mBtnTest1;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        
        System.loadLibrary("jintest");
        
        initView();
        startTest();
    }
    
    @Override
    protected void onDestroy() {
        // TODO Auto-generated method stub
        super.onDestroy();
        stopTest();
    }
    
    private void initView() {
        mBtnTest1 = (Button) findViewById(R.id.btn_test1);
        mBtnTest1.setOnClickListener(listener);
    }
    
    public void onNotifyData(byte[] data) {
        Log.d("Main", "onNotifyData length " + data.length);
    }
    
    private View.OnClickListener listener = new View.OnClickListener() {
        
        @Override
        public void onClick(View arg0) {
            // TODO Auto-generated method stub
            switch (arg0.getId()) {
            case R.id.btn_test1:
                Log.d("Main", "testfunc1 onClick");
                testfunc1();
                break;

            default:
                break;
            }
        }
    };
    
    public native void startTest();
    public native void stopTest();
    public native void testfunc1();
}

3.2、创建C++类型文件,实现 JNI_OnLoad函数;

TestJniNative.h TestJniNative.cpp

#include <jni.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <pthread.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <assert.h>
#include <android/log.h>

#include "TestJniNative.h"

//全局变量
JNIEnv* g_env = NULL;
JavaVM *g_jvm = NULL;
jobject g_obj = NULL;

jclass cls;
jmethodID method_id;

static BYTE tmp[2];

// 和JAVA中的包名路径一样
static const char* const kClassPathName = "com/example/jnitest/MainActivity";

void TestJniNative_onNotifyData(BYTE *data, UINT32 length) {

	jbyte *by = (jbyte*)data;
	jbyteArray jarray = g_env->NewByteArray(length);
	g_env->SetByteArrayRegion(jarray, 0, length, by);
	g_env->CallVoidMethod(g_obj, method_id, jarray);
	g_env->DeleteLocalRef(jarray);

}

// 对全局变量进行赋值,并查找java 类的中的方法
JNIEXPORT void TestJniNative_startTest(JNIEnv* env, jobject obj) {
	if(g_obj) {
		g_env->DeleteGlobalRef(g_obj);
	}
	g_obj = env->NewGlobalRef(obj);
	g_env= env;

	cls = g_env->GetObjectClass(g_obj);
	method_id = g_env->GetMethodID(cls, "onNotifyData", "([B)V");
}

JNIEXPORT void TestJniNative_stopTest(JNIEnv* env, jobject obj) {
	if(g_obj) {
		g_env->DeleteGlobalRef(g_obj);
		g_obj = NULL;
	}
}

JNIEXPORT void TestJniNative_testfunc1(JNIEnv* env, jobject obj) {
	LOGI("TestJniNative_testfunc1");

	memset(tmp, 0x00, 2);
	TestJniNative_onNotifyData(&tmp[0], 2);
}

// 结构体,分别是java层的函数名称,签名,对应的函数指针
static JNINativeMethod gMethods[] = {
	{ "testfunc1", "()V", (void *) TestJniNative_testfunc1 },
	{ "startTest", "()V", (void *) TestJniNative_startTest },
	{ "stopTest", "()V", (void *) TestJniNative_stopTest },
};

static int registerNativeMethods(JNIEnv* env, const char* className,
		JNINativeMethod* gMethods, int numMethods) {
	jclass clazz;
	g_env = env;
	clazz = env->FindClass(className);
	if (clazz == NULL)
		return JNI_FALSE;
	if (env->RegisterNatives(clazz, gMethods, numMethods) < 0) {
		return JNI_FALSE;
	}
	return JNI_TRUE;
}

static int registerDriveRecordNatives(JNIEnv *env) {
	return registerNativeMethods(env, kClassPathName, gMethods,
			sizeof(gMethods) / sizeof(gMethods[0]));
}

//System.loadLibrary 后,首先会调用 JNI_OnLoad函数
JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM *vm, void *reserved) {
	JNIEnv *env = NULL;
	if (vm->GetEnv((void **) &env, JNI_VERSION_1_4) != JNI_OK) {
		return -1;
	}
	assert(env != NULL);
	// 关联JAVA 类中的函数
	if (registerDriveRecordNatives(env) < 0) {
		return -1;
	}
    // 返回版本号
	return JNI_VERSION_1_4;
}

3.3 创建Android.mk文件进行编译

mk文件仿照Android 原生代码进行编写,在此是生成一个动态库(libjintest.so)

LOCAL_PATH := $(call my-dir)

include $(CLEAR_VARS)

LOCAL_MODULE    := jintest

LOCAL_SRC_FILES := \
	TestJniNative.cpp \

LOCAL_C_INCLUDES := $(JNI_H_INCLUDE)

#LOCAL_C_INCLUDES += \
#	DigitalTachoNative.h \
#	DigitalTachoConnect.h 
	
LOCAL_LDLIBS += -llog
LOCAL_SHARED_LIBRARIES := libutils

include $(BUILD_SHARED_LIBRARY)

3.4 编译

       如果对eclipse等工具进行指令配置的话,就可以在编译apk的时候把jni层也编译了,但是每个项目都需要进行指令配置,也很麻烦。也可以直接用 ndk-build -C 目录 指令在 进行编译,生成so库后,再去通过工具生成apk文件。

      采用 ndk-build -C xxx/xxx/jni  指令进行编译,编译OK后会生成.so文件, 生成路径 libs/armeabi/ 下面;

3.5 生成apk

   直接run或者build apk 即可将 .so也打包进 apk文件。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值