Android NDK开发入门学习笔记(图文教程,极其详尽)

以前也简单用过JNI,但是只是简单用一下,好多都不明白。最近在看源码部分,有涉及到JNI调用的,所以这次打算彻底把它搞定。

先普及一下JNI的调用关系:JAVA------------------------>JNI------------------------------->Native.

我们需要从我们的入口代码写起,我们先来一段含有native函数的简单类:


package com.sahadev.regix;
public class Hello {public static String getStringFromNative() {Bean createBean = createBean();System.out.println(createBean.tag);return createBean.tag;}public static native Bean createBean();
}

这段代码就是说从Native中生成了一个Bean对象,然后将这个对象的tag属性返回给调用者。


所以这里有两个知识点可以学到:

 

1.如果生成一个自定义对象

2.如何在native中生成String字符串对象

Ok,既然Hello这个类有了createBean这个native方法,则我们需要使用Jdk提供的javah命令来生成C/C++所需要的头文件,Javah命令的说明如下:

用法:javah [options] <classes>
其中, [options] 包括:-o <file>                输出文件 (只能使用 -d 或 -o 之一)-d <dir>                 输出目录-v  -verbose             启用详细输出-h  --help  -?           输出此消息-version                 输出版本信息-jni                     生成 JNI 样式的标头文件 (默认值)-force                   始终写入输出文件-classpath <path>        从中加载类的路径-bootclasspath <path>    从中加载引导类的路径
<classes> 是使用其全限定名称指定的
(例如, java.lang.Object)。

在我们正常使用的时候只需要简单的几个参数即可,我们以Hello这个类来举例说明:

javah -d E:\Kongfuzi\HelloJNI com.sahadev.regix.Hello

javah最基本的,不需要多说。-d 前面的用法已经说明,用于指定输出目录,我这里的输入目录是:E:\Kongfuzi\HelloJNI,最后是我们要对那个类操作的全路径名称,必须写全包名,最后需要注意的一点是,必须在com包名的上一级目录执行,一般是src目录,否则会出现如下错误:

E:\Kongfuzi\HelloJNI\src\com>javah -d E:\Kongfuzi\HelloJNI com.sahadev.regix.Hello
错误: 找不到 'com.sahadev.regix.Hello' 的类文件。

上面是个错误的例子,我现在位于src的下级目录com,注意不可以。

当这个命令执行完成之后,会在我们指定的目录生成一个以.h结尾的头文件:


打开它:

/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class com_sahadev_regix_Hello */#ifndef _Included_com_sahadev_regix_Hello
#define _Included_com_sahadev_regix_Hello
#ifdef __cplusplus
extern "C" {
#endif
/** Class:     com_sahadev_regix_Hello* Method:    createBean* Signature: ()Lcom/sahadev/regix/Bean;*/
JNIEXPORT jobject JNICALL Java_com_sahadev_regix_Hello_createBean(JNIEnv *, jclass);#ifdef __cplusplus
}
#endif
#endif

这是一段C++代码,上面的注释说,不要去编辑这个文件,这是机器自动生成的。看看它为我们自动生成了什么:


红色箭头指出的便是我们在Java文件中自己定义的。好,接下来我们来实现它:

我们在工程中添加名为jni的文件夹(如果安装了NDK支持的话,可以一键自动生成相关文件,这里就不多介绍了)。

然后在其中添加Android.mk、com_sahadev_regix_Hello.h,再新建一个名叫JNI_C++.cpp的文件:


打开JNI_C++.cpp:

写入如下代码:

//============================================================================
// Name        : JNI_C++.cpp
// Author      : Sahadev
// Version     :
// Copyright   : Your copyright notice
// Description : Hello World in C++, Ansi-style
//============================================================================
#include <jni.h>//引入必须的JNI支持文件
#include "com_sahadev_regix_Main.h"//引入头文件JNIEXPORT jobject JNICALL Java_com_sahadev_regix_Main_createBean(JNIEnv *env,jclass jc) {jclass jcl1 = env->FindClass("com/sahadev/regix/Bean"); //载入Bean类,注意写全包名,jclass 即为JAVA中的Class,代表类jmethodID cMethodID = env->GetMethodID(jcl1, "<init>","(Ljava/lang/String;)V");//获取构造方法的ID,第一个参数是从哪个类获取,第二个参数<init>代表这是构造方法,第三个参数代表传入的参数是String,返回值为Void,这里的写法可以从JNI的文档中找到return env->NewObject(jcl1, cMethodID,env->NewStringUTF("Hello,JNI!This is sahadev!"));//最后通过调用这个方法传入一个jString的字符串
}

