iOS从Javascript调用Objective-C:忽略某些调用?
我已经使用UIWebView委托shouldStartLoadWithRequest()方法实现了在iOS上从javascript到objective-c的唯一通信方式 起初它似乎工作得很好,但现在我注意到,如果我在短时间内多次从javascript调用objective-c,第二次调用通常会被忽略(该应用程序是一个钢琴键盘,每个按键都会触发对本机代码的调用,当处理多次触摸时,不会对每个手指调用本机代码) 这是我的objective-c代码,用于响应javascript调用。我知道这很糟糕,但我只是想要一些暂时有效的东西iOS从Javascript调用Objective-C:忽略某些调用?,javascript,objective-c,ios,timing,Javascript,Objective C,Ios,Timing,我已经使用UIWebView委托shouldStartLoadWithRequest()方法实现了在iOS上从javascript到objective-c的唯一通信方式 起初它似乎工作得很好,但现在我注意到,如果我在短时间内多次从javascript调用objective-c,第二次调用通常会被忽略(该应用程序是一个钢琴键盘,每个按键都会触发对本机代码的调用,当处理多次触摸时,不会对每个手指调用本机代码) 这是我的objective-c代码,用于响应javascript调用。我知道这很糟糕,但我
- (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,因此,如果他们没有这个问题,那么我很想弄清楚他们在做什么,使这项工作
更新:
我刚刚发现:
这证实了我对快速连续呼叫丢失的怀疑。显然,我需要将呼叫集中在一起或手动延迟呼叫,以便在我再次呼叫时url请求已返回。移动复杂的url可能比在JavaScript端排队更简单、更快使用GCD将逻辑(如对音频处理程序的调用)从主线程中移除。您可以使用
dispatch\u async()
对事件进行排队。如果将事件放入串行队列,则它们肯定会按顺序运行,但您会更快地返回javascript。例如:
- 在初始化期间为对象创建队列: self.queue=dispatch\u queue\u 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]}); }
- 接受的答案无法解决问题,因为在处理第一个答案之前到达的位置更改仍然会被忽略。请参阅第一条注释
我建议采取以下办法:
function execute(url)
{
var iframe = document.createElement("IFRAME");
iframe.setAttribute("src", url);
document.documentElement.appendChild(iframe);
iframe.parentNode.removeChild(iframe);
iframe = null;
}
重复调用execute
函数,由于每个调用都在其自己的iframe中执行,因此在快速调用时不应忽略它们
问题在于,第一个window.src集必须在发送另一个window.src集之前完成,这最终是因为runloop是单线程和同步的。在第一个window.src集完成之前,除了排队和/或批处理调用之外,没有其他方法可以解决这一问题。实现这一完美实际上相当棘手,尤其是当您在Mobile Safari上发送第一个onClick之前,请考虑延迟,因此您最好将源代码查找到一个开放源代码桥接器,或者更好的是,只使用一个。这会使问题变得更小,但不会使问题消失。处理程序仍然需要非零的时间,并且第二次更改为.src/.location在这段时间内,仍然不会触发。因此,最终您确实需要在JS端排队/批处理。如果您将所有内容发送到后台线程并立即返回,您是否发现实际上这太慢了?(尚未测试此功能;我自己也很好奇。)我个人?没有。但我从来没有开发过这样的应用程序,用户可以在非常近的距离内进行两次点击,并注意到第二次点击是否丢失。但对于他尝试模拟(复调)钢琴键盘的用例,人们可能会有这样的期望。这是有问题的,因为之前我使用的是[m_audioController播放说明]做一些事情。不过这是一个有趣的建议。当它完成时,你会将它发送回javascript。当前的javascript代码阻止了此方法,但请注意你实际上是如何将信息发送回javascript的。你发送信息时没有返回
值,而是通过评估一个完全不同的JavaScri来发送PT。我是说,在后台线程完成之后,您应该能够评估完全不同的JavaScript,并且可以在 BudDistAtgult的中间进行它(可能更好,因为您不想重新进入JS)。。这就是我最后所做的,我认为这是最好的解决方案。谢谢你的回答!这是一个非常好的方法。我动态添加了表单和iframe,它也很好地工作。