图片缓存的方案

下载

下载的话,我查阅了很多人写的,像SDWebImage,使用的是NSURLConnection,但是我这里准备使用NSURLsession,使用NSURLSessionDataDelegate的代理方法实现下载数据.

说点题外话:我为什么选择NSURLsession二部选择NSURLConnection。因为iOS9之前在做网络连接的时候,我们使用的时NSURLConnection,但是iOS9之后NSURLConnection宣布被弃用了,在2013年的WWDC大会上,苹果就已经设计出NSURLConnection的继任者NSURLSession,他使用起来比NSURLConnection更加简单,更加强大。

在这个过程当中,还会用到GCD与NSOperation来管理下载线程,为什么混合使用呢?我们使用子类化NSOperation来高复抽象我们的下载线程进行抽象化,这样使我们的下载模块更加清晰,在整个不算太复杂的下载过程中,让接口变得简单。GDC我们在下载中局部会使用到,GCD的优点我们都知道,简单,易用,节省代码,使用block让代码变得更加简洁。

基本上使用的东西上面都总结完了,开始进入下载的设计。

线程程管理器

使用子类化自定义NSOperation,这样一个下载就是一条线程,管理这些线程的话,就需要一个下载管理器,我们就是先来构建这个下载管理器。

这个管理器在整个下载模块中起到的就是对线程资源进行管理,起到一个工具的作用,这样的话我们需要把管理器构建成一个单例类,所以这里我们需要先使用单例模式来达到数据共享的目的。

+(instancetype)shareDownloader{

    static LYImageDownloader *lyImageDownloader;
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        lyImageDownloader = [[LYImageDownloader alloc] init];
    });
    return lyImageDownloader;
}

以上就是我们下载管理器的单例。

整个下载的时候,通过阅读开源库,查找资料,发现很多的设计者他们的下载都具备状态监听,这个状态指的就是像下载进度,完成进度,错误信息回调。这些都是下载过程中,我们需要实时知道的东西。

这些信息都准备以block回调的形式展现,具体如下:

/**
 *  无参数block
 */
typedef void(^DownloaderCreateBlock)();

/**
 *  下载回调信息,下载进度Block
 *
 *  @param AlreadyReceiveSize 已经接收大小
 *  @param NotReceiveSize     未接收大小
 */
typedef void(^DownloaderProgressBlock)(NSInteger alreadyReceiveSize,NSInteger expectedContentLength);

/**
 *  下载回调信息,完成下载Block
 *
 *  @param data     data
 *  @param image    图片
 *  @param error    错误信息
 *  @param finished 是否完成
 */
typedef void(^DownloaderCompletedBlock)(NSData *data,UIImage *image,NSError *error,BOOL finished);

在整个下载中,我们还需要有一些配置选项,例如是否允许后台下载,选择队列下载方式,还是栈的下载方式.所以设置了以下的选项。

typedef NS_OPTIONS(NSInteger,DownloaderOptions) {

    //默认下载操作
    DownloaderDefault = 1,

    //允许后台操作
    DownloaderContinueInBackground = 2
};

typedef NS_ENUM(NSInteger,DownloaderOrder){

    //默认下载顺序,先进先出
    DownloaderFIFO,

    //先进后出
    DownloaderLIFO
};

基本的信息构建完成,我考虑的就是需要将这些状态的回调信息存在一个NSMutableDictionary中,key值就是我们的下载地址,value就是NSMutableArray,里面包含所DownloaderProgressBlock,DownloaderCompletedBlock进度信息。

定义了一下属性:

/**
 *  将所有的下载回调信息存储在这里,Key是URL,Value是多组回调信息
 */
@property(strong,nonatomic) NSMutableDictionary *downloaderCallBack;
在一个下载开始之前,需要加载,或者是删除一些状态信息,构建了以下的函数。

/**
 *  添加回调信息
 *
 *  @param progressBlock         DownloaderProgressBlock
 *  @param completedBlock        DownloaderCompletedBlock
 *  @param url                   url
 *  @param DownloaderCreateBlock DownloaderCreateBlock
 */