我们将头文件引入,然后拷贝头文件中的native方法,添加参数变量,添加方法体。

上面的代码后面有一部分注释,这里补充一下:findClass和Java中的ClassLoader差不多,jclass等于Java中的Class,在这里调用方法的话需要先得到方法的ID,就像第二行使用了GetMethodID来获取,这三个参数分别代表:获取哪个类的方法,构造方法传<init>普通方法写方法名,第三个参数则代表参数个数类型返回值,最后通过NewStringUTF来生成jString,注意:在c++直接写出的字符串它并不是Java中的字符串,必须通过NewStringUTF方法来实现,最后通过调用NewObject方法来调用Bean的构造方法,并传入参数jString来生成一个Bean对象,并返回给调用者。具体资料请参见:JNI官方文档


好,我们的C++文件写好了,接下来就需要对它进行编译了,这里的编译工具不是什么GUN,而是Android官方提供的NDK-BUILD工具,如果是在Eclipse中集成了NDK环境,可以使用clean功能直接对它进行编译,我们这里只介绍手动编译:

首先我们需要编辑Android.mk文件:

LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE    := JNI_C++
LOCAL_SRC_FILES := JNI_C++.cpp
include $(BUILD_SHARED_LIBRARY)

LOCAL_PATH即为调用命令的所在目录,你在哪个目录下使用cmd命令,这里就会返回它的路径地址

LOCAL_MODULE你生成的文件名称是什么,输出之后会自动在名称的前后加上lib和.so

LOCAL_SRC_FILES要对哪个文件进行编译。

http://developer.android.com/intl/zh-cn/ndk/guides/android_mk.html官方文档对上面的命令做了详细解释。


接下来,需要进入通过cmd命令进入工程所在的目录,然后使用命令ndk-build编译即可,如果ndk-build目录没有添加入本地变量,则使用全路径访问这个命令。我当时不知道这个命令如何使用,就进入了ndk-build所在的目录,想看看它的帮助说明,结果出现了这样的提示:

E:\Android_Sdk\android-ndk-r10e>ndk-build
Android NDK: Could not find application project directory !
Android NDK: Please define the NDK_PROJECT_PATH variable to point to it.
E:\Android_Sdk\android-ndk-r10e\build/core/build-local.mk:143: *** Android NDK:
Aborting    .  Stop.

说没有发现应用的工程目录,于是我就去build/core/build-local.mk文件中的143行查看是什么原因:

ifndef NDK_PROJECT_PATHNDK_PROJECT_PATH := $(call find-project-dir,.,jni/Android.mk)
endif
ifndef NDK_PROJECT_PATHNDK_PROJECT_PATH := $(call find-project-dir,.,AndroidManifest.xml)
endif
ifndef NDK_PROJECT_PATH$(call __ndk_info,Could not find application project directory !)$(call __ndk_info,Please define the NDK_PROJECT_PATH variable to point to it.)$(call __ndk_error,Aborting)
endif

根据上下文,应该是没有找到jni/Android.mk或者AndroidManifest.xml之类的文件,再根据上下文发现有这么一段说明:

# ====================================================================
#
# If NDK_PROJECT_PATH is not defined, find the application's project
# path by looking at the manifest file in the current directory or
# any of its parents. If none is found, try again with 'jni/Android.mk'
#
# Note that we first look at the current directory to avoid using
# absolute NDK_PROJECT_PATH values. This reduces the length of all
# source, object and binary paths that are passed to build commands.
#
# It turns out that some people use ndk-build to generate static
# libraries without a full Android project tree.
#
# If NDK_PROJECT_PATH=null, ndk-build make no attempt to look for it, but does
# need the following variables depending on NDK_PROJECT_PATH to be explicitly
# specified (from the default, if any):
#
#   NDK_OUT
#   NDK_LIBS_OUT
#   APP_BUILD_SCRIPT
#   NDK_DEBUG (optional, default to 0)
#   Other APP_* used to be in Application.mk
#
# This behavior may be useful in an integrated build system.
#
# ====================================================================

也就是说如果你没有定义NDK_PROJECT_PATH的话,它就会根据manifest文件去寻找工程路径,这里的NDK_PROJECT_PATH你可以在环境变量中指定:


这样的话,就可以在任何地方直接使用ndk-build命令对工程目录进行编译了,设置完成之后请重启cmd


好,编译完成之后我们就可以在我们工程目录的obj目录下发现编译好的.so等相关文件:

注意,我们在Android.mk文件中定义的LOCAL_MODULE是JNI_C++,而这里的输出文件会为它自动加上lib前缀与.so后缀。

如果集成了NDK环境,在Clean的时候会自动进行编译:


