android ndk是什么怎么调用c实现的返回值为int指针类型

您所在的位置: &
Android开发进阶:Android NDK介绍(1)
Android开发进阶:Android NDK介绍(1)
本文旨在帮助那些缺乏Android NDK经验但又想扩充这方面知识的人们。我所关注的是JNI(本地编程接口,简称JNI)。本文分上下两篇,在上篇中,会从JNI为接口开始讲起;下篇会进行回顾,并给出带两个文件读写功能的实例。
为了在Android&OS系统上开发应用程序,Google提供了两种开发包:SDK和NDK。你可以从Google官方查阅到有许多关于SDK 的优秀的书籍、文章作为参考,但Google没有提供足够的NDK资料。在现有的书籍中,我认为Cinar&O.写于2012年 的&Pro&Android&C++&with&the&NDK&值得一读。
本文旨在帮助那些缺乏Android&NDK经验但又想扩充这方面知识的人们。我所关注的是JNI(本地编程接口,简称JNI)。本文分上下两篇,在上篇中,会从JNI为接口开始讲起;下篇会进行回顾,并给出带两个文件读写功能的实例。
什么是&Android&NDK?
Android&NDK(Native&Development&Kit&)是一套工具集合,允许你用像C/C++语言那样实现应用程序的一部分。
何时使用NDK?
Google仅在极少数情况下建议使用NDK,有如下使用场景:
必须提高性能(例如,对大量数据进行排序)。
使用第三方库。举例说明:许多第三方库由C/C++语言编写,而Android应用程序需要使用现有的第三方库,如Ffmpeg、OpenCV这样的库。
底层程序设计(例如,应用程序不依赖Dalvik&Java虚拟机)。
什么是JNI?
JNI是一种在Java虚拟机控制下执行代码的标准机制。代码被编写成汇编程序或者C/C++程序,并组装为动态库。也就允许了非静态绑定用法。这提供了一个在Java平台上调用C/C++的一种途径,反之亦然。
与其他类似接口(Netscape
Java运行接口、Microsoft的原始本地接口、COM/Java接口)相比,JNI主要的竞争优势在于:它在设计之初就确保了二进制的兼容 性,JNI编写的应用程序兼容性以及在某些具体平台上的Java虚拟机兼容性(当谈及JNI,这里并不特别针对Dalvik;JNI由Oracle开发, 适用于所有Java虚拟机)。这就是为什么C/C++编译后的代码无论在任何平台上都能执行。不过,一些早期版本并不支持二进制兼容。
二进制兼容性是一种程序兼容性类型,允许一个程序在不改变其可执行文件的条件下在不同的编译环境中工作。
JNI组织结构
498)this.width=498;' onmousewheel = 'javascript:return big(this)' class="wp-image-8043" src="/wyfs01/M01/45/DE/wKioJlK87PbSVENQAAAZqFYU5dg793.jpg" alt="图1 & JNI接口指针" />图1 & JNI接口指针
这张JNI函数表的组成就像C++的虚函数表。虚拟机可以运行多张函数表,举例来说,一张调试函数表,另一张是调用函数表。JNI接口指针仅在当前线程中起作用。这意味着指针不能从一个线程进入另一个线程。然而,可以在不同的线程中调用本地方法。
示例代码:
jdouble&Java_pkg_Cls_f__ILjava_lang_String_2&(JNIEnv&*env,&jobject&obj,&jint&i,&jstring&s)&{&&&&&&const&char&*str&=&(*env)-&GetStringUTFChars(env,&s,&0);&&&&&&(*env)-&ReleaseStringUTFChars(env,&s,&str);&&&&&&return&10;&}&
*env & 一个接口指针。
obj & 在本地方法中声明的对象引用。
i和s & 用于传递的参数。
原始类型(Primitive Type)在虚拟机和本机代码进行拷贝,对象之间使用引用进行传递。VM(虚拟机)要追踪所有传递给本地代码的对象引用。GC无法释放所有传递给本地代码的对象引用。与此同时,本机代码应该通知VM不需要的对象引用。
局部引用和全局引用
JNI定义了三种引用类型:局部引用、全局引用和全局弱引用。局部引用在方法完成之前是有效的。所有通过JNI函数返回的Java对象都是本地引 用。程序员希望VM会清空所有的局部引用,然而局部引用仅在其创建的线程里可用。如果有必要,局部引用可以通过接口中的DeleteLocalRef
JNI方法立即释放:
jclass&&clazz&=&(*env)-&FindClass(env,&&java/lang/String&);&...&(*env)-&DeleteLocalRef(env,&clazz)&
全局引用在完全释放之前都是有效的。要创建一个全局引用,需要调用NewGlobalRef方法。如果全局引用并不是必须的,可以通过DeleteGlobalRef方法删除:
jclass&localC&jclass&globalC&...&localClazz&=&(*env)-&FindClass(env,&&java/lang/String&);&globalClazz&=&(*env)-&NewGlobalRef(env,&localClazz);&...&(*env)-&DeleteLocalRef(env,&localClazz);&
JNI不会检查NullPointerException、IllegalArgumentException这样的错误,原因是:
导致性能下降。
在绝大多数C的库函数中,很难避免错误发生。
JNI允许用户使用Java异常处理。大部分JNI方法会返回错误代码但本身并不会报出异常。因此,很有必要在代码本身进行处理,将异常抛给Java。在JNI内部,首先会检查调用函数返回的错误代码,之后会调用ExpectOccurred()返回一个错误对象。
jthrowable&ExceptionOccurred(JNIEnv&*env);&
例如:一些操作数组的JNI函数不会报错,因此可以调用ArrayIndexOutofBoundsException或ArrayStoreExpection方法报告异常。
JNI原始类型
JNI有自己的原始数据类型和数据引用类型。
本地类型()
boolean(布尔型)
无符号8个比特
byte(字节型)
有符号8个比特
char(字符型)
无符号16个比特
short(短整型)
有符号16个比特
有符号32个比特
long(长整型)
有符号64个比特
float(浮点型)
double(双精度浮点型)
void(空型)
JNI引用类型
图2 & JNI引用类型
改进的UTF-8编码
JNI使用改进的UTF-8字符串来表示不同的字符类型。Java使用UTF-16编码。UTF-8编码主要使用于C语言,因为它的编码用\u000表示为0xc0,而不是通常的0&00。非空ASCII字符改进后的字符串编码中可以用一个字节表示。
内容导航&第 1 页: &第 2 页:
关于&&的更多文章
本专题意在帮助想要了解Android的人能快速上手Android开发,能对
既然强大的Android Studio来了,有什么理由不去用呢?
在本系列文章当中,我们将从零开始学习Android开发。
寒冷的北京城依旧雾蒙蒙。北方的十二月本该是安逸静谧
智能电视正在加速来到我们的生活,在一些人对这个名词
一个网站,无论视觉上多美观或者内容多丰富,如果它不能适应各种浏览情况并能面向尽可能广泛的用户群,那它就不算是真正成功的网
Windows Phone专家
Android开发专家
51CTO旗下网站 下载
 收藏