-(void)addWithDownloaderProgressBlock:(DownloaderProgressBlock)progressBlock DownloaderCompletedBlock:(DownloaderCompletedBlock)completedBlock URL:(NSURL *)url DownloaderCreateBlock:(DownloaderCreateBlock)downloaderCreateBlock{

    /**
     *  判断url是否为空
     */
    if ([url isEqual:nil]) {
        completedBlock(nil,nil,nil,NO);
    }

    /**
     *  设置屏障,保证在同一时间,只有一个线程可以操作downloaderCallBack属性,保证在并行多个处理的时候,对downloaderCallBack属性的读写操作保持一致
     */
    dispatch_barrier_sync(self.concurrentQueue, ^{

        BOOL firstDownload = NO;
        /**
         *  添加回调信息,处理同一个url信息。
         */
        if(!self.downloaderCallBack[url]){
            self.downloaderCallBack[url] = [NSMutableArray new];
            firstDownload = YES;
        }

        NSMutableArray *callBacksArray = self.downloaderCallBack[url];
        NSMutableDictionary *callBacks = [[NSMutableDictionary alloc] init];
        if (progressBlock) {
            callBacks[@"progress"] = [progressBlock copy];
        }
        if (completedBlock) {
            callBacks[@"completed"] = [completedBlock copy];
        }
        [callBacksArray addObject:callBacks];
        self.downloaderCallBack[url] = callBacksArray;

        if (firstDownload) {
            downloaderCreateBlock();
        }
    });
}

首先就是判断当前的url是否为空,如果为空,直接回调空处理。不为空的话,为了防止同一URL的value被重复创建,我们需要在这里判断下原来是否存在,是否为第一次下载,是第一下下载的话,这里我们会触发adownloaderCreateBlock()的回调来进行operation的配置,当然如果不是第一次,我就仅仅需要把这个新的DownloaderProgressBlock,DownloaderCompletedBlock放进callBacksArray中即可。

这里为了保证downloaderCallBack的线程安全性,我们加了一个屏障,来保证每次只有一个线程操作downloaderCallBack属性。

这么做的一个好处就是,我每一个下载,我判断一下是不是同一URL,是的话我就做伪下载,就是感觉上下载,但是不下载,然后已经正在下载进度会同时反馈给当前其他相同的下载请求。

整个下载管理器,我们需要将下载在一个模块中被管理。就像下面这样

/**
 *  下载管理器对于下载请求的管理
 *
 *  @param progressBlock  DownloaderProgressBlock
 *  @param completedBlock DownloaderCompletedBlock
 *  @param url            url
 */
-(void)downloaderImageWithDownloaderWithURL:(NSURL *)url DownloaderProgressBlock:(DownloaderProgressBlock)progressBlock DownloaderCompletedBlock:(DownloaderCompletedBlock)completedBlock{

    __weak __typeof(self)myself = self;
    __block LYDownloaderOperation *operation;

    [self addWithDownloaderProgressBlock:progressBlock DownloaderCompletedBlock:completedBlock URL:url DownloaderCreateBlock:^{

        NSMutableURLRequest *request = [[NSMutableURLRequest alloc] initWithURL:url cachePolicy: NSURLRequestReloadIgnoringLocalCacheData timeoutInterval:20];

        operation = [[LYDownloaderOperation alloc] initWithRequest:request
                                                 DownloaderOptions:1
                                           DownloaderProgressBlock:^(NSInteger alreadyReceiveSize,NSInteger expectedContentLength){

                                               __block NSArray *urlCallBacks;

                                               dispatch_sync(self.concurrentQueue, ^{
                                                   urlCallBacks = [myself.downloaderCallBack[url] copy];
                                               });
                                               for (NSDictionary *callbacks in urlCallBacks) {
                                                   dispatch_async(dispatch_get_main_queue(), ^{
                                                       DownloaderProgressBlock progress = callbacks[@"progress"];
                                                       if (progress) {
                                                           progress(alreadyReceiveSize,expectedContentLength);
                                                       }
                                                   });
                                               }
                                           }
                                          DownloaderCompletedBlock:^(NSData *data,UIImage *image,NSError *error,BOOL finished){
                                              completedBlock(data,image,error,finished);
                                          }
                                                         cancelled:^{

                                                         }];
        [myself.downloadQueue addOperation:operation];

    }];
}