编译完成之后,就需要看看如何使用它了。

在使用的Java文件Hello中添加如下静态代码块:

	static {System.loadLibrary("JNI_C++");}

在这里的JNI_C++便是我们在.mk文件中定义好的,在使用它的时候,它会自动为我们加上lib前缀与.so后缀,以便访问我们的.so文件,注意,千万别自己加lib*.so,以下是Runtime.loadLibrary的方法说明:

Given the name "MyLibrary", that string will be passed to System.mapLibraryName. That means it would be a mistake for the caller to include the usual "lib" prefix and ".so" suffix. 


好,接下来看一下如何运行:

在Activity中调用Hello这个类的getStringFromNative方法,这个方法便可以将从native中生成的字符串对象、Bean对象返回。

效果:


快试试,遇到什么问题欢迎留言。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/479468.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

论文浅尝 | 利用问题生成提升知识图谱问答

论文笔记整理&#xff1a;谭亦鸣&#xff0c;东南大学博士生&#xff0c;研究方向为知识库问答。来源&#xff1a;NLPCC2019链接&#xff1a;http://tcci.ccf.org.cn/conference/2019/papers/183.pdf本文提出了一种利用问题生成提升知识图谱问答模型性能的方法&#xff08;一个…

顶会论文:基于神经网络StarNet的行人轨迹交互预测算法

1.背景 民以食为天&#xff0c;如何提升超大规模配送网络的整体配送效率&#xff0c;改善数亿消费者在”吃“方面的体验&#xff0c;是一项极具挑战的技术难题。面向未来&#xff0c;美团正在积极研发无人配送机器人&#xff0c;建立无人配送开放平台&#xff0c;与产学研各方共…

python操作mysql数据库实现增删改查

python操作mysql数据库实现增删改查 Python 标准数据库接口为 Python DB-API&#xff0c;Python DB-API为开发人员提供了数据库应用编程接口。 Python 数据库接口支持非常多的数据库&#xff0c;你可以选择适合你项目的数据库&#xff1a; GadFlymSQLMySQLPostgreSQLMicrosoft …

LeetCode 654. 最大二叉树(递归)

文章目录1. 题目2. 解题1. 题目 给定一个不含重复元素的整数数组。一个以此数组构建的最大二叉树定义如下&#xff1a; 二叉树的根是数组中的最大元素。 左子树是通过数组中最大值左边部分构造出的最大二叉树。 右子树是通过数组中最大值右边部分构造出的最大二叉树。 通过给…

Probe:Android线上OOM问题定位组件

配送骑手端App是骑手用于完成配送履约的应用&#xff0c;帮助骑手完成接单、到店、取货及送达&#xff0c;提供各种不同的运力服务&#xff0c;也是整个外卖闭环中的重要节点。由于配送业务的特性&#xff0c;骑手App对于应用稳定性的要求非常高&#xff0c;体现App稳定性的一个…

Android中使用官方提供好的功能使用说明(比如系统图库获取),也作为延生学习的学习文档

这篇文章最核心的就是去学习如何学习Android&#xff0c;如何去使用Android文档。 我们一般在刚开始接触开发的时候&#xff0c;如果遇到无法解决的问题&#xff0c;常常会百度&#xff0c;或者google去寻找答案&#xff0c;比如有个需求是获取系统中的图片&#xff0c;你可能…

再介绍一篇Contrastive Self-supervised Learning综述论文

文 | 黄浴源 | 知乎之前已经介绍过三篇自监督学习的综述&#xff1a;《怎样缓解灾难性遗忘&#xff1f;持续学习最新综述三篇&#xff01;》。这是最近2020年10月arXiv上的又一篇论文"A Survey On Contrastive Self-supervised Learning"。论文地址&#xff1a;https…

GCN-Based User Representation Learning for Unifying Robust Recommendation and Fraudster Detection

GCN-Based User Representation Learning for Unifying Robust Recommendation and Fraudster Detection 点击率预测&#xff1a;其主要思想是根据用户的历史行为对一组未评级的项目进行评级预测&#xff0c;然后从预测评级最高的项目中选择个性化推荐。 欺诈检测&#xff1a;…

公开课 | 知识图谱构建与应用概述

本文转载自公众号&#xff1a;博文视点Broadview。 AI是新的生产力&#xff0c;知识图谱是AI进步的阶梯。随着近年来人工智能的进一步发展&#xff0c;知识图谱也取得了一系列新的进展&#xff0c;并在各个行业中落地应用。知识图谱的相关技术已经在搜索引擎、智能问答、…

LeetCode 217. 存在重复元素(哈希)

