【问题标题】:iOS Test Connectivity to the Server During App Lanuching Takes Too Long应用启动期间 iOS 测试与服务器的连接耗时过长
【发布时间】:2012-07-11 14:23:16
【问题描述】:

我有一个允许用户自动登录的应用。我的老板想要的是,当应用程序启动时,应用程序需要测试与我们服务器的连接(不仅仅是测试是否有 WiFi 或 3G 网络)。我借了苹果的Reachability 样品,它有效。

一个问题是花费的时间太长,尤其是在启动时。我在没有互联网连接的本地无线网络上尝试了它,花了我将近半分钟的时间。由于在 -ViewDidLoad() 中调用了自动登录,因此在那半分钟内,ui 没有加载。花那么长时间是不可接受的。更糟糕的是,如果我的应用加载时间过长,iOS 甚至可能会关闭应用。

此外,我的应用中有很多 Web 服务调用。我知道每个 Web 服务调用都有可能失败,因为即使用户从一个地方到另一个地方走一小段路,iPhone/iPad 也很容易失去或获得连接。我个人不喜欢它,但这是我老板要我做的,我可能必须经常在应用程序中测试连接。

所以我在这里寻找的是一种非常快速(在几秒钟内)检测连接的方法,或者是一种在幕后进行而不影响用户体验的方法。

有人有建议吗?

感谢您花时间阅读我的问题。

【问题讨论】:

  • 可能是服务器问题?我在我的应用程序中使用了类似的可达性代码,它运行良好。当用户单击登录时,我触发了一个 checkServerAvailability 方法,如果它返回 false,则显示一个警报。所有这一切都在一瞬间发生 - 永远不会超过一秒钟......
  • 您是否真的断开了互联网或设备没有任何连接(例如您关闭了设备上的 wifi)?我用于测试的是没有互联网连接的无线路由器。所以设备认为它连接到wifi。如果我运行可达性示例,它将显示 localWiFi 和 TCP/IP 路由都可用。
  • 来自文档:当应用程序发送到网络堆栈的数据包可以离开本地设备时,远程主机被认为是可访问的。可达性并不能保证数据包会真正被主机接收到。

标签: iphone ios ipad networking reachability


【解决方案1】:

在进行测试时使用presentModalViewController 显示中间加载视图,并使用performSelectorInBackground 运行实际测试。然后用performSelectorOnMainThread向主线程发信号:

- (void)viewDidLoad
{
    [super viewDidLoad];

    // Make this a hidden member
    loadingViewController = [LoadingViewController new];
    [self presentModalViewController:loadingViewController animated:NO];

    [self performSelectorInBackground:@selector(testConnectivity) withObject:nil];
}

- (void)testConnectivity
{
    // Do expensive testing stuff

    [self performSelectorOnMainThread:@selector(testCompleted) withObject:nil waitUntilDone:NO];
}

- (void)testCompleted
{
    [loadingViewController dismissViewControllerAnimated:YES];
    loadingViewController = nil;
}

请注意,等待 30 秒等待应用程序启动的整体用户体验有点糟糕,而且在您使用应用程序时连接经常会发生变化,因此即使您在每次启动时都进行测试,它也不太可能是可靠的。但如果这是你老板想要的,我会和你一起受苦。 ;)

【讨论】:

  • 我了解您要执行的操作,并且每当应用程序与服务器(通过 Web 服务)对话时,我都有一个“加载”视图显示。然而,仅仅为了检测是否存在与服务器的连接而阻止用户做任何事情对于我们来说是不可接受的,这需要多长时间。您是对的,即使我可以在登录时或任何时间进行测试,结果也不能保证下一个 Web 服务调用都会通过。我正在寻找一种在不影响用户体验的情况下检测连接的方法,或者我需要一种真正快速检测的方法。谢谢。
  • 抱歉,但@Hampus 完全正确。 performSelectorInBackground: 完全按照它所说的去做,在后台做一些事情而不会阻止用户。您的请求需要很长时间,因为您的服务器有问题或者您同时进行了太多连接。这是处理它的正确方法,您可以在执行请求时显示加载视图或带有预填充字段的登录视图
  • 此外,您真的希望尽可能快地加载应用程序(>30 秒 -> 崩溃)。因此,您需要加载尽可能少的数据。通过此过程,应用程序在 2-3 秒后完成加载,并且您每次都可以做其他事情(例如执行请求或登录)
  • @relikd:一开始我的主视图中没有加载任何自定义数据。如果有互联网连接,唯一的事情就是自动登录。如果我有互联网,那不是问题,而且代码检查速度非常快。它不是服务器。如果我没有互联网连接(即使我连接到本地 WiFi),它总是需要 30 秒才能给我任何东西。这就是我的问题。
  • 我将所有内容都视为自定义数据,而不是 Apple 的东西。如果这 30 秒是您唯一的问题,您可以简单地将请求的超时设置为例如。 10秒甚至5秒
【解决方案2】:

对于它的价值,这是我使用的方法..

- (BOOL)checkServerAvailability {
    bool success = false;
    const char *host_name = [@"host" cStringUsingEncoding:NSASCIIStringEncoding];

    SCNetworkReachabilityRef reachability = 
                  SCNetworkReachabilityCreateWithName(NULL, host_name);
    SCNetworkReachabilityFlags flags;
    success = SCNetworkReachabilityGetFlags(reachability, &flags);
    bool isAvailable = success && (flags & kSCNetworkFlagsReachable) 
                   && !(flags & kSCNetworkFlagsConnectionRequired);

    CFRelease(reachability);
    return isAvailable;
}