这部分主要就是在配置我们的operation,将配置完成后的operation添加到下载队列。

/**
 *  下载队列
 */
@property(strong,nonatomic) NSOperationQueue *downloadQueue;
[myself.downloadQueue addOperation:operation];
在这里:

DownloaderProgressBlock:^(NSInteger alreadyReceiveSize,NSInteger expectedContentLength){

                                               __block NSArray *urlCallBacks;

                                               dispatch_sync(self.concurrentQueue, ^{
                                                   urlCallBacks = [myself.downloaderCallBack[url] copy];
                                               });
                                               for (NSDictionary *callbacks in urlCallBacks) {
                                                   dispatch_async(dispatch_get_main_queue(), ^{
                                                       DownloaderProgressBlock progress = callbacks[@"progress"];
                                                       if (progress) {
                                                           progress(alreadyReceiveSize,expectedContentLength);
                                                       }
                                                   });
                                               }
                                           }

就是进度的回调通知,在这里可以知道,如果我们使用了GCD,保证通知对象加载完整后在进行通知

dispatch_sync(self.concurrentQueue, ^{
                                    urlCallBacks = [myself.downloaderCallBack[url] copy];
                                               });

这里使用同步保证了我们进度被通知对象的完整性。

接下来的话就是异步回调通知了,下面的其他地方的基本结构也都做了类似的处理。主要就是保证每一条下载线程,每一条通知都安全的进行着。在完成下载的时候移除对应url的状态,这里也是为了保证downloaderCallBack的线程安全性,我们加了一个屏障,来保证每次只有一个线程操作downloaderCallBack属性。

子类化NSOperation

这里开始就是做下载处理了。需要做的就是重写start方法,在这里创建并且配置NSURLSession对象。

-(void)start{
    NSLog(@"start");
    /**
     * 创建NSURLSessionConfiguration类的对象, 这个对象被用于创建NSURLSession类的对象.
     */
    NSURLSessionConfiguration *configura = [NSURLSessionConfiguration defaultSessionConfiguration];

    /**
     * 2. 创建NSURLSession的对象.
     * 参数一 : NSURLSessionConfiguration类的对象.(第1步创建的对象.)
     * 参数二 : session的代理人. 如果为nil, 系统将会提供一个代理人.
     * 参数三 : 一个队列, 代理方法在这个队列中执行. 如果为nil, 系统会自动创建一系列的队列.
     * 注: 只能通过这个方法给session设置代理人, 因为在NSURLSession中delegate属性是只读的.
     */
    NSURLSession *session = [NSURLSession sessionWithConfiguration:configura delegate:self delegateQueue:nil];

    /**
     *  创建request
     */
    NSMutableURLRequest *request = self.request;

    /**
     *  创建数据类型任务
     */
    NSURLSessionDataTask *dataTask = [session dataTaskWithRequest:request];

    /**
     *  开始任务
     */
    [dataTask resume];

    /**
     *  在session中的所有任务都完成之后, 使session失效.
     */
    [session finishTasksAndInvalidate];

}

因为我们实现了NSURLSessionDataDelegate协议,所以可以自定义一些操作。

