求知 文章 文库 Lib 视频 iPerson 课程 认证 咨询 工具 讲座 Modeler   Code  
会员   
 
  
 
 
     
   
分享到
如何改进iOS App的离线使用体验
 

作者:Frank Ableson,发布于2012-10-31,来源:博客园

 

App Store中的App分析

App已经与我们形影不离了,不管在地铁上、公交上还是在会场你总能看到很多人拿出来手机,刷一刷微博,看看新闻。

据不完全统计有近一半的用户在非Wifi环境打开App,以下为一个典型iPhone和Android App(50W+用户)的友盟后台数据:

3G、2G的数据连接往往不稳定(特别在公交或者地铁上),这时打开一些App就会像这样:

当然也会有一些体验很好的App,在离线状态下也能顺畅使用:

甚至提供了离线阅读功能:

如何做?

打开过的文章、下载过的音频、查看过的图片我们都希望Cache到本地,下次不用再向服务器请求。

首先,我们为了最快让用户看到内容,会在ViewDidLoad加载Cache数据,如:

- (void)viewDidLoad {
    [self getArticleList:0 length:SECTION_LENGTH useCacheFirst:YES];
}

然后在viewDidAppear中向服务器请求最新数据,如

- (void)viewDidAppear:(BOOL)animated {
    [super viewDidAppear:animated];
    //...
    [self getArticleList:0 length:SECTION_LENGTH useCacheFirst:NO]
}

当然这里的getArticleList接口有useCacheFirst参数,我们需要网络请求模块能够支持这一点,下面就介绍这些库和工具。(借助一些工具很容易能做到这些,而不用自己造轮子。遵循“凡事都应该最简单,而不过于简陋”的原则,这里整理一下,方便项目中使用)。

1.NSMutableURLRequest

Sample(参考麒麟的文章《iOS开发之缓存(一):内存缓存》来使用NSURLCache):

    NSString *paramURLAsString= @"http://www.baidu.com/";
    if ([paramURLAsString length] == 0){
        NSLog(@"Nil or empty URL is given");
        return;
    }
    NSURLCache *urlCache = [NSURLCache sharedURLCache];
    /* 设置缓存的大小为1M*/
    [urlCache setMemoryCapacity:1*1024*1024];
     //创建一个nsurl
    NSURL *url = [NSURL URLWithString:paramURLAsString];
        //创建一个请求
    NSMutableURLRequest *request =
    [NSMutableURLRequest
     requestWithURL:url
     cachePolicy:NSURLRequestUseProtocolCachePolicy
     timeoutInterval:60.0f];
     //从请求中获取缓存输出
    NSCachedURLResponse *response =
    [urlCache cachedResponseForRequest:request];
    //判断是否有缓存
    if (response != nil){
        NSLog(@"如果有缓存输出,从缓存中获取数据");
        [request setCachePolicy:NSURLRequestReturnCacheDataDontLoad];
    }
    self.connection = nil;
    /* 创建NSURLConnection*/
    NSURLConnection *newConnection =
    [[NSURLConnection alloc] initWithRequest:request
                                    delegate:self
                            startImmediately:YES];
    self.connection = newConnection;
    [newConnection release];

但是NSMutableURLRequest使用起来不够简便,在实际项目中我很少用它,而基本使用ASIHTTPRequest来代替。

2.ASIHTTPRequest

你可以从这里找到它的介绍:http://allseeing-i.com/ASIHTTPRequest/,在5.0/4.0及之前iOS版本,ASIHTTPRequest基本是主力的 HTTP requests library,它本身也是Github中的开源项目,但是从iOS 5.0之后逐渐停止维护了。未来的项目可以使用AFNetworking或者MKNetworkKit代替ASIHTTPRequest。

ASIHTTPRequest的简介如下:

ASIHTTPRequest is an easy to use wrapper around the CFNetwork API that makes some of the more tedious aspects of communicating with web servers easier. It is written in Objective-C and works in both Mac OS X and iPhone applications.

It is suitable performing basic HTTP requests and interacting with REST-based services (GET / POST / PUT / DELETE). The included ASIFormDataRequest subclass makes it easy to submit POST data and files usingmultipart/form-data.

ASIHTTPRequest库API设计的简单易用,并且支持block、queue、gzip等丰富的功能,这是该开源项目如此受欢迎的主要原因。

ASIHTTPRequest库中提供了ASIWebPageRequest组件用于请求网页,并且能把网页中的外部资源一并请求下来,但是我在实际项目中使用后发现有严重Bug,所以不建议使用。

ASIHTTPRequest库的介绍中也提到了它可以支持REST-based service,但是与Restfull API打交道我们往往使用下面介绍的的RestKit。

