Hessian 是个轻量级的跨平台的远程过程调用框架(RPC)。我在目前的项目中使用其 java 服务器端和 iOS 客户端(HessianKit)。客户端调用远程的接口时是以阻塞方式进行的,我把请求远程数据的代码放在后台执行,当其获取结束时通知主线程更新视图。从一开始我就在纠结 HessianKit 如何设置超时时间,比如我希望它 20 秒后如果没能获取成功就取消连接并返回给我一个错误代码。在网上搜了很久,官方文档也端详了几遍,最终证明 HessianKit 的确没有提供这样的功能。
于是初期我只好采用了一个不太优雅的方式,就是设置一个 Timer ,在 20 秒后去设置一个超时标识并提示用户超时,当后台线程访问结束后去判断这个标识,如果已经超时了则不管获取数据是否成功都以失败来处理(不能刚告诉用户超时了,接着又成功跳转了吧)。
目前项目开发已经结束,因此这两天有时间来研究一下 HessianKit 的源代码,以期扩展其代码,加入超时处理的功能,不过还是以失败告终。好吧,前提铺垫到此为止,具体原因请听我娓娓道来。
HessianKit 请求数据相关的代码在 CWDistantHessianObject+Private.m 文件中的 -(NSData*)sendRequestWithPostData:(NSData*)postData 方法里。
-(NSData*)sendRequestWithPostData:(NSData*)postData;
{
NSData* responseData = nil;
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:self.URL
cachePolicy:NSURLRequestReloadIgnoringCacheData timeoutInterval:60.0];
[request setHTTPMethod:@"POST"];
[request setHTTPBody:postData];
// Fool Tomcat 4, fails otherwise...
[request setValue:@"text/xml" forHTTPHeaderField:@"Content-type"];
NSHTTPURLResponse * returnResponse = nil;
NSError* requestError = nil;
responseData = [NSURLConnection sendSynchronousRequest:request
returningResponse:&returnResponse error:&requestError];
NSLog(@"request over");
if (requestError) {
responseData = nil;
[NSException raise:NSInvalidArchiveOperationException format:@"Network error domain:%@ code:%d", [requestError domain], [requestError code]];
} else if (returnResponse != nil) {
if ([returnResponse statusCode] == 200) {
[responseData retain];
} else {
responseData = nil;
[NSException raise:NSInvalidArchiveOperationException format:@"HTTP error %d", [returnResponse statusCode]];
}
} else {
responseData = nil;
[NSException raise:NSInvalidArchiveOperationException format:@"Unknown network error"];
}
return responseData ? [responseData autorelease] : nil;
}
可以看到 HessianKit 用的是 NSMutableURLRequest ,并且在初始化实例时设置了 60 秒超时的初始化参数。那么问题的切入点是不是找到呢?是不是通过修改初始化参数或者单独增加一个方法来设置 NSMutableURLRequest 的超时时间就可以达到我的目的了呢?其实不然。
经我测试,运行时实际超时远不止 60 秒,而是好几分钟。其实是因为在 [request setHTTPBody:postData] 语句执行后超时时间又会被设置为 240 秒,之后再调用 request 的 setTimeoutInterval 方法也不会起作用。
关于该问题的详细描述请参考博客:
NSMutableURLRequest timeout interval 不起作用的原因
那么也就是说没有超时设置的功能罪魁祸首不在 HessianKit ,而是 iOS SDK 的问题,可能是个 bug 也可能是苹果自有考虑。也许 HessianKit 的开发团队也发现了该问题,所以没有设置该功能,而是等开发者自己去实现定时和相应的逻辑。以下为苹果官方文档对 setTimeoutInterval: 和 setHTTPBody: 方法的描述:
可以看到,苹果也未对该特殊情况做说明。
最后还是将超时的问题交给开发者自己用 Timer 去解决吧。希望苹果能在下一版的 SDK 中解决该问题,或者 HessianKit 放弃使用 NSMutableURLRequest。
======================================================================================================================
时隔一年多再来补充这篇博客
======================================================================================================================
今天再一次翻看NSMutableURLRequest 的 API,看到了文档中对早期该接口的一个说明,突然想到了这篇博客。目前的文档版本是iOS 7.1。
下面的截图中红色注解部分可以看到Apple终于在新版的文档中对该接口做了补充,也明确说到:在iOS 6以前,如果请求包含request body,默认的和最小的超时时间是240秒,也就是说如果设置小于240秒也会被置为240秒。总算是证实了当初我的猜想!
参考博客:
http://blog.csdn.net/mirage520/article/details/7871286