//最先调用,在这里做一些数据的初始化。
-(void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask didReceiveResponse:(NSURLResponse *)response completionHandler:(void (^)(NSURLSessionResponseDisposition))completionHandler{
    NSLog(@"开始");
    self.imageData = [[NSMutableData alloc] init];
    self.expectedContentLength = response.expectedContentLength;

    if (self.progressBlock) {
        self.progressBlock(0,self.expectedContentLength);
    }

    completionHandler(NSURLSessionResponseAllow);

}

//下载响应
- (void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask
    didReceiveData:(NSData *)data{

    [self.imageData appendData:data];
    if (self.progressBlock) {
        self.progressBlock(self.imageData.length,self.expectedContentLength);
    }

}

//下载完成后调用
-(void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didCompleteWithError:(NSError *)error{

    self.completedBlock(self.imageData,nil,error,YES);
    [self cancel];
}

重写了以上的一些实现。

最后强调下这里:

NSMutableURLRequest *request = [[NSMutableURLRequest alloc] initWithURL:url cachePolicy: NSURLRequestReloadIgnoringLocalCacheData timeoutInterval:30];

为了避免潜在的重复缓存,NSURLCache与自己的缓存方案,则禁用图片请求的缓存操作。

基本上我们就可以进行测试了:

写了一个简单UI,用来展示下载部分

缓存

缓存这个地方需要考虑的东西还是很多的,那么将会针对一下问题进行描述,设计。

  • 缓存在内存,或者是磁盘,或者是内存+磁盘
  • 缓存是否成功
  • 缓存的容器
  • 存储图片方式
  • 查询图片
  • 清理图片(完全清理,清理部分,其中清理部分图片的清理方式为按时间,安空间的大小)

直接上代码:

/**
 *  进行缓存
 *
 *  @param memoryCache  内存
 *  @param image        图片
 *  @param imageData    图片data
 *  @param urlKey       key值就用来唯一标记数据
 *  @param isSaveTOdisk 是否进行沙箱缓存
 */
-(void)saveImageWithMemoryCache:(NSCache *)memoryCache image:(UIImage *)image imageData:(NSData *)imageData urlKey:(NSString *)urlKey isSaveToDisk:(BOOL)isSaveToDisk{

    //内存缓存
    if ([memoryCache isEqual:nil]) {
        [self.memoryCache setObject:image forKey:urlKey];

    }else{
        [memoryCache setObject:image forKey:urlKey];
    }

    //磁盘缓存
    if (isSaveToDisk) {
        dispatch_sync(self.ioSerialQueue, ^{
            if (![_fileMange fileExistsAtPath:_diskCachePath]) {
                [_fileMange createDirectoryAtPath:_diskCachePath withIntermediateDirectories:YES attributes:nil error:nil];
            }
            NSString *pathForKey = [self defaultCachePathForKey:urlKey];

            NSLog(@"%@",pathForKey);

            [_fileMange createFileAtPath:pathForKey contents:imageData attributes:nil];
        });
    }
}

注释中基本上就描述了各部分的职责,首先就是内存缓存,这里的内存缓存我用NSCache进行处理,这里用NSCache就是其实就是一个集合类型,这个集合类型维护这一个key-value结构。当某一些对象销毁的代价,或者重新生成的代价高于我们内存保留。那么我们就把它内存缓存下来就是很值的。因此重用这些对象是很值得的,毕竟我们不需要二次计算了,并且当我们的内存警报的时候,他自己会丢弃掉一些没用的东西的。就像代码中标记的,就是做了内存缓存工作。

磁盘缓存使用NSFileManager实现,存放的位置就是沙箱的Cache文件夹内,这样就可以了。并且我们可以看到,这里我们是可以根据isSaveToDisk来判断是否需要进行磁盘的缓存,因为有一些东西是不需要缓存在磁盘中的,另外,异步操作也是很关键的一个地方,同样我们在这里使用dispatch_sync来做一些处理,实现我们的异步操作。并且这里的文件名实使用是将URL变换为MD5值。保证了唯一性。

缓存的操作基本上就完成了,既然能存,就需要对应查询。

//查询图片
-(void)selectImageWithKey:(NSString *)urlKey completedBlock:(CompletedBlock)completed{
    UIImage *image = [self.memoryCache objectForKey:urlKey];
    if ([image isEqual:nil]) {
        NSLog(@"ok");
        completed(image,nil,ImageCacheTypeMemory);

    }else{

        NSString *pathForKey = [self defaultCachePathForKey:urlKey];

        NSLog(@"%@",pathForKey);

        NSData *imageData = [NSData dataWithContentsOfFile:pathForKey];
        UIImage *diskImage = [UIImage imageWithData:imageData];
        completed(diskImage,nil,ImageCacheTypeDisk);
    }
}

这里的查询基本上就是两种方式,第一种如果内存中存在,那么就在内存中读取就可以了。当然也存在着内存中不存在的可能性,这样就需要从磁盘中开始读取信息数据。根据MD5值进行索引,然后block回调给上层数据信息进行处理。

最后就是删除操作,因为如果我们设置了磁盘的上限,当我们设定的磁盘空间达到上限的时候该怎么做?当我们想清空所有缓存的时候,我们该怎么做呢?下面的这两段代码就是为了做清理磁盘空间的事情的。

/**
 *  清空全部
 *
 *  @param completion completion
 */
- (void)clearDiskOnCompletion:(NoParamsBlock)completion
{
    dispatch_async(self.ioSerialQueue, ^{
        [_fileMange removeItemAtPath:self.diskCachePath error:nil];
        [_fileMange createDirectoryAtPath:self.diskCachePath
              withIntermediateDirectories:YES
                               attributes:nil
                                    error:NULL];
        if (completion) {
            dispatch_async(dispatch_get_main_queue(), ^{
                completion();
            });
        }
    });
}

这段代码的作用就是为了做清空磁盘的作用,同样是使用NSFileManager来实现。

/**
 *  按条件进行清空(主要是时间),这里盗用了SDWebImage的设计
 *
 *  @param noParamsBlock completion
 */
-(void)clearDiskWithNoParamsBlock:(NoParamsBlock)noParamsBlock{

    dispatch_async(self.ioSerialQueue, ^{

        NSURL *diskCache = [NSURL fileURLWithPath:self.diskCachePath isDirectory:YES];
        NSArray *resourcKeys = @[NSURLIsDirectoryKey,NSURLContentModificationDateKey, NSURLTotalFileAllocatedSizeKey];

        // 1. 该枚举器预先获取缓存文件的有用的属性
        NSDirectoryEnumerator *fileEnumerator = [_fileMange enumeratorAtURL:diskCache
                                                 includingPropertiesForKeys:resourcKeys
                                                                    options:NSDirectoryEnumerationSkipsHiddenFiles
                                                               errorHandler:NULL];

        NSDate *expirationDate = [NSDate dateWithTimeIntervalSinceNow:-60 * 60 * 24 * 7];
        NSMutableDictionary *cacheFiles = [NSMutableDictionary dictionary];
        NSInteger currentCacheSize = 0;

        NSMutableArray *urlsToDelete = [[NSMutableArray alloc] init];

        for (NSURL *fileURL in fileEnumerator) {
            NSDictionary *resourceValues = [fileURL resourceValuesForKeys:resourcKeys error:NULL];

            // 3. 跳过文件夹
            if ([resourceValues[NSURLIsDirectoryKey] boolValue]) {
                continue;
            }

            NSDate *modificationDate = resourceValues[NSURLContentModificationDateKey];
            if ([[modificationDate laterDate:expirationDate] isEqualToDate:expirationDate]) {
                [urlsToDelete addObject:fileURL];

                continue;
            }

            // 5. 存储文件的引用并计算所有文件的总大小,以备后用
            NSNumber *totalAllocatedSize = resourceValues[NSURLTotalFileAllocatedSizeKey];
            currentCacheSize += [totalAllocatedSize unsignedIntegerValue];
            [cacheFiles setObject:resourceValues forKey:fileURL];

        }

        for (NSURL *fileURL in urlsToDelete) {
            [self.fileMange removeItemAtURL:fileURL error:NULL];
        }

        if (self.maxCacheSize > 0 && currentCacheSize > self.maxCacheSize) {
            const NSUInteger desiredCacheSize = self.maxCacheSize / 2;

            // Sort the remaining cache files by their last modification time (oldest first).
            NSArray *sortedFiles = [cacheFiles keysSortedByValueWithOptions:NSSortConcurrent
                                                            usingComparator:^NSComparisonResult(id obj1, id obj2) {
                                                                return [obj1[NSURLContentModificationDateKey] compare:obj2[NSURLContentModificationDateKey]];
                                                            }];

            // Delete files until we fall below our desired cache size.
            for (NSURL *fileURL in sortedFiles) {
                if ([_fileMange removeItemAtURL:fileURL error:nil]) {
                    NSDictionary *resourceValues = cacheFiles[fileURL];
                    NSNumber *totalAllocatedSize = resourceValues[NSURLTotalFileAllocatedSizeKey];
                    currentCacheSize -= [totalAllocatedSize unsignedIntegerValue];

                    if (currentCacheSize < desiredCacheSize) {
                        break;
                    }
                }
            }
        }
        if (noParamsBlock) {
            dispatch_async(dispatch_get_main_queue(), ^{
                noParamsBlock();
            });
        }

    });

}

这段代码就是实现了部分的清理工作,清理的工作就是根据我们设定的一些参数来实现的,这里包含着我们设定的缓存有效期,缓存的最大空间是多少。过了我们设定的有效期,这个时候我们就需要去清理掉这部分内容。另外如果如果所有缓存文件的总大小超过这一大小,则会按照文件最后修改时间的逆序,以每次一半的递归来移除那些过早的文件,直到缓存的实际大小小于我们设置的最大使用空间

到了这里我们的缓存的方案就基本上完成了,我们做一个小小的测试,从本地读取一个文件,然后缓存到cache文件加重,然后进行加载,看一下效果。来看一下测试代码:

- (IBAction)read:(id)sender {

    LDImageCache *l = [LDImageCache shareLDImageCache];

    UIImage *myImage = [UIImage imageNamed:@"author.jpg"];
    NSData *data = UIImagePNGRepresentation(myImage);
    [l saveImageWithMemoryCache:nil image:myImage imageData:data urlKey:@"lastdays.cn" isSaveToDisk:YES];

    [l selectImageWithKey:@"lastdays.cn"
           completedBlock:^(UIImage *image,NSError *error,ImageCacheType type){
               NSLog(@"%ld",(long)type);
               [self.image setImage:image];
           }];

//    [l clearDiskOnCompletion:^{
//        NSLog(@"完成清空");
//    }];

}

这里的读取数据,首先我们从本地读取一个图片,然后调用saveImageWithMemoryCache:将图片数据缓存在内存和磁盘中。然后根据key值调用selectImageWithKey进行查询,这里我们输出的ImageCacheType数据,查看一下都是从哪里进行的读取。我们会优先从内存中进行读取数据。

时间: 2024-12-09 16:21:56

图片缓存的方案的相关文章

移动应用开发(IOS/android等)中一个通用的图片缓存方案讲解(附流程图)

在移动应用开发中,我们经常会遇到从网络请求图片到设备上展示的场景. 如果每次都重复发起请求,浪费流量.浪费电量,用户体验也不佳: 将图片持久化到磁盘也不失为一种策略:但每次从文件读取图片也存在一定的io开销,就算采用此策略,我们也需要控制磁盘缓存的容量,以免占用过多系统资源. 其实没有一个方案可以说是完美的方案,只有最适合自己业务需求的方案,才可以说是一个好方案. 我们下面所讲解的方案具备很强的通用性,设计思路简单而清晰: 1.假设每个网络图片的url具有唯一性,如果网络上的图片变化了,会引起输

Windows Phone App基于文件系统的图片缓存方案

最近在做一个Windows Phone 8.1的应用,因为应用里面使用了很多图片,所以打算将图片文件缓存到文件系统里面,以便节省流量和提升体验. 一般显示图片的做法大多只需要将对应的Uri地址绑定到对应控件的ImageSource属性上即可,或者将Uri传入BitmapImage对象,其会自动将资源异步下载好然后加载. 为了不将缓存的逻辑侵入实体模型层,所以打算在Uri->BitmapImage绑定上做文章,也就是利用IValueConverter接口构造值转换器. 这里有一个问题就是,因为绑定

【WP8】图片缓存控件

在做图片相关的应用的时候,经常需要用大图片的缓存,默认的Image控件不支持缓存的支持,本文自定义一个支持图片缓存的控件 当图片的地址是网络图片时候 根据Url判断该图片是否存在本地,如果存在,则直接从本地读取,如果不存在,则通过Http请求下载该图片,保存到本地,然后读取到Image控件中 当图片为本地地址的时候,直接从本地读取,设置到Image控件中 1.在定义可缓存图片控件之前,先封装一下文件存储的帮助类 using System; using System.IO; using Syste

Android探索之图片缓存&lt;初识Glide&gt;(三)

前言: 前面总结学习了图片的使用以及Lru算法,今天来学习一下比较优秀的图片缓存开源框架.技术本身就要不断的更迭,从最初的自己使用SoftReference实现自己的图片缓存,到后来做电商项目自己的实现方案不能满足项目的需求改用Afinal,由于Afinal不再维护而选择了师出同门的Xutils,中间也接触过别的开源框架比如Picasso,对Picasso的第一次印象就不太好,初次接触是拿到了公司刚从外包公司接手过来的图片社交类app,对内存占用太大,直接感受就是导致ListView滑动有那么一

用nginx图片缓存服务器

用nginx图片缓存服务器 图片的存储硬件 把图片存储到什么介质上? 如果有足够的资金购买专用的图片服务器硬件或者 NAS 设备,那么简单的很: 如果上述条件不具备,只想在普通的硬盘上存储,首先还是要考虑一下物理硬盘的实际处理能力.是 7200 转的还是 15000 转的,实际表现差别就很大.是选择 ReiserFS 还是 Ext3 ,怎么也要测试一下吧? 创建文件系统的时候 Inode 问题也要加以考虑,选择合适大小的 inode size ,在空间和速度上做取舍,同时防患于未然,注意单个文件

android 加载图片oom若干方案小结

本文根据网上提供的一些技术方案加上自己实际开发中遇到的情况小结. 众所周知,每个Android应用程序在运行时都有一定的内存限制,限制大小一般为16MB或24MB(视手机而定).一般我们可以通过获取当前线程的可运行内存来判断,比如系统分给当前运行内存只有16M,而你的图片就有16M,这肯定会oom的. 相关知识介绍 1.颜色模型 常见的颜色模型有RGB.YUV.CMYK等,在大多数图像API中采用的都是RGB模型,Android也是如此:另外,在Android中还有包含透明度Alpha的颜色模型

android之图片缓存的探究

在用户界面(UI)加载一张图片时很简单,然而,如果你需要加载多张较大的图像,事情就会变得更加复杂,.在许多情况下(如与像的ListView GridView或ViewPager的组件),屏幕上的图片的总数伴随屏幕上滚动的骤然增加,且基本上是无限的.为使内存使用保持在稳定范围内,这些组件会在子view在屏幕中消失后,对其进行资源回收,垃圾回收机制会释放掉已加载的图片内存空间,所以建议你不要保持图片的常引用,这样做很好,为了保证页面的流畅性和响应速度,你可能不愿意在页面返回时频繁处理加载过图片.通过

android图片缓存框架Android-Universal-Image-Loader

最近跟同学们做了一个创业项目,其实跟以前做项目不同,以前大多数都是做web网站,对内存管理这些不太关注,因为是pc机,做android的话也就是一些小列子,现在到了做产品阶段吧,只能把项目做到最优.不扯了,先来说这个框架是做什么的,Android-Universal-Image-Loader主要是一个图片的缓存框架,根据官方解释就是,它提供了一个异步处理图片的方案.它提供两种获取图片方式async or sync,并且提供了一个可定制化的组件(thread executors, download

瀑布流照片墙与图片缓存的完美结合

传统界面的布局方式总是行列分明.坐落有序的,这种布局已是司空见惯,在不知不觉中大家都已经对它产生了审美疲劳.这个时候瀑布流布局的出现,就给人带来了耳目一新的感觉,这种布局虽然看上去貌似毫无规律,但是却有一种说不上来的美感,以至于涌现出了大批的网站和应用纷纷使用这种新颖的布局来设计界面. 记得我在之前已经写过一篇关于如何在Android上实现照片墙功能的文章了,但那个时候是使用的GridView来进行布局的,这种布局方式只适用于“墙”上的每张图片大小都相同的情况,如果图片的大小参差不齐,在Grid