OpenUrl 冻结应用程序超过 10 秒

问题描述 投票:0回答:12

我目前正在开发一个App,需要打开浏览器才能显示网页。 为此,我使用带有 url 的

[UIApplication sharedApplication] openURL
方法。

在 iOS 6 中,这工作得很好,但在 iOS 7 中,它会冻结应用程序 10 秒以上,然后打开浏览器,一切都很好。

这是使用临时配置发生的。互联网上有人评论说这是一个已知问题,但是,关于这个问题我能找到的只有一条评论。

ios ios7 openurl
12个回答
95
投票

自 iOS 7 起,我在从应用程序委托 didReceiveRemoteNotification: 或 didFinishLaunchingWithOptions: 调用 -[UIApplication openUrl:] 时注意到同样的问题。

我通过使用 GCD 稍微延迟调用来解决了这个问题:

// objc
dispatch_async(dispatch_get_main_queue(), ^{
    [[UIApplication sharedApplication] openURL:url];
});

它让 iOS 有一些时间来完成应用程序初始化,然后调用就可以毫无问题地执行。别问我为什么。

这对你有用吗?

由于经常看到这个答案,我添加了 swift 版本:

// swift
dispatch_async(dispatch_get_main_queue()) {
    UIApplication.sharedApplication().openURL(url)
}

10
投票

我在 iOS 7 中看到了同样的问题。我的解决方案与已经提出的解决方案仅略有不同。通过使用

performSelector
,仅延迟 0.1 秒,应用程序就会立即打开 URL。

[self performSelector:@selector(methodToRedirectToURL:) withObject:url afterDelay:0.1];

9
投票

与您描述的症状完全相同:在 iOS6 上工作正常,但在 iOS7 上挂起约 10 秒。原来是线程问题。

我们直接从 AppDelegate 方法

[UIApplication sharedApplication] openURL
发出
applicationDidBecomeActive()
。将其移至后台线程立即解决了问题:

- (void)applicationDidBecomeActive:(UIApplication *)application
{
    ...

    // hangs for 10 seconds
    // [[UIApplication sharedApplication] openURL:[NSURL URLWithString: url]];

    // Fix: use threads!
    [NSThread detachNewThreadSelector:@selector(openbrowser_in_background:) toTarget:self withObject:url];

    ...
}

- (void)openbrowser_in_background:(NSString *)url
{
    [[UIApplication sharedApplication] openURL:[NSURL URLWithString: url]];
}

7
投票

感谢上面所有人员的建议,这就是我在 Xamarin.iOS(和 Xamarin.Forms)中解决它的方法。该解决方案的灵感来自于上面讨论的内容,并希望它可以帮助其他面临同样问题但使用 Xamarin 的人。


[Register("AppDelegate")]
public class AppDelegate
{
     ....

 public override bool OpenUrl(UIApplication application, NSUrl url, string sourceApplication, NSObject annotation)
 {
      // We do some logic to respond to launching app, and return to that app. 
      Task.Delay(500).ContinueWith(_ => {
            this.InvokeOnMainThread( () => {
                UIApplication.SharedApplication.OpenUrl(NSUrl.FromString(openUri));
            });
        });
 }

}


5
投票

在做了一些非常快速的基准测试之后,我发现 @lidsinkers 方法显然是最快的。尤其是当我用

 public override bool OpenUrl(UIApplication application, NSUrl url, string sourceApplication, NSObject annotation)
 {
      // We do some logic to respond to launching app, and return to that app. 
      Task.Delay(500).ContinueWith(_ => {
            this.InvokeOnMainThread( () => {
                UIApplication.SharedApplication.OpenUrl(NSUrl.FromString(openUri));
            });
        });
 }
替换
0.1
的延迟时。

因此我决定将其转换为 Swift 代码:

0.001

完整方法:

dispatch_after(dispatch_time(DISPATCH_TIME_NOW, Int64(0.001 * Double(NSEC_PER_SEC))), dispatch_get_main_queue()) {
            UIApplication.sharedApplication().openURL(url)
        }

4
投票

适用于 iOS 9

/// An attempt at solving 'openUrl()' freeze problem
func lidsinkerOpenURL(url: NSURL) {
        dispatch_after(dispatch_time(DISPATCH_TIME_NOW, Int64(0.001 * Double(NSEC_PER_SEC))), dispatch_get_main_queue()) {
            UIApplication.sharedApplication().openURL(url)
        }
    }

这似乎对我有用


1
投票

我发现从 iOS 10 开始使用它会变得更好。

if([[UIApplication sharedApplication] canOpenURL:url]){            
        dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
            [[UIApplication sharedApplication] openURL:url];
        });
    }

0
投票

如果你把“openURL”动作放在viewDidLoad方法中,那么它肯定会执行得很慢。您可以将其放在 viewDidAppear 方法中。或者,您可以在 viewDidLoad 方法中使用 GCD,如下所示:

dispatch_async(dispatch_get_main_queue(), ^{
    if ([[[[UIDevice currentDevice] systemVersion] componentsSeparatedByString:@"."].firstObject integerValue] < 10) {
        [[UIApplication sharedApplication] openURL:[NSURL URLWithString:@"tel:..."]];
    } else {
        [[UIApplication sharedApplication] openURL:[NSURL URLWithString:@"tel:..."] options:@{} completionHandler:^(BOOL success) {

        }];
    }
});

0
投票

这是 Swift 3.0 中的答案,检查我们是否可以打开 URL。

dispatch_async(dispatch_get_main_queue(), ^{
    [[UIApplication sharedApplication] openURL:url];
});

0
投票

带有操作系统版本检查的 Swift 4.1。

guard let url = URL(string: myURLString) else {
    return
}


if UIApplication.shared.canOpenURL(url) {
   DispatchQueue.main.async {
     UIApplication.shared.openURL(url)
   }
}

0
投票

对于 Swift3

DispatchQueue.main.async() {
  if #available(iOS 10.0, *) {
    UIApplication.shared.open(url)
  } else {
    UIApplication.shared.openURL(url)
  }
}

0
投票

Swift 中的所有答案都会将 url 的打开分派到主线程,这仍然会导致问题。一些 Objective-C 解决方案分派到后台任务,这应该是正确的解决方案(主要是打开 Safari 来打开 url 是一项非常昂贵的任务,因此您不想在主线程上执行此操作)。

要在 Swift 中调度到后台线程,你可以这样做:

    DispatchQueue.main.async {
        UIApplication.shared.openURL(url)
    }
© www.soinside.com 2019 - 2024. All rights reserved.