简介

前面写了几篇NDK相关的文章:

这个demo就是对这几篇文章中涉及内容的一个应用。其功能只是对数组进行排序。如果单纯使用Java来做十分简单,只需写个排序方法,或者将数组转化为集合,然后调用sort函数就ok了。

但我们的目的是练习JNI所以需要使劲的折腾一下,其中主要涉及了:

  • Java调用JNI函数
  • JNI调用Java函数
  • JNI中打印Android Log
  • JNI中调用c++函数库
  • JNI调用普通方法/静态方法
  • JNI全局变量使用
  • JNI数组的操作
  • JNI异常处理

演示图

点击按钮对数组排序:

排序成功:

Java代码:

Activity代码:

public class MainActivity extends AppCompatActivity {

    // 导入动态库
static {
System.loadLibrary("test");
} // 本地排序方法
public native boolean doSort(int [] array); private Button mOrderBtn;
private TextView mResultText; // 需要排序的数组
int[] orderArrays = {6,2,5,9,1,3,4,7,8,0,10}; @Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main); // 调用jni调用静态方法返回一个字符串(hello world?)
Log.d("TestNDK", JNIHelper.getStringFromNative()); mResultText = (TextView) this.findViewById(R.id.result);
setResultText(orderArrays); mOrderBtn = (Button) this.findViewById(R.id.plus_btn);
mOrderBtn.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
// 调用本地排序方法, 根据是否有异常打印success/failed
boolean isSuccess = doSort(orderArrays);
mOrderBtn.setText(isSuccess?"success":"failed");
}
}); } public void setResultText(String resultText) {
mResultText.setText(resultText);
} public void setResultText(int[] array) {
mResultText.setText(Arrays.toString(array));
} }

JNI工具类:

public class JNIHelper {

    @Keep
private void updateStatus(String msg) {
if (msg.toLowerCase().contains("error")) {
Log.e("JniHandler", "Native Err: " + msg);
} else {
Log.i("JniHandler", "Native Msg: " + msg);
}
} public static native String getStringFromNative();
}

JNI代码

头文件

#include <jni.h>
/* Header for class com_dean_testndk_JNIHelper */ #ifndef _Included_com_dean_testndk_JNIHelper
#define _Included_com_dean_testndk_JNIHelper #ifdef __cplusplus
extern "C" {
#endif
/*
* Class: com_dean_testndk_JNIHelper
* Method: getStringFromNative
* Signature: ()Ljava/lang/String;
*/
JNIEXPORT jstring JNICALL
Java_com_dean_testndk_JNIHelper_getStringFromNative(JNIEnv *, jclass); JNIEXPORT jboolean JNICALL
Java_com_dean_testndk_MainActivity_doSort(JNIEnv *, jobject, jintArray);
#ifdef __cplusplus
}
#endif
#endif

c++代码

