Android坑点-ByteBuffer.array() 入过坑吗

目录

  • 1、坑点介绍
  • 2、正确使用姿势(入坑了怎么办)
  • 3、坑坑详解
    • 3.1HeapByteBuffer可以用buffer.array()
    • 3.2DirectByteBuffer的坑在哪里

1、坑点介绍

如下代码:

ByteBuffer buffer =  ByteBuffer.allocateDirect(int capacity)
byte[] array =  buffer.array()

在android平台前面会有几个字节是没有实际数据的,在jre环境下,发生异常。
怎么理解呢?
来个栗子:

 private void bufferTest() {ByteBuffer buffer = ByteBuffer.allocateDirect(10);buffer.put((byte) 1);buffer.put((byte) 2);byte[] arr = buffer.array();//这里使用array返回字节数组for (int i = 0; i < 10; i++) {Log.d("BufferTester", "bufferTest arr[" + i + "]=" + arr[i]);}}

结果如下:

2021-10-27 15:29:43.169 D/BufferTester: bufferTest arr[0]=0
2021-10-27 15:29:43.169 D/BufferTester: bufferTest arr[1]=0
2021-10-27 15:29:43.169 D/BufferTester: bufferTest arr[2]=0
2021-10-27 15:29:43.169 D/BufferTester: bufferTest arr[3]=0
2021-10-27 15:29:43.169 D/BufferTester: bufferTest arr[4]=1
2021-10-27 15:29:43.169 D/BufferTester: bufferTest arr[5]=2
2021-10-27 15:29:43.169 D/BufferTester: bufferTest arr[6]=0
2021-10-27 15:29:43.169 D/BufferTester: bufferTest arr[7]=0
2021-10-27 15:29:43.169 D/BufferTester: bufferTest arr[8]=0
2021-10-27 15:29:43.169 D/BufferTester: bufferTest arr[9]=0

通过上面的结果可以看到 arr[4]=1, arr[5]=2。我们是put了1和2,正常情况是从数组第一个元素开始写入,我们取出的数组却跳跃了前面4个字节。

2、正确使用姿势(入坑了怎么办)

实际上buffer.array() 是不能用的,我们需要使用get(byte[] dst)get(byte[] dst, int offset, int length),具体代码如下

 private void bufferTest() {ByteBuffer buffer = ByteBuffer.allocateDirect(10);buffer.put((byte) 1);buffer.put((byte) 2);
//        byte[] arr = buffer.array();buffer.position(0);byte []arr = new byte[10];//put 2次,实际是2个字节buffer.get(arr);for (int i = 0; i < 10; i++) {Log.d("BufferTester", "bufferTest arr[" + i + "]=" + arr[i]);}}

结果:

2021-10-27 15:53:49.954 D/BufferTester: bufferTest arr[0]=1
2021-10-27 15:53:49.954 D/BufferTester: bufferTest arr[1]=2
2021-10-27 15:53:49.954 D/BufferTester: bufferTest arr[2]=0
2021-10-27 15:53:49.954 D/BufferTester: bufferTest arr[3]=0
2021-10-27 15:53:49.954 D/BufferTester: bufferTest arr[4]=0
2021-10-27 15:53:49.954 D/BufferTester: bufferTest arr[5]=0
2021-10-27 15:53:49.954 D/BufferTester: bufferTest arr[6]=0
2021-10-27 15:53:49.954 D/BufferTester: bufferTest arr[7]=0
2021-10-27 15:53:49.954 D/BufferTester: bufferTest arr[8]=0
2021-10-27 15:53:49.954 D/BufferTester: bufferTest arr[9]=0

arr[0]=1,arr[1]=2 是我们期望的结果,也是正确结果。
注:put一字节,postion+1,所以上述代码在取的时候,将pos恢复到起始位置。
所以请使用get方法来获取内容。

如果还是要用array(),那么一定要完全掌握好DirectByteBuffer的机制。

3、坑坑详解

ByteBuffer是一个抽象类,通过类函数 allocateDirect(int capacity)创建的是DirectByteBuffer对象;通过类函数allocate(int capacity)创建的是HeapByteBuffer对象。关系和代码如下

  1. HeapByteBuffer extends ByteBuffer
public static ByteBuffer allocate(int capacity) {if (capacity < 0)throw new IllegalArgumentException();return new HeapByteBuffer(capacity, capacity);}
  1. DirectByteBuffer extends MappedByteBuffer extends ByteBuffer
 public static ByteBuffer allocateDirect(int capacity) {if (capacity < 0) {throw new IllegalArgumentException("capacity < 0: " + capacity);}DirectByteBuffer.MemoryRef memoryRef = new DirectByteBuffer.MemoryRef(capacity);return new DirectByteBuffer(capacity, memoryRef);}

简单过一下:HeapByteBuffer 是堆上分配的空间,DirectByteBuffer是系统分配的空间。

3.1HeapByteBuffer可以用buffer.array()

private HeapByteBuffer(int cap, int lim, boolean isReadOnly) {super(-1, 0, lim, cap, new byte[cap], 0);this.isReadOnly = isReadOnly;}

因为调用super的时候,就 按照给定的大小(new byte[cap]) 分配了数组传递给父类,最终调用array()的时候,就返回的这个数组。

3.2DirectByteBuffer的坑在哪里

注意了,我们重点看两个构造函数
DirectByteBuffer.MemoryRef memoryRef = new DirectByteBuffer.MemoryRef(capacity);
return new DirectByteBuffer(capacity, memoryRef);

  1. MemoryRef内存分配
    注意看注释要点1、2、3:
  MemoryRef(int capacity) {VMRuntime runtime = VMRuntime.getRuntime();//要点1,分配了一个字节数组,大小是我们给的大小+ 7,例如我们给10,分配了17字节buffer = (byte[]) runtime.newNonMovableArray(byte.class, capacity + 7);//要点2,得到buffer的地址(指针)allocatedAddress = runtime.addressOf(buffer);// Offset is set to handle the alignment: http://b/16449607//要点3、计算一个偏移量,计算结果是在0-7之间。offset = (int) (((allocatedAddress + 7) & ~(long) 7) - allocatedAddress);isAccessible = true;isFreed = false;originalBufferObject = null;}
  1. DirectByteBuffer(int capacity, MemoryRef memoryRef)内存管理与使用
DirectByteBuffer(int capacity, MemoryRef memoryRef) {//要点4、将要点1的数组和要点3的偏移传递给父类super(-1, 0, capacity, capacity, memoryRef.buffer, memoryRef.offset);this.memoryRef = memoryRef;//要点5、给address赋值为要点2的地址+要点3的偏移地址this.address = memoryRef.allocatedAddress + memoryRef.offset;cleaner = null;this.isReadOnly = false;
}

也许老铁已经看出了端倪,此处传递给父类的数组是capacity + 7长度的,其中address是数组的一个子集,举个例子,如果capacity = 10,offset = 5,那么buffer的长度是17,allocatedAddress 是buffer[0]的地址,address 就是buffer[5] 地址。

  1. put是自行实现的
 public final ByteBuffer put(byte x) {if (!memoryRef.isAccessible) {throw new IllegalStateException("buffer is inaccessible");}if (isReadOnly) {throw new ReadOnlyBufferException();}put(ix(nextPutIndex()), x);return this;}private ByteBuffer put(long a, byte x) {
//填充数据Memory.pokeByte(a, x);return this;}private long ix(int i) {
//这里是重点,新加入的字节,用address+已经存在的长度所在的位置进行填充数据。return address + i;}
  1. get(byte[] dst, int dstOffset, int length) 也是通过addr来操作的
public ByteBuffer get(byte[] dst, int dstOffset, int length) {if (!memoryRef.isAccessible) {throw new IllegalStateException("buffer is inaccessible");}checkBounds(dstOffset, length, dst.length);int pos = position();int lim = limit();assert (pos <= lim);int rem = (pos <= lim ? lim - pos : 0);if (length > rem)throw new BufferUnderflowException();//通过address来取数据Memory.peekByteArray(ix(pos),dst, dstOffset, length);position = pos + length;return this;}

总体来说DirectByteBuffer分配了一个字节数组buffer 长度capacity+7,并计算出了一个小于7的偏移值x,再得到一个buffer的偏移地址,address = buffer+x。put、get都是通过address来存取的,因偏移,存取起始位置不一定是buffer[0],array() 返回的是buffer,因此可能存在前面x个字节没有值。

  1. buffer长度验证
private void bufferTest() {ByteBuffer buffer = ByteBuffer.allocateDirect(10);byte[] arr = buffer.array();Log.d("BufferTester", "bufferTest len = " + arr.length);

参数给的10,buffer实际长度17:

2021-10-27 17:18:14.740 D/BufferTester: bufferTest len = 17

同时在jre的环境下,DirectByteBuffer的实现稍有区别,没有偏移,且array被调用会抛出异常。没有计算offset也没有持有数组对象,只通过一个地址进行存取。

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

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

相关文章

php四则运算出题器_php实现简单四则运算器

本文实例为大家分享了php实现简单四则运算器的具体代码&#xff0c;供大家参考&#xff0c;具体内容如下前端代码 &#xff1a;php计算器请输入num1&#xff1a;请选择运算符&#xff1a;-*/%请输入num2&#xff1a;后台&#xff1a;php计算器$num1$_post["num1"];$n…

步苹果iOS的后尘,谷歌Android12“翻车”,更新需谨慎?

苹果不论电脑还是移动设备&#xff0c;都是一如既往的“强硬”。说实话&#xff0c;忽视“兼容”&#xff0c;体验极低。 有小伙伴调侃&#xff1a;人家就是要你买新机器。也有小伙伴&#xff08;包括我在内&#xff09;&#xff0c;直接关闭系统自动更新。 开发者&#xff1a…

word把选择答案弄到题目里_word中把选择题的正确答案自动填到括号里技巧

为了适应各类复习迎考&#xff0c;大家都会利用一些题目来练习。当面对题目与答案分离的现状(两个文档或一个文档的两个部分)时&#xff0c;怎样将题目和答案合二为一&#xff0c;使答案自动填充到题目后的括号中是颇让大家头疼的一件事情。经过实践探索多步骤的组合操作可以实…

Android Studio无线连接设备调试,比数据线更方便

前言 一般情况下&#xff0c;多数移动开发者使用的是数据线连接电脑&#xff0c;进行各种移动设备的调试&#xff0c;更有胜者&#xff0c;非常迷恋模拟器&#xff0c;模拟器它好不好&#xff0c;答案是好&#xff0c;因为直接运行在电脑上&#xff0c;直接操作&#xff0c;调试…

机器学习里面的基函数_神经网络与机器学习 笔记—核方法和径向基函数网络(上)...

对于神经网络的监督学习有多种不同方法。之前一直总结整理的多层感知器反向传播算法&#xff0c;可以看做是递归技术的应用&#xff0c;这种技术在统计学中通称为随机逼近。这次是采用不同的途径&#xff0c;通过包含如下两个阶段的混合方式解决非线性可分模式的分类问题&#…

AndroidJava List与equals的微妙关系,小心掉坑里

前言 List 有多个实现&#xff0c;本文以ArrayList(LinkedList也一样)作为说明&#xff0c;equals是Object的一个成员函数&#xff0c;例子中的bean重写实现它。 一、Bean 类定义并重写equals函数 public class Book {private String id;private String name;public String ge…

apache缺省banner_http服务器缺省banner

HTTP协议详解(真的很经典)HTTP协议详解(真的很经典)Author :清晨引言HTTP是一个属于应用层的面向对象的协议&#xff0c;由于其简捷、快速的方式&#xff0c;适用于分布式超媒体信息系统。它于1990年提出&#xff0c;经过几年的使用与发展&#xff0c;得到不断地完善和扩展。目…

GenseeSDK 使用Kotlin要注意TODOAndroid Studio关闭TODO

目录一、Kotlin的TODO二、GenseeSDK与TODO 请注意三、Android studio关闭TODO一、Kotlin的TODO 在实现一些接口时候&#xff0c;工具自动将所有的接口函数"空"实现&#xff0c;并在函数体中增加一行代码&#xff1a; TODO或TODO(“not implemented”) 作为提醒催促…

如何启动netcat_Netcat用法

Netcat用法--服务泄漏内部信息要得到某些端口号的详细信息&#xff0c;可以连接到某个端口&#xff0c;对应的服务会告知它的版本号、结构甚至其工作的操作系统。所以&#xff0c;可以使用Netcat来扫描一个特定范围内的端口并报告在这些端口上运行的服务。要使Netcat自动工作&a…

AndroidJava try-catch-finally正确用法

目录一、try-catch-finally的用途二、try-catch的正确使用三、奇怪的try-finally &#xff08;错误的用法&#xff09;四、关于try-catch-finally的面试考察1、try、catch、finally 考察&#xff0c;请指出下面程序的运行结果。2、try、catch、finally 考察2&#xff0c;请指出…

python tcp服务器并发_python tcp并发服务器

{"moduleinfo":{"card_count":[{"count_phone":1,"count":1}],"search_count":[{"count_phone":6,"count":6}]},"card":[{"des":"云服务器 ECS(Elastic Compute Service)是一…

OpenCV Mat基础认知感

OpenCV是一个开源的供开发的计算机视觉处理库&#xff0c;涵盖的内容包括图像处理&#xff0c;机器学习。由c到c &#xff0c;再到各平台的跨平台框架。 Mat - 图像容器 Mat类用于表示一个多维的单通道或者多通道的稠密数组。能够用来保存实数或复数的向量、矩阵&#xff0c;…

layui多文件上传讲解_Layui 多文件上传 SSH

jsp 页面pageEncoding"UTF-8"%>Insert title here选择多文件文件名大小状态操作开始上传layui.use(upload, function(){var $ layui.jquery,upload layui.upload;//多文件列表示例var demoListView $(#demoList),uploadListIns upload.render({elem: #testLis…

OpenCV:H1.type() == H2.type() H1.depth() == CV_32F

如题&#xff1a; E/cv::error(): OpenCV(4.1.0) Error: Assertion failed (H1.type() H2.type() && H1.depth() CV_32F) in compareHist, file /build/master_pack-android/opencv/modules/imgproc/src/histogram.cpp, line 1936 E/org.opencv.imgproc: imgproc::co…

css 外弧_css 伪类实现弧形

在实现页面五花八门的有特色的ui时&#xff0c;我们有时会遇到要用实现一个弧形&#xff0c;而这样的弧形要怎么实现呢&#xff1f;用图片&#xff1f;不太优雅&#xff0c;这样就要无故多加载一张图片了&#xff0c;这里来说说怎么用css的after伪类来实现弧形。总思路&#xf…

C++期末实践程序设计与数组作为参数的注意事项

目录小表弟发来的求助信号要点代码文件头文件Student.h源文件Student.cppmain.c执行结果c数组特性以及数组做形参的特点数组试验数组特殊性质不允许拷贝和赋值数组是通常被转化成指针使用数组形参多种写法代理模式MVC模式小表弟发来的求助信号 并补充说要5种写法才算过关。 要…

sgm3157功能_SGM3157_SGM3157供应商_价格_Datasheet_pdf资料-IC资料网

SGM31574.5惟 Low Voltage SPDT Analog SwitchSGMICROSG Micro CorpLow Voltage SPDT Analog Switch in 6-Pin SC70SGMICROSG Micro Corp4.5惟 Low Voltage SPDT Analog SwitchSGMICROSG Micro CorpFLAT WASHERS NYLON FIBREetc2List of Unclassifed Manufacturers70 MHz Bandp…

windows 7远程桌面和被远程连接电脑启动远程桌面服务

目录远程桌面连接开启远程桌面服务&#xff08;被连电脑&#xff09;配置启动远程桌面服务远程桌面连接 这部分主要讲解如何通过一台windows 电脑的远程桌面程序连接"远程"电脑桌面。前提是被连的那台电脑已开启远程桌面服务。远程桌面服务开启之后&#xff0c;可以…

desktop docker 无法卸载_关于Docker:Docker – 无法移除死容器

我无法删除死容器&#xff0c;它在重新启动Docker服务后再次出现。docker ps -aCONTAINER ID STATUS11667ef16239 Dead然后docker rm -f 11667ef16239然后&#xff0c;当我运行docker ps -a时&#xff0c;没有Docker容器显示。docker ps -aCONTAINER ID …

git 命令详解和Android Studio代码管理工具

目录前言git命令上传项目一、核心操作二、解决冲突三、情景举例四、查看ssh密钥五、克隆远程项目六、回退某个操作七、和远程分支建立关联八、删除文件九、远程仓储十、多人协作十一、分支策略十二、其他Android Studio代码管理控制前言 相比SVN CVS的管理模式&#xff0c;git…