Markdown版本筆記 | 個人GitHub首頁 | 個人博客 | 個人微信 | 個人郵箱 |
---|---|---|---|---|
MyAndroidBlogs | baiqiantao | baiqiantao | bqt20094 | baiqiantao@sina.com |
WebView JS交互 addJavascriptInterface MDjavascript
目錄
原生和 JS 交互
Demohtml
注意,若是在 java 與 Javascript 交互的時候出現以下錯誤:前端
Uncaught ReferenceError: <pre name="code" class="html">getDeviceID
極可能是由於在 html 頁面尚未加載完,就加載裏面的 JS 方法,這樣是找不到這個方法的。java
解決方法爲:把調用放到 onPageFinished() 裏面。android
addJavascriptInterface
void addJavascriptInterface(Object object, String name)
參數:github
- Object object : the Java object to inject注入 into this WebView's JavaScript context. Null values are ignored.
- String name : the name used to expose暴露、公開 the object in JavaScript
方法說明:web
- Injects the supplied Java object into this WebView.
- The object is injected into the JavaScript context of the main frame, using the supplied name.
- This allows the Java object's methods to be accessed from JavaScript.
- For applications targeted to API level JELLY_BEAN_MR1 and above, only public methods that are annotated with
JavascriptInterface
can be accessed from JavaScript. - For applications targeted to API level JELLY_BEAN or below, all public methods (including the inherited繼承的 ones) can be accessed, see the
important security note
安全注意事項 below for implications意義、含義. - Note that injected objects will not appear in JavaScript until the page is next (re)loaded. JavaScript should be enabled before injecting the object.
使用案例:安全
class JsObject { @JavascriptInterface public String toString() { return "injectedObject"; } } webview.getSettings().setJavaScriptEnabled(true); webView.addJavascriptInterface(new JsObject(), "injectedObject"); webView.loadData("", "text/html", null); webView.loadUrl("javascript:alert(injectedObject.toString())");
IMPORTANT微信
- This method can be used to allow JavaScript to control the host application.
- This is a powerful feature, but also presents a security risk for apps targeting JELLY_BEAN or earlier.
- Apps that target a version later than JELLY_BEAN are still vulnerable很容易受到攻擊 if the app runs on a device running Android earlier than 4.2.
- The most secure way to use this method is to target JELLY_BEAN_MR1 and to ensure the method is called only when running on Android 4.2 or later.
- With these older versions, JavaScript could use
reflection
to access an injected object'spublic fields
. - Use of this method in a WebView containing untrusted content could allow an attacker攻擊者 to manipulate操縱 the host application in unintended非預期 ways, executing執行 Java code with the permissions of the host application.
- Use extreme care格外當心 when using this method in a WebView which could contain untrusted content.
說明:
- JavaScript interacts with交互 Java object on a
private, background thread
of this WebView. Care is therefore required to maintain保持 thread safety. - The Java object's fields are not accessible.
- For applications targeted to API level LOLLIPOP and above, methods of injected Java objects are enumerable枚舉 from JavaScript.
removeJavascriptInterface
void removeJavascriptInterface(String name)
String name : the name used to expose暴露 the object in JavaScript. This value must never be null.
Removes a previously injected Java object from this WebView.
Note that the removal will not be reflected in JavaScript until the page is next (re)loaded.
evaluateJavascript
void evaluateJavascript (String script, ValueCallback<String> resultCallback)
參數:
String script : the JavaScript to execute.
ValueCallback resultCallback :
- A callback to be invoked when the script execution completes with the result of the execution (if any).
當腳本執行完成時的回調,若是有執行結果的話會攜帶執行結果。 - May be null if no notification of the result is required.
若是不須要結果通知,能夠爲null。
說明:
- Asynchronously evaluates JavaScript in the context of the currently displayed page.
在當前顯示頁面的上下文中異步執行JavaScript。 - If non-null, resultCallback will be invoked with any result returned from that execution.
若是非空,resultCallback會被回調並攜帶調用後返回的結果。 - This method must be called on the UI thread and the callback will be made on the UI thread.
此方法必須在UI線程上調用,而且回調也是運行在UI線程上的。
Compatibility note 兼容性說明
- Applications targeting N or later, JavaScript state from an empty WebView is no longer persisted across navigations like
loadUrl(String)
.
目標版本爲N或更高的應用程序,空的WebView中的JavaScript狀態再也不繼續像loadUrl那樣在navigations中保留。 - For example, global variables and functions defined before calling
loadUrl(String)
will not exist in the loaded page.
例如,在調用loadUrl
以前定義的全局變量和函數將不會存在於加載的頁面中。 - Applications should use addJavascriptInterface(Object, String) instead to persist JavaScript objects across navigations.
應用程序應該使用 addJavascriptInterface 來跨navigations維護(持久化)JavaScript對象。
案例
原生代碼
webview.addJavascriptInterface(new JSInterface(this, webview), JSInterface.JS_INTERFACE_NAME);
public class JSInterface { public static final String JS_INTERFACE_NAME = "JSInterface";//JS調用類名 private Context mContext; private WebView webView; public JSInterface(Context context, WebView webView) { this.mContext = context; this.webView = webView; } @JavascriptInterface public void hello(String content) { Log.i("bqt", "JS 調用原生時是否發生在主線程:" + (Looper.myLooper() == Looper.getMainLooper()));//false new Handler(Looper.getMainLooper()).post(() -> //WebView等UI操做必須在主線程中進行 Toast.makeText(mContext, "原生的hello方法被調用了:" + content, Toast.LENGTH_SHORT).show()); SystemClock.sleep(3000);//模擬耗時操做 String call = "javascript:javacalljs(" + System.currentTimeMillis() + ")";//格式很重要,大部分錯誤都是因爲格式問題致使的 new Handler(Looper.getMainLooper()).post(() -> webView.loadUrl(call));//WebView等UI操做必須在主線程中進行 } @JavascriptInterface public void hello2(String content) { new Handler(Looper.getMainLooper()).post(() -> Toast.makeText(mContext, content, Toast.LENGTH_SHORT).show()); SystemClock.sleep(3000);//模擬耗時操做 String call = "javascript:javacalljs2(" + System.currentTimeMillis() + ")";//JS此方法的返回值會經過onReceiveValue回調到原生 new Handler(Looper.getMainLooper()).post(() -> webView.evaluateJavascript(call, value -> { Log.i("bqt", "ValueCallback 是否發生在主線程:" + (Looper.myLooper() == Looper.getMainLooper()));//true Toast.makeText(mContext, "【onReceiveValue】" + value, Toast.LENGTH_SHORT).show(); })); } }
前端代碼
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <button onclick='jscalljava("包青天")'>點擊後調用 JS 的 jscalljava 方法,在此方法中 JS 調用原生 JSInterface 類中的 hello 方法,原生 hello 方法執行完操做後回調 JS 中的 javacalljs 方法</button> <button onclick='jscalljava2()'>演示原生調用 JS 方法後,JS 將執行完return的結果經過原生提供的 ValueCallback 對象的 onReceiveValue 方法回調給原生</button> </body> <script type="text/javascript"> function jscalljava(content){ console.log("【jscalljava】"); JSInterface.hello(content); } function jscalljava2(){ console.log("【jscalljava2】"); var content="[\"包青天\",\"白乾濤\",\"bqt\"]"; JSInterface.hello2(content); } function javacalljs(content){ console.log("【js中的javacalljs方法被調用了】content="+content); } function javacalljs2(content){ console.log("【js中的javacalljs2方法被調用了】content="+content); return 20094; } </script> </html>
2017-8-25