Sample:

    NSMutableString *requestedUrl = [[NSMutableString alloc] initWithString:self.url];

    //如果优先使用本地数据
    ASICachePolicy policy = _useCacheFirst ? ASIOnlyLoadIfNotCachedCachePolicy 
        : (ASIAskServerIfModifiedCachePolicy | ASIFallbackToCacheIfLoadFailsCachePolicy);
    asiRequest = [ASIHTTPRequest requestWithURL:
             [NSURL URLWithString:[requestedUrl stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding]]];
    [asiRequest setDownloadCache:[ASIDownloadCache sharedCache]];
    [asiRequest setCachePolicy:policy];
    [asiRequest setCacheStoragePolicy:ASICachePermanentlyCacheStoragePolicy];
    // Connection
    if (_connectionType == ConnectionTypeAsynchronously) {
        [asiRequest setDelegate:self];
        [asiRequest startAsynchronous];
        // Tell we're receiving.
        if (!_canceled && [_delegate respondsToSelector:@selector(downloaderDidStart:)])
            [_delegate downloaderDidStart:self];
    }
    else 
    {
        [asiRequest startSynchronous];
        NSError *error = [asiRequest error];
        if (!error) 
        {
            [self requestFinished:asiRequest];
        }
        else 
        {
            [self requestFailed:asiRequest];
        }
    }
    [requestedUrl release];

3.RestKit

官方网站:http://restkit.org/,Github开源项目,与 Restfull API 的 Web服务打交道,这个库非常便捷,它也提供了很完整的Cache机制。

Sample:

+ (void)setCachePolicy:(BOOL)useCacheFirst
{
    RKObjectManager* objectManager = [RKObjectManager sharedManager];
    
    if (useCacheFirst) {
        objectManager.client.cachePolicy = RKRequestCachePolicyEnabled;
        //使用本地Cache,如果没有Cache请求服务器
    }
    else 
    {
        objectManager.client.cachePolicy = RKRequestCachePolicyLoadIfOffline|RKRequestCachePolicyTimeout; 
        //离线或者超时时使用本地Cache
    }
}+ (BOOL)getHomeTimeline:(NSInteger)maxId 
                 length:(NSInteger)length
               delegate:(id<RKObjectLoaderDelegate>)delegate
          useCacheFirst:(BOOL)useCacheFirst
{
    if (delegate == nil)
        return NO;
    
    [iKnowAPI setCachePolicy:useCacheFirst];

    //...

Cache请求只是RestKit最基本的功能,RestKit真正强大的地方在于处理与RESTful web services交互时的相关工作非常简便(https://github.com/RestKit/RestKit/wiki),RestKit还可以Cache data model到Core Data中:

Core Data support. Building on top of the object mapping layer, RestKit provides integration with Apple's Core Data framework. This support allows RestKit to persist remotely loaded objects directly back into a local store, either as a fast local cache or a primary data store that is periodically synced with the cloud. RestKit can populate Core Data associations for you, allowing natural property based traversal of your data model. It also provides a nice API on top of the Core Data primitives that simplifies configuration and querying use cases through an implementation of the Active Record access pattern.

但实际上RKRequestCachePolicy已经解决了大部分Cache需求。

4.SDWebImage

SDWebImage是Github开源项目:https://github.com/rs/SDWebImage,它用于方便的请求、Cache网络图片,并且请求完毕后交由UIImageView显示。

Asynchronous image downloader with cache support with an UIImageView category.

SDWebImage作为UIImageView的一个Category提供的,所以使用起来非常简单:

    // Here we use the new provided setImageWithURL: method to load the web image
    [imageView setImageWithURL:[NSURL URLWithString:@"http://www.domain.com/path/to/image.jpg"]
                   placeholderImage:[UIImage imageNamed:@"placeholder.png"]];

AFNetworking也提供了类似功能(UIImageView+AFNetworking):

UIImageView *imageView = [[UIImageView alloc] initWithFrame:CGRectMake(0.0f, 0.0f, 100.0f, 100.0f)];
[imageView setImageWithURL:[NSURL URLWithString:@"http://i.imgur.com/r4uwx.jpg"] 
placeholderImage:[UIImage imageNamed:@"placeholder-avatar"]];

5.UIWebView中的图片Cache

如果你使用UIWebView来展示内容,在离线情况下如果也想能显示的话需要实现2点:

  • Cache Html页面
  • Cache 图片等元素

使用上面介绍的网络组件来Cache Html页面比较便捷,之后使用webView loadHTMLString即可加载本地Html页面,而Cache图片需要更换NSURLCache公共实例为自定义的NSURLCache(UIWebView使用的即是+[NSURLCache sharedURLCache]):

    //设置使用自定义Cache机制
    LocalSubstitutionCache *cache = [[[LocalSubstitutionCache alloc] init] autorelease];
    [cache setMemoryCapacity:4 * 1024 * 1024];
    [cache setDiskCapacity:10 * 1024 * 1024];
    [NSURLCache setSharedURLCache:cache];

自定义NSURLCache:

#import <Foundation/Foundation.h>
@interface LocalSubstitutionCache : NSURLCache
{
	NSMutableDictionary *cachedResponses;
}
+ (NSString *)pathForURL:(NSURL*)url;
@end

 
分享到
 
 
     


android人机界面指南
Android手机开发(一)
Android手机开发(二)
Android手机开发(三)
Android手机开发(四)
iPhone消息推送机制实现探讨
手机软件测试用例设计实践
手机客户端UI测试分析
手机软件自动化测试研究报告
更多...   


Android高级移动应用程序
Android应用开发
Android系统开发
手机软件测试
嵌入式软件测试
Android软、硬、云整合


领先IT公司 android开发平台最佳实践
北京 Android开发技术进阶
某新能源领域企业 Android开发技术
某航天公司 Android、IOS应用软件开发
阿尔卡特 Linux内核驱动
艾默生 嵌入式软件架构设计
西门子 嵌入式架构设计
更多...