通过前面的分析我们了解到
Fresco
中的图片缓存分为3种: 解码图片内存缓存、编码图片内存缓存和磁盘缓存,在Fresco缓存架构分析一文中比较详细的分析了内存缓存和磁盘缓存。本文就来分析一下Fresco编码图片缓存(EncodeMemoryCache)
的实现。
Fresco
从网络获取的其实是图片的字节流,这个字节流的内容就是未解码的图片的数据:
NetworkFetchProducer.java
protected void onResponse(FetchState fetchState, InputStream responseData, int responseContentLength){
final PooledByteBufferOutputStream pooledOutputStream;
...
pooledOutputStream = mPooledByteBufferFactory.newOutputStream(responseContentLength);
final byte[] ioArray = mByteArrayPool.get(READ_SIZE);
int length;
while ((length = responseData.read(ioArray)) >= 0) {
if (length > 0) {
pooledOutputStream.write(ioArray, 0, length);
...
}
}
}
即把从网路获取的未解码的图片数据
写到了PooledByteBufferOutputStream
中。那PooledByteBufferOutputStream
把这些数据写到哪里了呢?在Fresco
中PooledByteBufferOutputStream
的唯一实现是MemoryPooledByteBufferOutputStream
:
MemoryPooledByteBufferOutputStream.java
public class MemoryPooledByteBufferOutputStream extends PooledByteBufferOutputStream {
private final MemoryChunkPool mPool; // the pool to allocate memory chunks from
private CloseableReference<MemoryChunk> mBufRef; // the current chunk that we're writing to
public void write(byte[] buffer, int offset, int count) throws IOException {
realloc(mCount + count);
mBufRef.get().write(mCount, buffer, offset, count);
mCount += count;
}
void realloc(int newLength) {
if (newLength <= mBufRef.get().getSize()) {
return;
}
MemoryChunk newbuf = mPool.get(newLength);
mBufRef.get().copy(0, newbuf, 0, mCount);
mBufRef.close();
mBufRef = CloseableReference.of(newbuf, mPool);
}
}
即MemoryPooledByteBufferOutputStream
实际上是把数据写到了MemoryChunkPool
中。MemoryChunkPool
负责管理MemoryChunk
。一个MemoryChunk
代表一个可用的内存块。所以Fresco
网络下载的图片会保存到MemoryChunk
。MemoryChunk
是一个接口,在Fresco
中一共有两个实现: BufferMemoryChunk
和NativeMemoryChunk
。他们分别代表不同的内存区域。在继续看之前我们先来回顾一下Android中应用内存相关知识:
Android应用内存
在Android中堆(heap)空间完全由程序员控制,堆空间可以细分为java heap
和native heap
。我们使用java方法申请的内存位于java heap
,使用jni通过C/C++申请的内存位于native heap
。Android系统对应用程序java heap
大小做了硬性限制,当java进程申请的java heap
空间超过阈值时,就会抛出OOM异常。不过对于native heap
没有任何限制,只要手机还有内存,那应用程序就可以在native heap
上一直分配空间。
所以对于一些由于内存不足而引发的OOM问题,可以通过在native heap
上分配空间的方式来解决。Fresco
中的EncodeMemoryCache
就是基于native heap
来缓存图片的。
MemoryChunk的分类
BufferMemoryChunk
BufferMemoryChunk.java
public class BufferMemoryChunk implements MemoryChunk, Closeable {
private ByteBuffer mBuffer;
@Override
public synchronized int write(final int memoryOffset, final byte[] byteArray, final int byteArrayOffset, final int count) {
final int actualCount = MemoryChunkUtil.adjustByteCount(memoryOffset, count, mSize);
mBuffer.position(memoryOffset);
mBuffer.put(byteArray, byteArrayOffset, actualCount);
return actualCount;
}
}
即它是基于ByteBuffer
实现。ByteBuffer
是java nio
中的一个类, IO
与NIO
的区别是:
- IO是面向流的,NIO是面向缓冲区的
- Java IO面向流意味着每次从流中读一个或多个字节,直至读取所有字节,它们没有被缓存在任何地方;
- NIO则能前后移动流中的数据,因为是面向缓冲区的
- IO流是阻塞的,NIO流是不阻塞的
- Java IO的各种流是阻塞的。这意味着,当一个线程调用read() 或 write()时,该线程被阻塞,直到有一些数据被读取,或数据完全写入。该线程在此期间不能再干任何事情了
- Java NIO的非阻塞模式,使一个线程从某通道发送请求读取数据,但是它仅能得到目前可用的数据,如果目前没有数据可用时,就什么都不会获取。NIO可让您只使用一个(或几个)单线程管理多个通道(网络连接或文件),但付出的代价是解析数据可能会比从一个阻塞流中读取数据更复杂。 非阻塞写也是如此。一个线程请求写入一些数据到某通道,但不需要等待它完全写入,这个线程同时可以去做别的事情。
所以可以简单的理解:BufferMemoryChunk
把未解码的图片保存在内存中,并可以方便的操作这块内存,当然这块内存位于java heap
上。
NativeMemoryChunk
NativeMemoryChunk.java
public class NativeMemoryChunk implements MemoryChunk, Closeable {
static {
ImagePipelineNativeLoader.load(); //加载了一个so库, 用于分配native内存
}
public NativeMemoryChunk(final int size) {
mSize = size;
mNativePtr = nativeAllocate(mSize); //通过 native so 库 来分配
mIsClosed = false;
}
public synchronized int write(int memoryOffset, byte[] byteArray, int byteArrayOffset, int count) {
final int actualCount = MemoryChunkUtil.adjustByteCount(memoryOffset, count, mSize);
nativeCopyFromByteArray(mNativePtr + memoryOffset, byteArray, byteArrayOffset, actualCount); //把数据字节拷贝到native内存中
return actualCount;
}
}
即NativeMemoryChunk
所管理的内存是通过native方法来分配的:
NativeMemoryChunk.c
static jlong NativeMemoryChunk_nativeAllocate(JNIEnv* env,jclass clzz,jint size) {
void* pointer = malloc(size);
...
return PTR_TO_JLONG(pointer);
}
前面我们已经说了,通过jni c malloc
分配的内存位于应用native内存。所以NativeMemoryChunk
所管理的内存位于native heap
上。
EncodeMemoryCache的大小
在Fresco
中编码图片的内存缓存大小最大是多次呢?
DefaultEncodedMemoryCacheParamsSupplier.java
private int getMaxCacheSize() {
final int maxMemory = (int) Math.min(Runtime.getRuntime().maxMemory(), Integer.MAX_VALUE);
if (maxMemory < 16 * ByteConstants.MB) {
return 1 * ByteConstants.MB;
} else if (maxMemory < 32 * ByteConstants.MB) {
return 2 * ByteConstants.MB;
} else {
return 4 * ByteConstants.MB;
}
}
对于目前大部分手机来说是4MB
编码图片默认的缓存位置
通过前面的分析我们知道编码图片既可以缓存在BufferMemoryChunk
上,也可以缓存在NativeMemoryChunk
,那默认是缓存在哪里呢?
ImagePipelineConfig.java
private static int getMemoryChunkType(Builder builder, ImagePipelineExperiments imagePipelineExperiments) {
if (builder.mMemoryChunkType != null) {
return builder.mMemoryChunkType;
} else if (imagePipelineExperiments.isNativeCodeDisabled()) { //没自定义配置的话这个值为false
return MemoryChunkType.BUFFER_MEMORY;
} else {
return MemoryChunkType.NATIVE_MEMORY;
}
}
其实在我们没有配置的情况下,Fresco
是把编码图片缓存在native memory
的。
OK,本文到这里就大致了解Fresco
编码图片的缓存方式。其实Fresco
编码图片缓存的管理也很复杂,本文就不做分析了。
参考文章:
1、本站所有资源均从互联网上收集整理而来,仅供学习交流之用,因此不包含技术服务请大家谅解!
2、本站不提供任何实质性的付费和支付资源,所有需要积分下载的资源均为网站运营赞助费用或者线下劳务费用!
3、本站所有资源仅用于学习及研究使用,您必须在下载后的24小时内删除所下载资源,切勿用于商业用途,否则由此引发的法律纠纷及连带责任本站和发布者概不承担!
4、本站站内提供的所有可下载资源,本站保证未做任何负面改动(不包含修复bug和完善功能等正面优化或二次开发),但本站不保证资源的准确性、安全性和完整性,用户下载后自行斟酌,我们以交流学习为目的,并不是所有的源码都100%无错或无bug!如有链接无法下载、失效或广告,请联系客服处理!
5、本站资源除标明原创外均来自网络整理,版权归原作者或本站特约原创作者所有,如侵犯到您的合法权益,请立即告知本站,本站将及时予与删除并致以最深的歉意!
6、如果您也有好的资源或教程,您可以投稿发布,成功分享后有站币奖励和额外收入!
7、如果您喜欢该资源,请支持官方正版资源,以得到更好的正版服务!
8、请您认真阅读上述内容,注册本站用户或下载本站资源即您同意上述内容!
原文链接:https://www.dandroid.cn/archives/18961,转载请注明出处。
评论0