这是一种利用Android's capability for JavaScript hooks 检测何时加载 URL 的新颖方法。使用这种模式,我们利用 JavaScript 对文档状态的了解在 Android 运行时生成本地方法调用。可以使用 @JavaScriptInterface 注释进行这些 JavaScript 可访问的调用。
此实现要求我们在 WebView 的设置上调用 setJavaScriptEnabled(true),因此它可能不适合,具体取决于您的应用程序的要求,例如安全问题。
src/io/github/cawfree/webviewcallback/MainActivity.java (Jelly Bean,API 级别 16)
package io.github.cawfree.webviewcallback;
/**
* Created by Alex Thomas (@Cawfree), 30/03/2017.
**/
import android.net.http.SslError;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.webkit.JavascriptInterface;
import android.webkit.SslErrorHandler;
import android.webkit.WebView;
import android.webkit.WebViewClient;
import android.widget.Toast;
/** An Activity demonstrating how to introduce a callback mechanism into Android's WebView. */
public class MainActivity extends AppCompatActivity {
/* Static Declarations. */
private static final String HOOK_JS = "Android";
private static final String URL_TEST = "http://www.zonal.co.uk/";
private static final String URL_PREPARE_WEBVIEW = "";
/* Member Variables. */
private WebView mWebView = null;
/** Create the Activity. */
@Override protected final void onCreate(final Bundle pSavedInstanceState) {
// Initialize the parent definition.
super.onCreate(pSavedInstanceState);
// Set the Content View.
this.setContentView(R.layout.activity_main);
// Fetch the WebView.
this.mWebView = (WebView)this.findViewById(R.id.webView);
// Enable JavaScript.
this.getWebView().getSettings().setJavaScriptEnabled(true);
// Define the custom WebClient. (Here I'm just suppressing security errors, since older Android devices struggle with TLS.)
this.getWebView().setWebViewClient(new WebViewClient() { @Override public final void onReceivedSslError(final WebView pWebView, final SslErrorHandler pSslErrorHandler, final SslError pSslError) { pSslErrorHandler.proceed(); } });
// Define the WebView JavaScript hook.
this.getWebView().addJavascriptInterface(this, MainActivity.HOOK_JS);
// Make this initial call to prepare JavaScript execution.
this.getWebView().loadUrl(MainActivity.URL_PREPARE_WEBVIEW);
}
/** When the Activity is Resumed. */
@Override protected final void onPostResume() {
// Handle as usual.
super.onPostResume();
// Load the URL as usual.
this.getWebView().loadUrl(MainActivity.URL_TEST);
// Use JavaScript to embed a hook to Android's MainActivity. (The onExportPageLoaded() function implements the callback, whilst we add some tests for the state of the WebPage so as to infer when to export the event.)
this.getWebView().loadUrl("javascript:" + "function onExportPageLoaded() { " + MainActivity.HOOK_JS + ".onPageLoaded(); }" + "if(document.readyState === 'complete') { onExportPageLoaded(); } else { window.addEventListener('onload', function () { onExportPageLoaded(); }, false); }");
}
/** Javascript-accessible callback for declaring when a page has loaded. */
@JavascriptInterface @SuppressWarnings("unused") public final void onPageLoaded() {
// Display the Message.
Toast.makeText(this, "Page has loaded!", Toast.LENGTH_SHORT).show();
}
/* Getters. */
public final WebView getWebView() {
return this.mWebView;
}
}
res/layout/activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<WebView
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/webView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
/>
基本上,我们附加了一个额外的 JavaScript 函数,用于测试文档的状态。如果它被加载,我们在 Android 的MainActivity 中启动一个自定义的onPageLoaded() 事件;否则,我们使用 window.addEventListener('onload', ...); 注册一个事件监听器,该监听器会在页面准备好后更新 Android。
由于我们是在调用 this.getWebView().loadURL("") 之后附加此脚本,因此我们可能根本不需要 'listen' 事件,因为我们只得到一旦页面已经加载,就有机会通过连续调用 loadURL 来附加 JavaScript 钩子。