文章目录1. 题目2. 解题1. 题目 给定一个整数数组&#xff0c;判断是否存在重复元素。 如果任何值在数组中出现至少两次&#xff0c;函数返回 true。如果数组中每个元素都不相同&#xff0c;则返回 false。 示例 1:输入: [1,2,3,1] 输出: true 示例 2:输入: [1,2,3,4] 输出:…

美团BERT的探索和实践

2018年&#xff0c;自然语言处理&#xff08;Natural Language Processing&#xff0c;NLP&#xff09;领域最激动人心的进展莫过于预训练语言模型&#xff0c;包括基于RNN的ELMo[1]和ULMFiT[2]&#xff0c;基于Transformer[3]的OpenAI GPT[4]及Google BERT[5]等。下图1回顾了近…

论文浅尝 | 探索将预训练语言模型用于事件抽取和事件生成

论文笔记整理&#xff1a;郝凯龙&#xff0c;南京大学硕士链接&#xff1a;https://www.aclweb.org/anthology/P19-1522.pdf动机传统的 ACE 事件抽取任务依赖于人工标注的数据&#xff0c;耗费大量的人力并且数据量有限&#xff0c;数据量不足给事件抽取带来了阻碍。传统的事件…

谷歌、CMU发文:别压榨单模型了!集成+级联上分效率更高!

文 | Sherry 不是小哀集成模型&#xff08;Ensemble&#xff09;可以提升模型的精度&#xff0c;但往往面临提升计算量的困境&#xff0c;用级联模型&#xff08;Cascade&#xff09;在预测时提前中断则可解决计算量的问题。最近&#xff0c;谷歌和CMU的研究者对此进行了深入的…

LeetCode 219. 存在重复元素 II(哈希)

文章目录1. 题目2. 解题1. 题目 给定数组nums和常数k&#xff0c;存在不同的i、j使得nums[i] nums[j]&#xff0c;且abs(i-j) < k。 输入: nums [1,2,3,1], k 3 输出: true 示例 2:输入: nums [1,0,1,1], k 1 输出: true 示例 3:输入: nums [1,2,3,1,2,3], k 2 输出…

Android静态代码扫描效率优化与实践

背景与问题 DevOps实践中&#xff0c;我们在CI(Continuous Integration)持续集成过程主要包含了代码提交、静态检测、单元测试、编译打包环节。其中静态代码检测可以在编码规范&#xff0c;代码缺陷&#xff0c;性能等问题上提前预知&#xff0c;从而保证项目的交付质量。Andro…

还在用[CLS]?从BERT得到最强句子Embedding的打开方式!

文&#xff1a;涅生编&#xff1a;兔子酱你有尝试从 BERT 提取编码后的 sentence embedding 吗&#xff1f;很多小伙伴的第一反应是&#xff1a;不就是直接取顶层的[CLS] token的embedding作为句子表示嘛&#xff0c;难道还有其他套路不成&#xff1f;nono&#xff0c;你知道这…

论文浅尝 | BERT:Pre-training of Deep Bidirectional Transformers

论文笔记整理&#xff1a;王春培&#xff0c;天津大学硕士。链接&#xff1a;https://arxiv.org/pdf/1810.04805.pdf动机将预训练语言表示应用于下有任务现有两种策略&#xff1a;基于特征的和基于微调的。文章认为当前技术限制了预训练的能力&#xff0c;尤其是基于微调的方法…

欺诈检测相关论文

欺诈检测相关论文一、分类1、GEM2、HACUD3、MAHINDER4、Semi-GNN5、MvMoE6、AMG-DP7、AddGraph8、NetWalk9、DOMINANT10、GraphConsis11、PC-GNN12、TRUST二、类别不平衡一、分类 1、GEM 来自蚂蚁金服的论文&#xff0c;他们提出GEM模型&#xff0c;是一个异质图神经网络方法&a…

LeetCode 220. 存在重复元素 III(lower_bound)

文章目录1. 题目2. 解题1. 题目 给定一个整数数组&#xff0c;判断数组中是否有两个不同的索引 i 和 j&#xff0c;使得 nums [i] 和 nums [j] 的差的绝对值最大为 t&#xff0c;并且 i 和 j 之间的差的绝对值最大为 ķ。 示例 1:输入: nums [1,2,3,1], k 3, t 0 输出: tr…

Android自定义控件入门实践之雷达扫描控件

以前因为工作的关系&#xff0c;对于自定义控件用的少之又少&#xff0c;无非就是把几个控件放置到ViewGroup内部&#xff0c;然后提供开放方法&#xff0c;就成了一个所谓的自定义控件&#xff0c;但是这种小伎俩太简单&#xff0c;面试的时候这点东西根本Hold不住场&#xff…