2

我已经使用 UIWebView 委托 shouldStartLoadWithRequest() 方法在 iOS 上实现了从 javascript 到 Objective-c 的唯一通信方式。

起初它似乎工作正常,但现在我注意到如果我在短时间内从javascript多次调用objective-c,第二次调用通常会被忽略(应用程序是钢琴键盘,每次按键都会触发一次调用对于本机代码,在处理多次触摸时,不会为每个手指调用本机代码)。

这是我响应 javascript 调用的 Objective-C 代码。我知道这很愚蠢,但我只是想要一些现在可以使用的东西。

- (BOOL)webView:(UIWebView *)webView2 shouldStartLoadWithRequest:(NSURLRequest *)request 
        navigationType:(UIWebViewNavigationType)navigationType 
{  
  // Intercept custom location change, URL begins with "js-call:"
  NSString * requestString = [[request URL] absoluteString];
  if ([requestString hasPrefix:@"js-call:"]) 
  {
    // Extract the selector name from the URL
    NSArray * components = [requestString componentsSeparatedByString:@":"];
    NSString * functionCall = [components objectAtIndex:1];
    NSArray * params = [functionCall componentsSeparatedByString:@"%20"];
    NSString * functionName = [params objectAtIndex:0];

    // Parse playnote event
    if ([functionName isEqualToString:@"playNote"])
    {
      NSString * param = [params objectAtIndex:1];
      NoteInstanceID note_id = [m_audioController playNote:[param intValue]];
      NSString * jscall = [NSString stringWithFormat:@"document.PlayNoteCallback(%i);", note_id];
      NSLog(@"playNote: %i", (int)note_id);
      [m_webView stringByEvaluatingJavaScriptFromString:jscall];
    }

    // Parse stopnote event
    if ([functionName isEqualToString:@"stopNote"])
    {
      NSString * param = [params objectAtIndex:1];
      NoteInstanceID note_id = [param intValue];
      NSLog(@"stopNote: %i", (int)note_id); 
      [m_audioController stopNote:note_id];
    }

    // Parse log event
    if ([functionName isEqualToString:@"debugLog"])
    {
      NSString * str = [requestString stringByReplacingOccurrencesOfString:@"%20" withString:@" "];
      NSLog(@"%s", [str cStringUsingEncoding:NSStringEncodingConversionAllowLossy]);
    }

    // Cancel the location change
    return NO;
  }

  // Accept this location change
  return YES;

}

从javascript我通过设置单个隐藏iframe的src属性来调用objective-c方法。这将触发 Objective-c 中的委托方法,然后将调用所需的本机代码。

$("#app_handle").attr("src", "js-call:playNote " + key.data("pitch"));

app_handle 是相关 iframe 的 id。

综上所述,我的方法基本可以,但是短时间内多次调用是不行的。这只是我们被迫从javascript到objective-c通信的可怕方法的产物吗?还是我做错了什么?我知道 PhoneGap 做了类似的事情来实现同样的目标。我宁愿不使用PhoneGap,所以如果他们没有这个问题,那么我很想弄清楚他们正在做什么来完成这项工作。

更新:

我刚刚发现:从 UIWebView 中的 javascript 向 ObjectiveC 发送通知, 这证实了我对快速连续调用丢失的怀疑。显然,我需要将我的呼叫集中在一起或手动延迟呼叫,以便在我拨打另一个呼叫时返回 url 请求。

4

2 回答 2

4

接受的答案并不能解决问题,因为在处理第一个之前到达的位置更改仍然被忽略。见第一条评论。

我建议采用以下方法:

function execute(url) 
{
  var iframe = document.createElement("IFRAME");
  iframe.setAttribute("src", url);
  document.documentElement.appendChild(iframe);
  iframe.parentNode.removeChild(iframe);
  iframe = null;
}

execute重复调用该函数,并且由于每个调用都在其自己的 iframe 中执行,因此在快速调用时不应忽略它们。

归功于这个家伙

于 2012-11-01T11:45:39.043 回答
1

与其在 JavaScript 端对所有内容进行排队,不如使用 GCD 将复杂的逻辑(如对音频处理程序的调用)移出主线程可能更容易、更快捷。您可以使用dispatch_async()来排队您的活动。如果您将它们放入串行队列,那么它们肯定会按顺序运行,但您会更快地返回到 javascript。例如:

  • 在初始化期间为您的对象创建一个队列:

    self.queue = dispatch_queue_create("player", NULL);

  • 在您的回调中:

    if ([functionName isEqualToString:@"stopNote"])
    {
      NSString * param = [params objectAtIndex:1];
      NoteInstanceID note_id = [param intValue];
      NSLog(@"stopNote: %i", (int)note_id); 
      dispatch_async(self.queue, ^{[m_audioController stopNote:note_id]});
    }
    
于 2012-05-15T22:29:32.450 回答