//
// Created by Dean Guo on 7/23/16.
//
#include "com_dean_testndk_JNIHelper.h"
#include "string.h"
#include "iostream"
#include "vector"
#include "algorithm"
#include <android/log.h> using namespace std;
// Android log function wrappers
static const char* kTAG = "testNDK";
#define LOGI(...) \
((void)__android_log_print(ANDROID_LOG_INFO, kTAG, __VA_ARGS__))
#define LOGW(...) \
((void)__android_log_print(ANDROID_LOG_WARN, kTAG, __VA_ARGS__))
#define LOGE(...) \
((void)__android_log_print(ANDROID_LOG_ERROR, kTAG, __VA_ARGS__)) struct GLOBAL_CONTEXT {
JavaVM* javaVM;
jclass mainActivityClz;
jobject mainActivityObj; jclass jniHelperClz;
jobject jniHelperObj;
}; /**
* 全局引用
*/
GLOBAL_CONTEXT mContext; void sendJavaMsg(JNIEnv *env, jobject instance, jmethodID func, const char *msg) { jstring javaMsg = env->NewStringUTF(msg);
env->CallVoidMethod(instance, func, javaMsg);
} void callSetResult(JNIEnv *env, vector<int> vector_) { char buff[1000] = ""; vector<int>::iterator it; for (it = vector_.begin(); it != vector_.end(); it++) {
char numChar[100] = "";
sprintf(numChar, "%d", *it);
strcat(numChar, ",");
strcat(buff, numChar);
} jstring javaMsg = env->NewStringUTF(buff);
jthrowable ex = env->ExceptionOccurred();
if (0 != ex) {
env->ExceptionClear();
env->DeleteLocalRef(javaMsg);
LOGE("Exception-sendJavaMsg!");
} else {
jmethodID methodID = env->GetMethodID(mContext.mainActivityClz, "setResultText", "(Ljava/lang/String;)V");
env->CallVoidMethod(mContext.mainActivityObj, methodID, javaMsg);
env->DeleteLocalRef(javaMsg);
}
} /**
* 根据排序好的vector集合, 创建排序数组返回
*/
void callSetArrayResult(JNIEnv *env, vector<int> vector_) { int size = vector_.size(); // 排序后需要返回的数组
jintArray array = env->NewIntArray(size); // 为jni数组赋值
jint *num = new jint[size];
for (int i = 0; i < size; i++)
{
num[i] = vector_[i];
}
env->SetIntArrayRegion(array, 0, size, num); // 调用java方法传递排序好的数组
jmethodID methodID = env->GetMethodID(mContext.mainActivityClz, "setResultText", "([I)V");
env->CallVoidMethod(mContext.mainActivityObj, methodID, array); // 释放数组
env->ReleaseIntArrayElements(array, num, 0);
} /**
* 系统函数, JNI初始化时调用。
* 可在这个函数这里做一些初始化赋值
*/
JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM* vm, void* reserved) { LOGI("JNI_OnLoad"); JNIEnv* env; mContext.javaVM = vm; if (vm->GetEnv((void**)&env, JNI_VERSION_1_6) != JNI_OK) {
LOGE("JNI_ERR");
return JNI_ERR; // 不支持该版本
} // 对JNIHelper设置全局引用
jclass clz = env->FindClass("com/dean/testndk/JNIHelper");
mContext.jniHelperClz = (jclass) env->NewGlobalRef(clz); jmethodID jniHelperCtor = env->GetMethodID(mContext.jniHelperClz, "<init>", "()V");
jobject handler = env->NewObject(mContext.jniHelperClz, jniHelperCtor);
mContext.jniHelperObj = env->NewGlobalRef(handler); // 调用JNIHelper中updateStatus方法打印信息
jmethodID statusId = env->GetMethodID(mContext.jniHelperClz, "updateStatus", "(Ljava/lang/String;)V");
sendJavaMsg(env, mContext.jniHelperObj, statusId, "JNI: initializing..."); return JNI_VERSION_1_6;
} /**
* 简单返回一个字符串
*/
JNIEXPORT jstring JNICALL Java_com_dean_testndk_JNIHelper_getStringFromNative
(JNIEnv *env, jclass jclz) {
return env->NewStringUTF("from Native");
} /**
* 排序方法, 成功返回true, 否则false
*/
JNIEXPORT jboolean JNICALL
Java_com_dean_testndk_MainActivity_doSort(JNIEnv *env, jobject instance, jintArray arr_) {
// 设置全局引用
mContext.mainActivityClz = env->GetObjectClass(instance);
mContext.mainActivityObj = env->NewGlobalRef(instance); // 获取传递过来的数组指针
jint *nums = env->GetIntArrayElements(arr_, NULL);
// 获取数组长度
jsize len = env->GetArrayLength(arr_); // 使用标准库中的vector排序
vector<int> mVector;
for (int i=0; i< len; i++) {
mVector.push_back(nums[i]);
}
sort(mVector.begin(), mVector.end()); // 调用返回结果函数
callSetArrayResult(env, mVector); // 释放数组
env->ReleaseIntArrayElements(arr_, nums, 0); // 判断是否有异常, 没有表示成功返回true, 否则false
jthrowable ex = env->ExceptionOccurred();
if (0 != ex) {
env->ExceptionClear();
LOGE("Exception!");
return false;
} else {
return true;
}
}

最后

下载

https://github.com/a396901990/TestNDK

总结

demo中所有详细的知识点都可以在NDK开发-零散知识点整理中找到。

如果想深入NDK推荐Google NDK的官方demo:

https://github.com/googlesamples/android-ndk