这是我的服务器检查方法的记录结果...

2012-07-11 11:29:04.892 ASURecycles[1509:f803] starting server check...
2012-07-11 11:29:04.894 ASURecycles[1509:f803] creating reachability...
2012-07-11 11:29:04.894 ASURecycles[1509:f803] creating flags...
2012-07-11 11:29:04.913 ASURecycles[1509:f803] checking for success of reachability, assigning to flags..
2012-07-11 11:29:04.913 ASURecycles[1509:f803] checking our flags to determine network status...
2012-07-11 11:29:04.913 ASURecycles[1509:f803] not available

如您所见,只有几分之一秒。当然我们的服务器目前正在遇到问题,但我认为您的问题可能是服务器问题,而不是框架问题。不过,您总是可以尝试在另一个线程上执行服务器检查。

【讨论】:

  • 谢谢各位,我尝试了一个更复杂的版本(但想法相同)。我的经验是,当设备连接到 WiFi 时,即使 WiFi 没有有效的互联网连接,该方法仍然会返回 YES。
  • 据我了解,如果能够访问某些服务器,但需要连接,SCNetworkReachability 将返回此:“kSCNetworkReachabilityFlagsConnectionRequired - 可以使用当前访问指定的节点名称或地址网络配置,但必须先建立连接”。这是来自苹果文档的权利。我在我的方法中检查它 - 如果返回 false 并且服务器可访问,我返回是,否则我返回 false。这就是“ !(flags & kSCNetworkFlagsConnectionRequired) ”的用途
  • 但是回答你的问题,我从来没有遇到过这个问题。如果没有连接到服务器,它会提醒我,如果有,我会继续进行身份验证
  • 有意思,我做个快速测试告诉你。
  • 你的方法绝对可以在有WiFi但没有互联网的情况下测试连接。我为此道歉。然而,它就像苹果的 Reachability 样本一样,需要大约 30 秒才能显示结果。
【解决方案3】:

正如我在 cmets 中所说,无论如何您都应该使用 Hampus Nilsson's approach 在后台执行请求。

关于你的 30 秒问题,我在 another blog 找到了这个:

- (BOOL)isHostAvailable:(NSString*)hostName
{
    // this should check the host but does not work in the simulator, aka it returns YES when should be no
    SCNetworkReachabilityRef reachability = SCNetworkReachabilityCreateWithName(NULL, [hostName cStringUsingEncoding:NSASCIIStringEncoding]);
    SCNetworkReachabilityFlags flags;
    BOOL success = SCNetworkReachabilityGetFlags(reachability, &flags);
    if (reachability) {
        CFRelease(reachability);
    }

    if ( ( success && (flags & kSCNetworkFlagsReachable) && !(flags & kSCNetworkFlagsConnectionRequired) ) == NO) {
        return NO;
    }

    // we know at least the network is up, second check for a known page
    NSData *dataReply;
    NSURLResponse *response;
    NSError *error;

    // create the request
    NSString *urlString = [NSString stringWithFormat:@"http://%@/index.php", hostName];
    NSURLRequest *theRequest=[NSURLRequest requestWithURL:[NSURL URLWithString:urlString]
                                            cachePolicy:NSURLRequestReloadIgnoringLocalCacheData
                                        timeoutInterval:8.0];
    // Make the connection
    dataReply = [NSURLConnection sendSynchronousRequest:theRequest returningResponse:&response error:&error];

    if (response != nil) {
        NSLog(@"SNNetworkController.isHostAvailable %@", response);
        return YES;
    } else {
        // inform the user that the download could not be made
        NSLog(@"SNNetworkController.isHostAvailable %@ %@", response, error);
        return NO;
    }
}

这将执行一个超时值为 8 秒的请求。

//编辑:
ASIHTTPRequest 示例:

ASIFormDataRequest *request = [ASIFormDataRequest requestWithURL:url];
[request setNumberOfTimesToRetryOnTimeout:3];
[request setTimeOutSeconds:20.0];
[request setRequestMethod:@"POST"];
[request startAsynchronous];

【讨论】:

  • 你试过这个方法吗?这对我不起作用。完全相同的 30 秒。
  • 还没有尝试过这个特定的,但在我的其他应用程序中使用几乎相同,唯一的区别是我在那里使用了ASIHTTPRequest。您从未说过您使用其他框架,所以我认为留在 iOS SDK 会更好。但如果您开始考虑使用第三方 SDK,请尝试使用AFNetworking 进行服务器通信;)
  • 请您发布您的互联网检查代码吗?如果这只是简单的互联网检测,我不介意使用 ASIHTTPRequest。顺便说一句,这个项目已经停止开发一年多了,他说“请注意我不再在这个库上工作——你可能想考虑为新项目使用其他东西。:)”。
  • 因此我说改用AFNetworking。这是一个较旧的项目,因此我使用了 ASI。我用 sn-p 更新了答案,但你真的应该使用AFNetworking。那个没用过,喜欢的可以去研究一下
  • OK relikd,我下载了 AFNetworking,接下来我该怎么做?请给我看看你的代码好吗?谢谢。
猜你喜欢
  • 2017-12-21
  • 2018-03-18
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多