该文档贡献者很忙,什么也没留下。
 下载此文档
正在努力加载中...
Android面试笔试题集
下载积分:300
内容提示:Android面试笔试题集
文档格式:PDF|
浏览次数:1|
上传日期: 06:50:19|
文档星级:
该用户还上传了这些文档
下载文档:Android面试笔试题集.PDF
官方公共微信使用Android NDK和Java测试Linux驱动
在Android系统中Linux驱动主要的使用者是APK程序。因此,Linux驱动做完后必须要用APK程序进行测试才能说明Linux驱动可以正常使用。由于上一节在Android上使用C语言编写的可执行程序测试了Linux驱动,因此很容易想到可以利用Android NDK来测试Linux驱动,
由于Android NDK也使用C/C++来编写程序,因此可以利用上一节的C语言代码,当然,还得加上一些AndroidNDK特有的代码。在使用AndroidNDK测试Linux驱动之前需要做如下两件事。
1. 由于Linux驱动模块不会随Android系统启动而装载,因此必须执行build.sh脚本文件安装word_count驱动。
2. 不能使用默认方式启动Android模拟器,而要使用我们自己编译的Linux内核启动Android模拟器,启动模拟器的命令如下:
# &emulator-avd myavd -kernel /root/kernel/goldfish/arch/arm/boot/zImage
为了方便,读者也可以在随书光盘带的Ubuntu Linux虚拟环境中直接执行如下的命令来异步启动Android模拟器。其中emulator.sh文件在/root/drivers目录中。
# sh emulator.sh &
本节的例子已经包含在随书光盘和虚拟环境中,路径如下:
随书光盘:&光盘根目录&/sources/ch06/word_count/word_count_ndk
虚拟环境:/root/drivers/ch06/word_count/word_count_ndk
word_count_ndk工程的代码部分由WordCountNDKTestMain.java和ndk_test_word_count.c文件组成。工程结构如图6-17所示。
& & & &ndk_test_word_count.c文件用于访问word_count驱动。该文件包含两个供访问的函数,分别用来读取/dev/wordcount设备文件中的单词数和向/dev/wordcount设备文件写入字符串。下面先看看ndk_test_word_count.c文件的完整代码。
#include &string.h& &
#include &jni.h& &
#include &fcntl.h& &
#include &stdio.h& &
#include &sys/types.h& &
#include &sys/stat.h& &
#include &unistd.h& &
#include &stdlib.h& &
// &JNI函数:readWordCountFromDev &
// &用于从/dev/wordcount设备文件读取单词数 &
jint Java_mobile_android_word_count_ndk_WordCountNDKTestMain_readWordCountFromDev( &
& & & & JNIEnv* env, jobject thiz) &
& & & & & & & & & &// &open函数打开/dev/wordcount设备文件后返回的句柄,打开失败返回-1 &
& & jint wordcount = 0; & & // &单词数 &
& & unsigned char buf[4]; & // &以4个字节形式存储的单词数 &
& & & & // &以只读方式打开/dev/wordcount设备文件 &
& & dev = open(&/dev/wordcount&, O_RDONLY); &
& & // &从dev/wordcount设备文件中读取单词数 &
& & read(dev, buf, 4); &
& & int n = 0; & & & & & & &// &存储单词数的int类型变量 &
& & // &将由4个字节表示的单词数转换成int类型的值 &
& & n = ((int) buf[0]) && 24 | ((int) buf[1]) && 16 | ((int) buf[2]) && 8 | ((int) buf[3]); &
& & & & &// &将int类型的单词数转换成jint类型的单词数 &
& & wordcount = (jint) &
& & // &关闭/dev/wordcount设备文件 &
& & close(dev); &
& & // &返回单词数 &
// &将jstring类型的值转换成char *类型的值 &
char* jstring_to_pchar(JNIEnv* env, jstring str) &
& & char* pstr = NULL; &
& & // &下面的代码会调用Java中的String.getBytes方法获取字符串的字节数 &
& & // &获取java.lang.String类 &
& & jclass clsstring = (*env)-&FindClass(env, &java/lang/String&); &
& & // &将字符串&utf-8&转换成jstring类型的值 &
& & jstring strencode = (*env)-&NewStringUTF(env, &utf-8&); &
& & // &获取java.lang.String.getBytes方法 &
& & jmethodID mid = (*env)-&GetMethodID(env, clsstring, &getBytes&, &(Ljava/lang/S)[B&); &
& & // &调用String.getBytes方法将str变量的值转换成jbytearray类型的值 &
& & jbyteArray byteArray = (jbyteArray)( (*env)-&CallObjectMethod(env, str, mid, strencode)); &
& & // &获取字节长度 &
& & jsize size = (*env)-&GetArrayLength(env, byteArray); &
& & // &将jbytearray类型的值转换成jbyte*类型的值 &
& & jbyte* pbyte = (*env)-&GetByteArrayElements(env, byteArray, JNI_FALSE); &
& & if (size & 0) &
& & & & // &为char*类型变量pstr分配空间 &
& & & & pstr = (char*) malloc(size); &
& & & & // &将pbyte变量中的值复制到pstr变量中 &
& & & & memcpy(pstr, pbyte, size); &
& & // &返回转换后的值 &
// &JNI函数:writeStringToDev &
// &用于向/dev/wordcount设备文件写入字符串 &
void Java_mobile_android_word_count_ndk_WordCountNDKTestMain_writeStringToDev( &
& & & & JNIEnv* env, jobject thiz, jstring str) &
& & & // &open函数打开/dev/wordcount设备文件后返回的句柄,打开失败返回-1 &
& & // &以只写方式打开/dev/wordcount设备文件 &
& & dev = open(&/dev/wordcount&, O_WRONLY); &
& & // &将jstring类型字符串转换成char* 类型的值 &
& & char* pstr = jstring_to_pchar(env, str); &
& & if (pstr != NULL) &
& & & & // &向/dev/wordcount设备文件写入字符串 &
& & & & write(dev,pstr, strlen(pstr)); &
& & // &关闭/dev/wordcount设备文件 &
& & close(dev); &
& & &编写上面的代码有一个重点就是jstring_to_pchar函数。该函数可以将jstring类型的数据转换成char*类型的数据。转换的基本思想就是调用Java方法String.getBytes,获取字符串对应的字节数组(jbyteArray)。由于write函数需要的是char *类型的数据,因此,还必须将jbyteArray类型的数据转换成char *类型的数据。采用的方法是先将jbyteArray类型的数据转换成jbyte类型的数据,然后调用memcpy函数将jbyte类型的数据复制到使用malloc函数分配的char *指针空间中。在jstring_to_pchar函数中有如下的一行代码。
jmethodID mid = (*env)-&GetMethodID(env,clsstring, &getBytes&, &(Ljava/lang/S)[B&];
& & &看到getMethodID方法最后一个参数的值是&(Ljava/lang/S)[B&,可能Android NDK初学者会对此感到困惑,以为是写错了。实际上这是JNI(Android NDK程序实际上就是遵循JNI规则的程序)对方法参数和返回类型的描述。在JNI程序中为了方便描述Java数据类型,将简单类型使用了一个大写英文字母表示,如表6-1所示。
& & 除了表6-1所示的Java简单类型外,还有一些数据类型需要在JNI代码中与其对应。表6-2是这些数据类型在JNI中的描述符。
& & &从表6-2所示的数据类型对照关系很容易想到本例中的&(Ljava/lang/S)[B&是什么意思。jstring_to_pchar函数调用的是如下的getBytes方法的重载形式。
public byte[] getBytes(String charsetName) throwsUnsupportedEncodingException
在JNI中调用Java方法需要指定方法参数和返回值的数据类型。在JNI中的格式如下:
&(参数类型)返回值类型&
& & &getBytes方法的参数类型是String,根据表6-2的描述,String类型中JNI在的描述符是&Ljava/lang/S &。getBytes方法的返回值类型是byte[]。这里就涉及到一个数组的表示法。在JNI中数组使用左中括号([]表示,后面是数组中元素的类型。每一维需要使用一个&[&。byte[]是一维字节数组,所以使用&[B&表示。如果是byte[][][],应使用&[[[B&表示。如果Java方法未返回任何值(返回值类型是void),则用V表示。如void mymethod(int value)的参数和返回值类型可表示为&(I)V&。
Android NDK程序还需要一个Android.mk文件,代码如下:
LOCAL_PATH := $(call my-dir) &
include $(CLEAR_VARS) &
LOCAL_MODULE & &:= ndk_test_word_count &
LOCAL_SRC_FILES := ndk_test_word_count.c &
include $(BUILD_SHARED_LIBRARY) &
在编写Java代码调用JNI函数之前,先看一下本例的界面,如图6-18所示。
& & & &读者需要先在PC上运行build.sh脚本文件安装word_count驱动。然后单击&从/dev/wordcount读取单词数&按钮,会在按钮下方输出当前/dev/wordcount设备文件中统计出的单词数。读者也可以在输入框中输入一个由空格分隔的字符串,然后单击&向/dev/wordcount写入字符串&按钮,再单击&从/dev/wordcount读取单词数&按钮,就会统计出字符串中包含的单词数,效果如图6-19所示。
下面看一下本例中Java部分(WordCountNDKTestMain.java)的完整代码。
package mobile.android.word.count. &
import android.app.A &
import android.os.B &
import android.view.V &
import android.widget.EditT &
import android.widget.TextV &
import android.widget.T &
public class WordCountNDKTestMain extends Activity &
& & private TextView tvWordC &
& & private EditText etS &
& & @Override &
& & public void onCreate(Bundle savedInstanceState) &
& & & & super.onCreate(savedInstanceState); &
& & & & setContentView(R.layout.main); &
& & & & tvWordCount = (TextView) findViewById(R.id.textview_wordcount); &
& & & & etString = (EditText) findViewById(R.id.edittext_string); &
& & // &&从/dev/wordcount读取单词数&按钮的执行代码 &
& & public void onClick_ReadWordCountFromDev(View view) &
& & & & // &显示单词数 &
& & & & tvWordCount.setText(&单词数:& + String.valueOf(readWordCountFromDev())); &
// &向/dev/wordcount写入字符串&按钮的执行代码 &
& & public void onClick_WriteStringToDev(View view) &
& & & & // &向/dev/wordcount设备文件写入字符串 &
& & & & writeStringToDev(etString.getText().toString()); &
& & & & Toast.makeText(this, &已向/dev/wordcount写入字符串&, Toast.LENGTH_LONG).show(); &
& & // native方法 &
& & public native int readWordCountFromDev(); &
& & public native void writeStringToDev(String str); &
& & static &
& & & & System.loadLibrary(&ndk_test_word_count&); &
WordCountNDKTestMain.java中的代码只是简单地调用了JNI函数来操作/dev/wordcount文件。其他的代码都是常规的应用级别的代码。如果读者对这部分不熟悉,可以参阅笔者所著的《Android开发权威指南》。
四、使用Java代码直接操作设备文件来测试驱动
& & & 如果Android拥有root权限,完全可以直接使用Java代码操作/dev/wordcount设备文件(没有root权限,Linux驱动模块是无法安装的)。本节将介绍如何使用Java代码来测试Linux驱动(测试程序不使用一行C/C++代码)。本节示例的路径如下:
随书光盘:&光盘根目录&/sources/ch06/word_count/word_count_java
虚拟环境:/root/drivers/ch06/word_count/word_count_java
word_count_java工程中只有一个源代码文件WordCountJavaTestMain.java。该文件的内容如下:
package mobile.android.word.count. &
import java.io.FileInputS &
import java.io.FileOutputS &
import android.app.A &
import android.os.B &
import android.view.V &
import android.widget.EditT &
import android.widget.TextV &
import android.widget.T &
public class WordCountJavaTestMain extends Activity &
& & private TextView tvWordC &
& & private EditText etS &
& & @Override &
& & public void onCreate(Bundle savedInstanceState) &
& & & & super.onCreate(savedInstanceState); &
& & & & setContentView(R.layout.main); &
& & & & tvWordCount = (TextView) findViewById(R.id.textview_wordcount); &
& & & & etString = (EditText) findViewById(R.id.edittext_string); &
& & // &&从/dev/wordcount读取单词数&按钮的执行代码 &
& & public void onClick_ReadWordCountFromDev(View view) &
& & & & // &显示单词数 &
& & & & tvWordCount.setText(&单词数:& + String.valueOf(readWordCountFromDev())); &
// &向/dev/wordcount写入字符串&按钮的执行代码 &
& & public void onClick_WriteStringToDev(View view) &
& & & & // &向/dev/wordcount设备文件写入字符串 &
& & & & writeStringToDev(etString.getText().toString()); &
& & & & Toast.makeText(this, &已向/dev/wordcount写入字符串&, Toast.LENGTH_LONG).show(); &
& & // &下面是用Java实现的操作/dev/wordcount设备文件的代码 &
& & // &读取/dev/wordcount设备文件中的单词数 &
& & private int readWordCountFromDev() &
& & & & int n = 0; &
& & & & byte[] buffer = new byte[4]; &
& & & & try &
& & & & { &
& & & & & & // &打开/dev/wordcount设备文件 &
& & & & & & FileInputStream fis = new FileInputStream(&/dev/wordcount&); &
& & & & & & // &从设备文件中读取4个字节 &&
& & & & & & fis.read(buffer); &
& & & & & & // &将4个字节转换成int类型的值 &
& & & & & & n = ((int) buffer[0]) && 24 | ((int) buffer[1]) && 16 &
& & & & & & & & & & | ((int) buffer[2]) && 8 | ((int) buffer[3]); &&
& & & & & & fis.close(); &
& & & & } &
& & & & catch (Exception e) &
& & & & { &
& & & & } &
& &// &向/dev/wordcount设备文件中写入字符串 &
& & private void writeStringToDev(String str) &
& & & & try &
& & & & { &
& & & & & & // &打开/dev/wordcount设备文件 &
& & & & & & FileOutputStream fos = new FileOutputStream(&/dev/wordcount&); &
& & & & & & // &写入字符串 &
& & & & & & fos.write(str.getBytes(&iso-8859-1&)); &
& & & & & & fos.close(); &
& & & & } &
& & & & catch (Exception e) &
& & & & { &
& & & & } &
您对本文章有什么意见或着疑问吗?请到您的关注和建议是我们前行的参考和动力&&
您的浏览器不支持嵌入式框架,或者当前配置为不显示嵌入式框架。

我要回帖

更多关于 android studio ndk 的文章

 

随机推荐