2010-07-21 92 views

回答

16

使用addJavascriptInterface()將Java對象添加到Javascript環境。讓您的Javascript調用該Java對象的方法來提供其「返回值」。

+0

我怎麼能在像http:// stackoverflow這樣的情況下做到這一點。com/questions/5521191 /返回值 - 從-javascript-function-in-android – vnshetty 2011-04-02 04:59:08

+0

如果您的js不寫入此對象並且您無法觸摸js,則這並不起作用。 – 2014-02-04 09:14:42

+6

@PacMani:使用'loadUrl(「javascript:...」)'(API Level 1-18)或'evaluateJavascript()'(API Level 19+)在當前加載的Web上下文中評估自己的JavaScript頁。 – CommonsWare 2014-02-04 12:34:04

59

這裏是你如何能做到這一個黑客:在onJsAlert

webView.loadUrl("javascript:alert(functionThatReturnsSomething)"); 

現在:

此客戶端添加到您的WebView:

final class MyWebChromeClient extends WebChromeClient { 
     @Override 
     public boolean onJsAlert(WebView view, String url, String message, JsResult result) { 
      Log.d("LogTag", message); 
      result.confirm(); 
      return true; 
     } 
    } 
在javascript調用

現在做調用「消息」將包含返回的值。

+15

這是一個非常有價值的破解;特別是當你正在構建一個應用程序時,你無法控制javascript並且不得不使用現有的功能。注意你可以使用'public boolean onConsoleMessage(ConsoleMessage consoleMessage)'而不是'onJsAlert'來攔截JS警報,然後在javascript調用中執行'webView.loadUrl(「javascript:console.log(functionThatReturnsSomething) 「);' - 這樣你就可以獨自留下JS警報。 – 2012-07-09 03:46:46

+0

