目錄
- 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物件,關系和代碼如下
HeapByteBuffer extends ByteBuffer
public static ByteBuffer allocate(int capacity) {
if (capacity < 0)
throw new IllegalArgumentException();
return new HeapByteBuffer(capacity, capacity);
}
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);
- 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;
}
- 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] 地址,
- 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;
}
- 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個位元組沒有值,
- 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也沒有持有陣列物件,只通過一個地址進行存取,
轉載請註明出處,本文鏈接:https://www.uj5u.com/yidong/340456.html
標籤:其他
上一篇:App基礎操作API