NDK开发—基础知识实战Demo的更多相关文章

  1. IM开发基础知识补课:正确理解前置HTTP SSO单点登陆接口的原理

    1.前言 一个安全的信息系统,合法身份检查是必须环节.尤其IM这种以“人”为中心的社交体系,身份认证更是必不可少. 一些PC时代小型IM系统中,身份认证可能直接做到长连接中(也就是整个IM系统都是以长 ...

  2. IM开发基础知识补课(四):正确理解HTTP短连接中的Cookie、Session和Token

    本文引用了简书作者“骑小猪看流星”技术文章“Cookie.Session.Token那点事儿”的部分内容,感谢原作者. 1.前言 众所周之,IM是个典型的快速数据流交换系统,当今主流IM系统(尤其移动 ...

  3. IOS开发基础知识碎片-导航

    1:IOS开发基础知识--碎片1 a:NSString与NSInteger的互换 b:Objective-c中集合里面不能存放基础类型,比如int string float等,只能把它们转化成对象才可 ...

  4. iOS开发——总结篇&IOS开发基础知识

    IOS开发基础知识 1:Objective-C语法之动态类型(isKindOfClass, isMemberOfClass,id) 对象在运行时获取其类型的能力称为内省.内省可以有多种方法实现. 判断 ...

  5. Ext常用开发基础知识

    Ext常用开发基础知识 组件定义 //这种方法可以缓存所需要的组件 调用起来比较方便(方法一 ) Ext.define('MySecurity.view.home.HomePanel', { //添加 ...

  6. IM开发基础知识补课(五):通俗易懂,正确理解并用好MQ消息队列

    1.引言 消息是互联网信息的一种表现形式,是人利用计算机进行信息传递的有效载体,比如即时通讯网坛友最熟悉的即时通讯消息就是其具体的表现形式之一. 消息从发送者到接收者的典型传递方式有两种: 1)一种我 ...

  7. [No0000138]软件开发基础知识

    1. 本文目的 本文目的在于,介绍软件开发的各种基础知识 以实现,看了之后,对于软件开发的很多领域的基础知识有所了解 如此在进行后续的真正的软件开发时,遇到各种细节知识,才会明白由来和背景知识 第 1 ...

  8. IM开发基础知识补课(七):主流移动端账号登录方式的原理及设计思路

    1.引言 在即时通讯网经常能看到各种高大上的高并发.分布式.高性能架构设计方面的文章,平时大家参加的众多开发者大会,主题也都是各种高大上的话题——什么5G啦.AI人工智能啦.什么阿里双11分分钟多少万 ...

  9. 3D开发基础知识和简单示例

    引言 现在物联网概念这么火,如果监控的信息能够实时在手机的客服端中以3D形式展示给我们,那种体验大家可以发挥自己的想象. 那生活中我们还有很多地方用到这些,如上图所示的Kinect 在医疗上的应用,当 ...

随机推荐

  1. SQL Server 存储过程

    Transact-SQL中的存储过程,非常类似于Java语言中的方法,它可以重复调用.当存储过程执行一次后,可以将语句缓存中,这样下次执行的时候直接使用缓存中的语句.这样就可以提高存储过程的性能. Ø ...

  2. unity3d的GUI元素的界面坐标系统总结(有公式)

    GUIText 和GUITexture 1.GUIText 锚点(Anchor)的概念我就不介绍了.像NGUI和tookit2d还有 Cocos2d中都有这个重要的概念,对于图片我们可以认为是图片自身 ...

  3. centos无法正常启动,报chown: invalid user:'root:root'

    现象: 系统无法正常启动,启动界面卡在上图的位置 注意错误信息:chown: invalid user:'root:root' 原因:/etc/passwd文件损坏或者被清空 尝试:1.grub菜单项 ...

  4. jQuery触发a标签点击事件-为什么不跳转

    今天开发发现 使用jQuery触发a标签的点击事件,当前的样式发生了变化,可是没有跳转,为什么? 百度后找到的解决方案: <a onclick="hanle()" href= ...

  5. HTML基础篇之图像热区补充一下图片相对地址的定义

    HTML5课堂笔记理解2 上次说到图片相对地址的定义,举例了4中情形的下的不同目录的图片书写方法补充一个如果你要的图片目录跟上面四种都不一样话可以用以下属性值尝试 ./            当前目录 ...

  6. 实战Java虚拟机之四:提升性能,禁用System.gc() ?

    今天开始实战Java虚拟机之四:"禁用System.gc()". 总计有5个系列 实战Java虚拟机之一“堆溢出处理” 实战Java虚拟机之二“虚拟机的工作模式” 实战Java虚拟 ...

  7. 前端开发与SEO

    前端开发中通过一些小习惯,可以有利于SEO,本着蚊子再小也是肉的原则,能抓住自然不能忽略. 1.控制首页链接数量,不能过多,也不要太少.更不要为了凑数而添加 2.扁平化层次,力争跳转三次可以到任何页面 ...

  8. [kuangbin带你飞]专题十 匹配问题 二分匹配部分

    刚回到家 开了二分匹配专题 手握xyl模板 奋力写写写 终于写完了一群模板题 A hdu1045 对这个图进行 行列的重写 给每个位置赋予新的行列 使不能相互打到的位置 拥有不同的行与列 然后左行右列 ...

  9. 关于iPhone设备不同显示尺寸适配的一些方法

    关于iPhone设备不同显示尺寸适配的一些方法   ------关于适配的理解------ 1.什么是适配? 适配是对不同硬件和系统软件的适应,硬件包括屏幕显示,处理器,内存等等(目前主要是屏幕适配, ...

  10. 百度编辑器ueditor 的 submit 表单提交

    页面中表单提交代码: <input type="submit" name="Submit" value="修改保存"> 提交的结 ...