2012-05-15 47 views
2

我已經實現了什麼似乎是使用UIWebView委託shouldStartLoadWithRequest()方法從iOS通信到iOS上的objective-c的唯一方式。iOS從Javascript調用Objective-C:有些調用被忽略?

它似乎一開始工作正常,但現在我注意到,如果我在短時間內從javascript到objective-c多次調用,通常會忽略第二個調用(應用程序是一個鋼琴鍵盤,每個按鍵觸發對本地代碼的調用,當處理多次觸摸時,本地代碼不會被每個手指調用)。

這是我的客觀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,所以如果他們沒有這個問題,那麼我很想弄清楚他們正在做什麼來完成這項工作。

更新:

我剛剛發現這一點:send a notification from javascript in UIWebView to ObjectiveC 這證實了我對臨門迷路的通話懷疑。顯然,我需要將我的電話合併到一起,或者手動延遲電話,以便在再次撥打電話時返回url請求。

+1

問題是第一個window.src集合必須已經完成,然後才能發送另一個,最終因爲runloop是單線程和同步的。除了排隊和/或批處理呼叫之外,沒有辦法解決,直到第一個完成。實現這個完美實際上是相當棘手的,特別是在發送第一個onClick on Mobile Safari之前考慮到延遲的情況下,所以最好的辦法是將源代碼查看到其中一個開源橋接器 - 或者更好的是,只需使用一。 – abarnert

回答

3

接受的答案沒有解決不了的問題,因爲在此之前,首先是處理到達位置的變化依然被忽略。查看第一條評論。

我建議以下方法:

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

您反覆調用execute功能,因爲每次調用在自己的iframe中執行,他們不應該趕緊打電話時忽略。

Credits to this guy

+0

這就是我最終做的,我認爲這是最好的解決方案。感謝你的回答! – phosphoer

1

與在JavaScript端排隊所有內容相比,使用GCD將複雜邏輯(如調用音頻處理程序)移出主線程可能更容易,更快速。您可以使用dispatch_async()排隊您的活動。如果你把它們放到一個串行隊列中,那麼它們肯定會按順序運行,但你會更快地回到JavaScript。例如:

  • 初始化過程中創建你的對象隊列:

    self.queue = dispatch_queue_create( 「玩家」,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]}); 
    } 
    
+3

這使問題變小,但不會消失。該處理程序仍然需要非零時間,並且在該時間內進入的.src/.location的第二個更改仍然不會觸發。所以最終你需要在JS端進行排隊/批處理。 – abarnert

+0

如果您將所有內容都發送到後臺線程並立即返回,您是否發現這在實踐中速度太慢? (沒有測試過;我很好奇) –

+0

我個人?但是,我從來沒有構建過一個應用程序,用戶希望能夠將兩次點擊靠得很近,並注意第二個點擊是否會丟失。但對於他的用例 - 試圖模仿(和絃)鋼琴鍵盤 - 人們可能會有這樣的期望。 – abarnert