「WebChromeClient.onConsoleMessage」的解決方案似乎更清晰,但請注意,它不適用於某些HTC設備。看到[這個問題](http://stackoverflow.com/questions/4860021/android-problem-with-debugging-javascript-in-webview)瞭解更多信息。 – Piotr 2012-07-13 09:29:57

+1

使用addJavascriptInterface的任何理由都不會更好? – Keith 2013-02-11 06:15:13

9

這是我今天想到的。它是線程安全的,相當高效,並且允許從Java的同步Javascript執行Android WebView

適用於Android 2.2及更高。 (需要commons-lang,因爲我需要將我的代碼片段作爲Javascript字符串傳遞給eval(),您可以通過將代碼不包含在引號中來消除此依賴關係,但是在function(){}

首先,將此添加到您的Javascript文件:

function evalJsForAndroid(evalJs_index, jsString) { 
    var evalJs_result = ""; 
    try { 
     evalJs_result = ""+eval(jsString); 
    } catch (e) { 
     console.log(e); 
    } 
    androidInterface.processReturnValue(evalJs_index, evalJs_result); 
} 

然後,將它添加到你的Android活動:

private Handler handler = new Handler(); 
private final AtomicInteger evalJsIndex = new AtomicInteger(0); 
private final Map<Integer, String> jsReturnValues = new HashMap<Integer, String>(); 
private final Object jsReturnValueLock = new Object(); 
private WebView webView; 

@Override 
public void onCreate(Bundle savedInstanceState) { 
    super.onCreate(savedInstanceState); 
    webView = (WebView) findViewById(R.id.webView); 
    webView.addJavascriptInterface(new MyJavascriptInterface(this), "androidInterface"); 
} 

public String evalJs(final String js) { 
    final int index = evalJsIndex.incrementAndGet(); 
    handler.post(new Runnable() { 
     public void run() { 
      webView.loadUrl("javascript:evalJsForAndroid(" + index + ", " + 
            "\"" + StringEscapeUtils.escapeEcmaScript(js) + "\")"); 
     } 
    }); 
    return waitForJsReturnValue(index, 10000); 
} 

private String waitForJsReturnValue(int index, int waitMs) { 
    long start = System.currentTimeMillis(); 

    while (true) { 
     long elapsed = System.currentTimeMillis() - start; 
     if (elapsed > waitMs) 
      break; 
     synchronized (jsReturnValueLock) { 
      String value = jsReturnValues.remove(index); 
      if (value != null) 
       return value; 

      long toWait = waitMs - (System.currentTimeMillis() - start); 
      if (toWait > 0) 
       try { 
        jsReturnValueLock.wait(toWait); 
       } catch (InterruptedException e) { 
        break; 
       } 
      else 
       break; 
     } 
    } 
    Log.e("MyActivity", "Giving up; waited " + (waitMs/1000) + "sec for return value " + index); 
    return ""; 
} 

private void processJsReturnValue(int index, String value) { 
    synchronized (jsReturnValueLock) { 
     jsReturnValues.put(index, value); 
     jsReturnValueLock.notifyAll(); 
    } 
} 

private static class MyJavascriptInterface { 
    private MyActivity activity; 

    public MyJavascriptInterface(MyActivity activity) { 
     this.activity = activity; 
    } 

    // this annotation is required in Jelly Bean and later: 
    @JavascriptInterface 
    public void processReturnValue(int index, String value) { 
     activity.processJsReturnValue(index, value); 
    } 
} 
+2

感謝上述代碼 - 我在Android項目中採用了它。但是,由於糟糕的Java API設計,它有一個陷阱。該行: jsReturnValueLock.wait(waitMs - (System.currentTimeMillis() - start)); wait()函數參數的值有時可能會發生評估爲0.這意味着「無限等待」 - 我偶爾會遇到「無響應」錯誤。我通過測試解決了這個問題: if(elapsed> = waitMS)break; 而不是再次調用System.currentTimeMillis(),但使用過去的值。最好編輯並修改上面的代碼。 – gregko 2013-03-30 21:01:03

+0

非常感謝!我也看到了這一點,也從未想出它是從哪裏來的。 – Keith 2013-04-01 14:06:31

+1

但是,如果我在button.click監聽器等中運行evalJS。 waitForJsReturnValue可能會導致evalJs()掛起。因此,handler.post()中的webView.loadUrl()可能沒有機會執行,因爲button.click監聽器沒有返回。 – victorwoo 2013-07-11 09:18:53

56

Keith但更短的答案

webView.addJavascriptInterface(this, "android"); 
webView.loadUrl("javascript:android.onData(functionThatReturnsSomething)"); 

並實現功能

@JavascriptInterface 
public void onData(String value) { 
    //.. do something with the data 
} 

不要忘記從proguard列表中刪除onData(如果啓用了proguard的)

+1

你能詳細介紹'proguard'部分嗎? – eugene 2014-07-10 13:51:22

+0

@eugene如果你不知道它是什麼,你可能不會使用它。無論如何,它的東西,使反向工程,你的應用程序更難 – 2014-07-10 14:08:46

+1

謝謝。我知道我在項目的根目錄下有proguard-project.txt,但沒有更多。我在問,因爲你的解決方案有效,但我得到了SIGSEGV。我正在評估'document.getElementById(「my-div」)。scrollTop'從SwipeRefreshLayout :: canChildScrollUp()獲取滾動位置。我懷疑這可能是由於在短時間內打了太多次電話。或'proguard'我懷疑是因爲我只是不知道它是什麼.. – eugene 2014-07-11 01:31:08

4

是@Felix Khazin建議工程的解決方案,但有一個關鍵點缺失。

在調用WebView的網頁後,應該調用javascript調用。將此WebViewClientWebChromeClient一起添加到WebView

完整的例子:

3

在API 19+,要做到這一點的最好方法是調用evaluateJavascript您的WebView:

webView.evaluateJavascript("foo.bar()", new ValueCallback<String>() { 
    @Override public void onReceiveValue(String value) { 
     // value is the result returned by the Javascript as JSON 
    } 
}); 

有更詳細的相關答案:https://stackoverflow.com/a/20377857

相關問題