由于文章太長荔仁,官方不讓發(fā)布更哄,所以文章就一分為二了
WebViewClient相關(guān)方法
- shouldOverrideUrlLoading(WebView view, String url)和shouldOverrideUrlLoading(WebView view, WebResourceRequest request)
shouldOverrideUrlLoading執(zhí)行時機是重定向時(網(wǎng)頁自動重定向或手動點擊網(wǎng)頁內(nèi)部鏈接)
mywebview.setWebViewClient(new WebViewClient(){
@Override
public boolean shouldOverrideUrlLoading(WebView view, String url) {
//過時
Log.d("yunchong", "shouldOverrideUrlLoading1:"+url);
if(!TextUtils.isEmpty(url)){
if(url.startsWith("http://") || url.startsWith("https://")){
view.loadUrl(url);
} else{
//跳轉(zhuǎn)到第三方
try {
Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse(url));
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_SINGLE_TOP);
MainActivity.this.startActivity(intent);
}catch (Exception e){
//如果拋出異常,則說明本地沒有安裝第三方
e.printStackTrace();
}
}
return true;
}
return false;
}
@Override
public boolean shouldOverrideUrlLoading(WebView view, WebResourceRequest request) {
Log.d("yunchong", "shouldOverrideUrlLoading2:"+url);
String url = view.getUrl();
if(!TextUtils.isEmpty(url)){
if(url.startsWith("http://") || url.startsWith("https://")){
view.loadUrl(url);
} else{
//跳轉(zhuǎn)到第三方
try {
Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse(url));
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_SINGLE_TOP);
MainActivity.this.startActivity(intent);
}catch (Exception e){
//如果拋出異常逻翁,則說明本地沒有安裝第三方
e.printStackTrace();
}
}
return true;
}
return false;
}
主要注意以下幾點:
(1)前者在API24(Android 7.0)之后已經(jīng)過時,雖然已經(jīng)過時了,但是我們還是需要用到這個方法的掀序,原因是API24之前的手機只執(zhí)行前者,后者只有在API24之后才會執(zhí)行惭婿;
(2)以上代碼中不恭,后者的返回值有true和false兩種, 第三種返回值是默認返回值
return super.shouldOverrideUrlLoading(view, request);
這個返回值最終還是會執(zhí)行前者方法审孽。
2.shouldInterceptRequest(WebView view, String url)和shouldInterceptRequest(final WebView view, WebResourceRequest request)
@Nullable
@Override
public WebResourceResponse shouldInterceptRequest(WebView view, String url) {
//過時
Log.d("yunchong", url);
return super.shouldInterceptRequest(view, url);
}
捕獲的日志如下:
此方法廢棄于API21县袱,調(diào)用于非UI線程,雖然廢棄了佑力,但是我在Android4.4式散、Android6.0、Android8.0的手機經(jīng)過測試打颤,依然可以攔截到加載資源
攔截資源請求并返回響應(yīng)數(shù)據(jù)暴拄,返回null時WebView將繼續(xù)加載資源
注意:API21以下的AJAX請求會走onLoadResource漓滔,無法通過此方法攔截
在API21之后新增一個新的方法:
@Nullable
@Override
public WebResourceResponse shouldInterceptRequest(final WebView view, WebResourceRequest request) {
view.post(new Runnable() {
@Override
public void run() {
Log.d("yunchong", "shouldInterceptRequest2:"+view.getUrl());
}
});
return super.shouldInterceptRequest(view, request);
}
用于替代前者。
3.onLoadResource(WebView view, String url)
這個方法和shouldInterceptRequest一樣乖篷,同樣可以攔截到加載資源响驴,他們的區(qū)別是:shouldInterceptRequest已過時,onLoadResource沒有過時撕蔼。
3.onTooManyRedirects(WebView view, Message cancelMsg, Message continueMsg)
這個方法是為了解決一些重定向問題豁鲤,已經(jīng)徹底廢棄,現(xiàn)在處理重定向問題是用shouldOverrideUrlLoading方法了鲸沮。
4.onPageStarted和onPageFinished
網(wǎng)頁的加載開始和加載結(jié)束琳骡。
5.onPageCommitVisible(WebView view, String url)
// 這個回調(diào)添加于API23,僅用于主框架的導(dǎo)航
// 通知應(yīng)用導(dǎo)航到之前頁面時讼溺,其遺留的WebView內(nèi)容將不再被繪制楣号。
// 這個回調(diào)可以用來決定哪些WebView可見內(nèi)容能被安全地回收,以確保不顯示陳舊的內(nèi)容
// 它最早被調(diào)用怒坯,以此保證WebView.onDraw不會繪制任何之前頁面的內(nèi)容炫狱,隨后繪制背景色或需要加載的新內(nèi)容。
// 當(dāng)HTTP響應(yīng)body已經(jīng)開始加載并體現(xiàn)在DOM上將在隨后的繪制中可見時剔猿,這個方法會被調(diào)用视译。
// 這個回調(diào)發(fā)生在文檔加載的早期,因此它的資源(css,和圖像)可能不可用归敬。
// 如果需要更細粒度的視圖更新憎亚,查看 postVisualStateCallback(long, WebView.VisualStateCallback).
// 請注意這上邊的所有條件也支持 postVisualStateCallback(long ,WebView.VisualStateCallback)
@Override
public void onPageCommitVisible(WebView view, String url) {
Log.d("yunchong", "onPageCommitVisible:"+url);
super.onPageCommitVisible(view, url);
}
跟蹤日志發(fā)現(xiàn)任意的網(wǎng)頁跳轉(zhuǎn)都會執(zhí)行onPageCommitVisible方法,參數(shù)url就是當(dāng)前頁面的url弄慰。
6.onReceivedHttpError(WebView view, WebResourceRequest request, WebResourceResponse errorResponse)
// 此方法添加于API23
// 在加載資源(iframe,image,js,css,ajax...)時收到了 HTTP 錯誤(狀態(tài)碼>=400)
@Override
public void onReceivedHttpError(WebView view, WebResourceRequest request, WebResourceResponse errorResponse) {
super.onReceivedHttpError(view, request, errorResponse);
}
7.onFormResubmission(WebView view, Message dontResend, Message resend)
// 是否重新提交表單第美,默認不重發(fā)
@Override
public void onFormResubmission(WebView view, Message dontResend, Message resend) {
super.onFormResubmission(view, dontResend, resend);
}
8.doUpdateVisitedHistory(WebView view, String url, boolean isReload)
// 通知應(yīng)用可以將當(dāng)前的url存儲在數(shù)據(jù)庫中,意味著當(dāng)前的訪問url已經(jīng)生效并被記錄在內(nèi)核當(dāng)中陆爽。
// 此方法在網(wǎng)頁加載過程中只會被調(diào)用一次什往,網(wǎng)頁前進后退并不會回調(diào)這個函數(shù)。
@Override
public void doUpdateVisitedHistory(WebView view, String url, boolean isReload) {
super.doUpdateVisitedHistory(view, url, isReload);
}
9.onReceivedClientCertRequest(WebView view, ClientCertRequest request)
// 此方法添加于API21慌闭,在UI線程被調(diào)用
// 處理SSL客戶端證書請求别威,必要的話可顯示一個UI來提供KEY。
// 有三種響應(yīng)方式:proceed()/cancel()/ignore()驴剔,默認行為是取消請求
// 如果調(diào)用proceed()或cancel()省古,Webview 將在內(nèi)存中保存響應(yīng)結(jié)果且對相同的"host:port"不會再次調(diào)用 onReceivedClientCertRequest
// 多數(shù)情況下,可通過KeyChain.choosePrivateKeyAlias啟動一個Activity供用戶選擇合適的私鑰
@RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
public void onReceivedClientCertRequest(WebView view, ClientCertRequest request) {
super.onReceivedClientCertRequest(view, request); //默認
//request.cancel();//取消
//request.ignore();//忽視
//request.proceed(param1, param2);//接受
}
10.onReceivedHttpAuthRequest(WebView view, HttpAuthHandler handler, String host, String realm)
// 處理HTTP認證請求,默認行為是取消請求
@Override
public void onReceivedHttpAuthRequest(WebView view, HttpAuthHandler handler, String host, String realm) {
Log.d("yunchong", "onReceivedHttpAuthRequest");
super.onReceivedHttpAuthRequest(view, handler, host, realm);//默認
//handler.cancel();//取消
//handler.proceed(username, password);//接受
}
11.shouldOverrideKeyEvent(WebView view, KeyEvent event)
// 給應(yīng)用一個機會處理按鍵事件
// 如果返回true,WebView不處理該事件到忽,否則WebView會一直處理率挣,默認返回false
@Override
public boolean shouldOverrideKeyEvent(WebView view, KeyEvent event) {
return super.shouldOverrideKeyEvent(view, event);
}
12.onUnhandledKeyEvent(WebView view, KeyEvent event)
// 處理未被WebView消費的按鍵事件
// WebView總是消費按鍵事件琳拭,除非是系統(tǒng)按鍵或shouldOverrideKeyEvent返回true
// 此方法在按鍵事件分派時被異步調(diào)用
@Override
public void onUnhandledKeyEvent(WebView view, KeyEvent event) {
super.onUnhandledKeyEvent(view, event);
}
13.onScaleChanged(WebView view, float oldScale, float newScale)
// 通知應(yīng)用頁面縮放系數(shù)變化
@Override
public void onScaleChanged(WebView view, float oldScale, float newScale) {
super.onScaleChanged(view, oldScale, newScale);
}
當(dāng)webview支持雙指縮放時训堆,onScaleChanged回調(diào)方法可以監(jiān)聽webview的縮放系數(shù)。
14.onReceivedLoginRequest(WebView view, String realm, @Nullable String account, String args)
//通知應(yīng)用有個已授權(quán)賬號自動登陸了
@Override
public void onReceivedLoginRequest(WebView view, String realm, @Nullable String account, String args) {
super.onReceivedLoginRequest(view, realm, account, args);
}
15.onRenderProcessGone(WebView view, RenderProcessGoneDetail detail)
//這個API處理一個WebView對象的渲染程序消失的情況白嘁,要么是因為系統(tǒng)殺死了渲染器以回收急需的內(nèi)存坑鱼,要么是因為渲染程序本身崩潰了。
// 通過使用這個API絮缅,您可以讓您的應(yīng)用程序繼續(xù)執(zhí)行鲁沥,即使渲染過程已經(jīng)消失了。
@RequiresApi(api = Build.VERSION_CODES.O)
@Override
public boolean onRenderProcessGone(WebView view, RenderProcessGoneDetail detail) {
//RenderProcessGoneDetail : 此類提供了有關(guān)渲染進程退出原因的更具體信息耕魄。應(yīng)用程序可以使用它來決定如何處理這種情況黍析。
//RenderProcessGoneDetail提供了兩個方法
//didCrash():指示是否觀察到呈現(xiàn)進程崩潰,或者是否被系統(tǒng)終止屎开。
//rendererPriorityAtExit():返回在渲染器退出時設(shè)置的渲染器優(yōu)先級。
if(!detail.didCrash()){
//由于系統(tǒng)內(nèi)存不足马靠,渲染器被終止奄抽。
//通過創(chuàng)建新的WebView實例,應(yīng)用程序可以正乘恢復(fù)
if (mywebview != null) {
ViewGroup webViewContainer = (ViewGroup) findViewById(R.id.mywebview);
webViewContainer.removeView(mywebview);
mywebview.destroy();
mywebview = null;
}
return true;
}
return false;
}
16.onSafeBrowsingHit(WebView view, WebResourceRequest request, int threatType, SafeBrowsingResponse callback)
//通知主應(yīng)用程序加載URL已被安全瀏覽標記逞度。
@Override
public void onSafeBrowsingHit(WebView view, WebResourceRequest request, int threatType, SafeBrowsingResponse callback) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O_MR1) {
callback.backToSafety(true);//類似于用戶點擊了“返回安全”按鈕
callback.proceed(true);//類似于用戶單擊了“訪問此不安全站點”按鈕一樣。
callback.showInterstitial(true);//如果為true則顯示報告復(fù)選框
}
super.onSafeBrowsingHit(view, request, threatType, callback);
}
以上的注釋是由谷歌官方文檔翻譯而來妙啃,可悲的是我嘗試了7.0档泽、8.0、8.1的手機仍然沒有執(zhí)行到該方法揖赴。沒有發(fā)現(xiàn)此方法的執(zhí)行時機馆匿。
17.onReceivedSslError(WebView view, SslErrorHandler handler, SslError error)
當(dāng)加載https網(wǎng)頁的情況下,默認不進行證書校驗燥滑,當(dāng)服務(wù)器要求webview必須進行證書校驗的時候渐北,默認情況下加載網(wǎng)頁是一片空白,這個時候我們需要在onReceivedSslError方法里做一些處理
@Override
public void onReceivedSslError(WebView view, SslErrorHandler handler, SslError error) {
handler.proceed();
}
也就是說铭拧,接受所有的https證書赃蛛。
然而這樣做是不安全的,我們需要對證書做ssl校驗搀菩,代碼如下:(我們事先將crt證書放入assets目錄下)
@Override
public void onReceivedSslError(WebView view, final SslErrorHandler handler, SslError error) {
String currentUrl = view.getUrl();
if(TextUtils.isEmpty(currentUrl)){//個別手機獲取到的url是空的
currentUrl = homeUrl;//這里的homeUrl是從其他地方獲取到的
}
SslManager.webviewSsl(currentUrl, new SslListener() {
@Override
public void onResponse() {
handler.proceed();
}
@Override
public void onFailure() {
handler.cancel();
}
});
}
public class SslManager {
/**
* webview ssl校驗
* @param url
*/
public static void webviewSsl(String url, final SslListener sslListener) {
if(!TextUtils.isEmpty(url)){
getOkHttpBuilder(url).build().newCall( new Request.Builder().url(url).build()).enqueue(new Callback() {
@Override
public void onFailure(Call call, IOException e) {
sslListener.onFailure();
}
@Override
public void onResponse(Call call, Response response) throws IOException {
sslListener.onResponse();
}
});
}
}
public static OkHttpClient.Builder getOkHttpBuilder(String url){
OkHttpClient.Builder builder = null;
String crt;
if(!TextUtils.isEmpty(url)){
crt = "crt/crtname.crt";
try {
builder = setCertificates(new OkHttpClient.Builder(), IMApp.getAppContext().getResources().getAssets().open(crt));
} catch (IOException e) {
builder = new OkHttpClient.Builder();
}
}
return builder;
}
private static OkHttpClient.Builder setCertificates(OkHttpClient.Builder client, InputStream... certificates) {
try {
CertificateFactory certificateFactory = CertificateFactory.getInstance("X.509");
KeyStore keyStore = KeyStore.getInstance("PKCS12", "BC");
keyStore.load(null);
int index = 0;
for (InputStream certificate : certificates) {
String certificateAlias = Integer.toString(index++);
keyStore.setCertificateEntry(certificateAlias, certificateFactory.generateCertificate(certificate));
try {
if (certificate != null)
certificate.close();
} catch (IOException e) {
e.printStackTrace();
}
}
SSLContext sslContext = SSLContext.getInstance("TLS");
TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
trustManagerFactory.init(keyStore);
sslContext.init(null, trustManagerFactory.getTrustManagers(), new SecureRandom());
SSLSocketFactory sslSocketFactory = sslContext.getSocketFactory();
X509TrustManager trustManager = Platform.get().trustManager(sslSocketFactory);
client.sslSocketFactory(sslSocketFactory, trustManager);
//hostName驗證
client.hostnameVerifier(new HostnameVerifier() {
@Override
public boolean verify(String hostname, SSLSession session) {
String peerHost = session.getPeerHost();//服務(wù)器返回的域名
try {
X509Certificate[] peerCertificates = (X509Certificate[]) session.getPeerCertificates();
for (X509Certificate c : peerCertificates) {
X500Principal subjectX500Principal = c.getSubjectX500Principal();
String name = new X500p(subjectX500Principal).getName();
String[] split = name.split(",");
for (String s : split) {
if (s.startsWith("CN")) {
if (s.contains(hostname) && s.contains(peerHost)) {
return true;
}
}
}
}
} catch (SSLPeerUnverifiedException e) {
e.printStackTrace();
}
return false;
}
});
} catch (Exception e) {
e.printStackTrace();
}
return client;
}
}
WebChromeClient相關(guān)方法
1.onProgressChanged(WebView view, int newProgress)
這是webview加載網(wǎng)頁的進度監(jiān)聽呕臂,常用作于添加瀏覽器的進度條。
//監(jiān)聽加載網(wǎng)頁的進度情況
mywebview.setWebChromeClient(new WebChromeClient(){
@Override
public void onProgressChanged(WebView view, int newProgress) {
super.onProgressChanged(view, newProgress);
}
2.onReceivedTitle(WebView view, String title)
//獲取網(wǎng)頁的標題
@Override
public void onReceivedTitle(WebView view, String title) {
super.onReceivedTitle(view, title);
}
3.onReceivedIcon(WebView view, Bitmap icon)
//字面上的意思是:當(dāng)前網(wǎng)頁接收到icon的時候執(zhí)行此方法
@Override
public void onReceivedIcon(WebView view, Bitmap icon) {
super.onReceivedIcon(view, icon);
}
4.onReceivedTouchIconUrl(WebView view, String url, boolean precomposed)
//字面上的意思是:觸摸當(dāng)前網(wǎng)頁接收到icon的時候執(zhí)行此方法
@Override
public void onReceivedTouchIconUrl(WebView view, String url, boolean precomposed) {
super.onReceivedTouchIconUrl(view, url, precomposed);
}
5.onShowCustomView和onHideCustomView
這兩個方法往往是一起使用肪跋,常用于解決webview不能全屏播放視頻的問題歧蒋。(有必要的話對當(dāng)前activity開啟硬件加速)
代碼如下:
private View customView;//默認view
private FrameLayout fullscreenContainer;//全屏view
/** 視頻全屏參數(shù) */
private FrameLayout.LayoutParams COVER_SCREEN_PARAMS = new FrameLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT);
private WebChromeClient.CustomViewCallback customViewCallback;
//顯示自定義View:常用于視頻全屏展示
@Override
public void onShowCustomView(View view, CustomViewCallback callback) {
if (customView != null) {
callback.onCustomViewHidden();
return;
}
//MainActivity.this.getWindow().getDecorView();
FrameLayout decor = (FrameLayout) getWindow().getDecorView();
fullscreenContainer = new FullscreenHolder(MainActivity.this);
fullscreenContainer.addView(view, COVER_SCREEN_PARAMS);
decor.addView(fullscreenContainer, COVER_SCREEN_PARAMS);
customView = view;
setStatusBarVisibility(false);
customViewCallback = callback;
}
//顯示自定義View:常用于視頻全屏展示
@Override
public void onShowCustomView(View view, int requestedOrientation, CustomViewCallback callback) {
onShowCustomView(view, callback);
}
//隱藏自定義View:
@Override
public void onHideCustomView() {
hideCustomView();
}
//視頻從全屏還原到原來
private void hideCustomView(){
if (customView == null) {
return;
}
setStatusBarVisibility(true);
FrameLayout decor = (FrameLayout) getWindow().getDecorView();
decor.removeView(fullscreenContainer);
fullscreenContainer = null;
customView = null;
customViewCallback.onCustomViewHidden();
mywebview.setVisibility(View.VISIBLE);
}
這里還需要注意的是,點擊返回鍵應(yīng)該退出全屏
@Override
public void onBackPressed() {
if(customView != null){
hideCustomView();
}else if(mywebview != null && mywebview.canGoBack()){
mywebview.goBack();
}else{
finish();
}
}
6.onCreateWindow和onCloseWindow
//請求主機應(yīng)用創(chuàng)建一個新窗口。
//如果主機應(yīng)用選擇響應(yīng)這個請求疏尿,則該方法返回true瘟芝,并創(chuàng)建一個新的WebView,
//將其插入到視圖系統(tǒng)中褥琐,并將其提供的resultMsg作為參數(shù)提供給新的WebView锌俱。
//如果主機應(yīng)用選擇不響應(yīng)這個請求時,則該方法返回false。
//默認情況下敌呈,該方法不做任何處理并返回false贸宏。
@Override
public boolean onCreateWindow(WebView view, boolean isDialog, boolean isUserGesture, Message resultMsg) {
return super.onCreateWindow(view, isDialog, isUserGesture, resultMsg);
}
//通知主機主機應(yīng)用WebView關(guān)閉了,并在需要的時候從view系統(tǒng)中移除它磕洪。
//此時,WebCore已經(jīng)停止窗口中的所有加載進度,并在javascript中移除了所有cross-scripting的功能吭练。
@Override
public void onCloseWindow(WebView window) {
super.onCloseWindow(window);
}
7.onRequestFocus(WebView view)
@Override
public void onRequestFocus(WebView view) {
super.onRequestFocus(view);
}
不太清楚它的執(zhí)行時機,其他博客的解釋是:請求獲得WebView的焦點,這可能由于另一個WebView打開一個連接,需要被展示.
8.三種提示框Alert析显、Confirm鲫咽、Prompt
@Override
public boolean onJsAlert(WebView view, String url, String message, JsResult result) {
return super.onJsAlert(view, url, message, result);
}
@Override
public boolean onJsConfirm(WebView view, String url, String message, JsResult result) {
return super.onJsConfirm(view, url, message, result);
}
@Override
public boolean onJsPrompt(WebView view, String url, String message, String defaultValue, JsPromptResult result) {
return super.onJsPrompt(view, url, message, defaultValue, result);
}
常用的提示框是Alert和Confirm。
網(wǎng)上也是有相關(guān)博客 js中三種彈出框谷异。
9.onJsBeforeUnload(WebView view, String url, String message, JsResult result)
//JS相關(guān)操作分尸,會在頁面關(guān)閉或刷新調(diào)用,觸發(fā)事件時歹嘹,彈出對話框是否關(guān)閉箩绍,確定則關(guān)閉頁面,取消則保持該頁面尺上。
@Override
public boolean onJsBeforeUnload(WebView view, String url, String message, JsResult result) {
return super.onJsBeforeUnload(view, url, message, result);
}
10.onExceededDatabaseQuota(String url, String databaseIdentifier, long quota, long estimatedDatabaseSize, long totalQuota, WebStorage.QuotaUpdater quotaUpdater)
//使webview支持建立html5本地緩存數(shù)據(jù)庫
@Override
public void onExceededDatabaseQuota(String url, String databaseIdentifier, long quota, long estimatedDatabaseSize, long totalQuota, WebStorage.QuotaUpdater quotaUpdater) {
quotaUpdater.updateQuota(estimatedDatabaseSize * 2);
}
11.onReachedMaxAppCacheSize(long requiredStorage, long quota, WebStorage.QuotaUpdater quotaUpdater)
//已被廢棄材蛛,不用管
@Override
public void onReachedMaxAppCacheSize(long requiredStorage, long quota, WebStorage.QuotaUpdater quotaUpdater) {
super.onReachedMaxAppCacheSize(requiredStorage, quota, quotaUpdater);
}
12.webview定位權(quán)限提示框
@Override
public void onGeolocationPermissionsShowPrompt(final String origin, final GeolocationPermissions.Callback callback) {
final boolean remember = false;//是否記住
AlertDialog.Builder builder = new AlertDialog.Builder(MainActivity.this);
builder.setTitle("位置信息");
builder.setMessage(origin + "允許獲取您的地理位置信息嗎?")
.setCancelable(true)
.setPositiveButton("允許", new DialogInterface.OnClickListener() {
@Override public void onClick(DialogInterface dialog, int id) {
callback.invoke(origin, true, remember);
} })
.setNegativeButton("不允許", new DialogInterface.OnClickListener() {
@Override public void onClick(DialogInterface dialog, int id) {
callback.invoke(origin, false, remember);
}
});
AlertDialog alert = builder.create();
alert.show();
}
@Override
public void onGeolocationPermissionsHidePrompt() {
super.onGeolocationPermissionsHidePrompt();
}
13.來自網(wǎng)頁的權(quán)限請求
@Override
public void onPermissionRequest(PermissionRequest request) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
request.deny();//調(diào)用此方法以拒絕請求(默認)
request.getOrigin();//調(diào)用此方法以獲取試圖訪問受限資源的網(wǎng)頁的來源怎抛。
request.getResources();//調(diào)用此方法獲取網(wǎng)頁試圖訪問的資源卑吭。
request.grant(request.getResources());//調(diào)用此方法授予Origin訪問給定資源的權(quán)限。
}
}
@Override
public void onPermissionRequestCanceled(PermissionRequest request) {
super.onPermissionRequestCanceled(request);
}
默認是拒絕所有請求马绝,開發(fā)者可以根據(jù)需求授予指定網(wǎng)頁的權(quán)限請求陨簇。
14.onJsTimeout()
//已過時,不用管
@Override
public boolean onJsTimeout() {
return super.onJsTimeout();
}
15.onConsoleMessage
//三個參數(shù)的方法已經(jīng)廢棄迹淌,被一個參數(shù)的方法替代
@Override
public void onConsoleMessage(String message, int lineNumber, String sourceID) {
super.onConsoleMessage(message, lineNumber, sourceID);
}
@Override
public boolean onConsoleMessage(ConsoleMessage consoleMessage) {
return super.onConsoleMessage(consoleMessage);
}
可以捕獲到網(wǎng)頁打印在控制臺的日志河绽,有利于日志的跟蹤。
16.getDefaultVideoPoster()
//Html中唉窃,視頻(video)控件在沒有播放的時候?qū)⒔o用戶展示一張“海報”圖片(預(yù)覽圖)耙饰。
// 其預(yù)覽圖是由Html中video標簽的poster屬性來指定的。如果開發(fā)者沒有設(shè)置poster屬性, 則可以通過這個方法來設(shè)置默認的預(yù)覽圖纹份。
@Override
public Bitmap getDefaultVideoPoster() {
Bitmap bitmap = super.getDefaultVideoPoster();
if(bitmap == null){
return BitmapFactory.decodeResource(getApplicationContext().getResources(), R.mipmap.ic_launcher);
}
return super.getDefaultVideoPoster();
}
17.getVideoLoadingProgressView()
//播放視頻時苟跪,在第一幀呈現(xiàn)之前廷痘,需要花一定的時間來進行數(shù)據(jù)緩沖。
//ChromeClient可以使用這個函數(shù)來提供一個在數(shù)據(jù)緩沖時顯示的視圖件已。
// 例如,ChromeClient可以在緩沖時顯示一個轉(zhuǎn)輪動畫笋额。
@Override
public View getVideoLoadingProgressView() {
return super.getVideoLoadingProgressView();
}
18.getVisitedHistory(ValueCallback<String[]> callback)
//獲得所有訪問歷史項目的列表,用于鏈接著色篷扩。
@Override
public void getVisitedHistory(ValueCallback<String[]> callback) {
super.getVisitedHistory(callback);
}
19.讓webview支持inputfile控件
public ValueCallback<Uri> mUploadMessage;
public ValueCallback<Uri[]> mUploadMessageForAndroid5;
public final static int FILECHOOSER_RESULTCODE = 1;
public final static int FILECHOOSER_RESULTCODE_FOR_ANDROID_5 = 2;
//android3.0以前
public void openFileChooser(ValueCallback<Uri> uploadMsg) {
mUploadMessage = uploadMsg;
Intent i = new Intent(Intent.ACTION_GET_CONTENT);
i.addCategory(Intent.CATEGORY_OPENABLE);
i.setType("image/*");
startActivityForResult(Intent.createChooser(i, "File Chooser"), FILECHOOSER_RESULTCODE);
}
//android3.0到android4.0
public void openFileChooser(ValueCallback<Uri> uploadMsg, String acceptType) {
mUploadMessage = uploadMsg;
Intent i = new Intent(Intent.ACTION_GET_CONTENT);
i.addCategory(Intent.CATEGORY_OPENABLE);
i.setType("image/*");
startActivityForResult(Intent.createChooser(i, "File Chooser"), FILECHOOSER_RESULTCODE);
}
//android4.0到android4.3
public void openFileChooser(ValueCallback<Uri> filePathCallback, String acceptType, String capture) {
mUploadMessage = filePathCallback;
Intent i = new Intent(Intent.ACTION_GET_CONTENT);
i.addCategory(Intent.CATEGORY_OPENABLE);
i.setType("image/*");
startActivityForResult(Intent.createChooser(i, "File Chooser"), FILECHOOSER_RESULTCODE);
}
//android5.0以上
@Override
public boolean onShowFileChooser(WebView webView, ValueCallback<Uri[]> filePathCallback, FileChooserParams fileChooserParams) {
if (mUploadMessageForAndroid5 != null) {
mUploadMessageForAndroid5.onReceiveValue(null);
mUploadMessageForAndroid5 = null;
}
mUploadMessageForAndroid5 = filePathCallback;
Intent intent = null;
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.LOLLIPOP) {
intent = fileChooserParams.createIntent();
}
try {
startActivityForResult(intent, FILECHOOSER_RESULTCODE_FOR_ANDROID_5);
} catch (ActivityNotFoundException e) {
mUploadMessageForAndroid5 = null;
return false;
}
return true;
}
});
@Override
protected void onActivityResult(int requestCode, int resultCode,Intent intent) {
if (mUploadMessage != null) {
mUploadMessage.onReceiveValue(null);
mUploadMessage = null;
}
if (mUploadMessageForAndroid5 != null) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
mUploadMessageForAndroid5.onReceiveValue(null);
mUploadMessageForAndroid5 = null;
}
}
if (requestCode == FILECHOOSER_RESULTCODE) {
if (null == mUploadMessage){
return;
}
Uri result = intent == null || resultCode != RESULT_OK ? null:intent.getData();
mUploadMessage.onReceiveValue(result);
mUploadMessage = null;
} else if (requestCode == FILECHOOSER_RESULTCODE_FOR_ANDROID_5){
if (null == mUploadMessageForAndroid5){
return;
}
Uri result = (intent == null || resultCode != RESULT_OK) ? null: intent.getData();
if (result != null) {
mUploadMessageForAndroid5.onReceiveValue(new Uri[]{result});
} else {
mUploadMessageForAndroid5.onReceiveValue(new Uri[]{});
}
mUploadMessageForAndroid5 = null;
}
}
Android有個很大的坑兄猩,添加以上代碼幾乎可以支持Android的所有版本了,唯獨Android4.4.*不支持鉴未,這里推薦H5和客戶端判斷Android的版本枢冤,采用JS的方式支持inputfile。
webview之JS交互
- Android調(diào)用JS代碼
(1)方法一:采用loadUrl方式
第一步:準備好html靜態(tài)頁面铜秆,寫好JS方法
<!DOCTYPE html>
<html>
<head> <meta charset="utf-8">
<title>Carson_Ho</title> // JS代碼
<script>
function callJS(){
alert("Android調(diào)用了JS的callJS方法");
}
</script>
</head>
</html>
第二步:加載該頁面
mywebview.loadUrl("file:///android_asset/htmldemo.html");
第三步:點擊某按鈕淹真,加載JS代碼
@Override
public void onClick(View v) {
switch (v.getId()){
case R.id.bt_js:
mywebview.loadUrl("javascript:callJS()");
break;
}
}
效果展示:
弊端:會刷新當(dāng)前頁面,執(zhí)行效率慢连茧。
(2)方法二:采用evaluateJavascript方式
其步驟和展示效果和方法一一樣
@Override
public void onClick(View v) {
switch (v.getId()){
case R.id.bt_js:
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
mywebview.evaluateJavascript("javascript:callJS()", new ValueCallback<String>() {
@Override
public void onReceiveValue(String value) {
}
});
}else {
mywebview.loadUrl("javascript:callJS()");
}
break;
}
}
優(yōu)點:不會刷新當(dāng)前頁面
弊端:Andorid4.4之后才能使用(不過現(xiàn)在4.4之前的手機幾乎已被淘汰)
- JS調(diào)用Android代碼
方法一:采用addJavascriptInterface映射
步驟一:定義接口
public class AndoridToJsInterface {
@JavascriptInterface
public void hello(){
System.out.println("hello");
}
}
AndoridToJsInterface 類專門負責(zé)聲明接口核蘸,接口方法必須添加“@JavascriptInterface”修飾,該注解將接口暴露給JS啸驯。其作用是為JS代碼提供調(diào)用的接口客扎。JS代碼想要調(diào)用Android代碼聲明的接口,就必須給JS創(chuàng)建一個映射
//AndroidtoJS類對象映射到j(luò)s的test對象
mywebview.addJavascriptInterface(new AndoridToJsInterface(), "android");
第一個參數(shù)是AndoridToJsInterface 對象坯汤,第二個參數(shù)就是AndoridToJsInterface對象的別名,也就是給JS使用的映射搀愧。
現(xiàn)在開始編寫JS代碼
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Carson</title>
<script>
function callAndroid(){
android.hello();
}
</script>
</head>
<body>
<button type="button" id="button1" onclick="callAndroid()">點擊調(diào)用Android的hello方法</button>
</body>
</html>
優(yōu)點:使用簡單
缺點:注解使Android的接口暴露出來惰聂,是一個高危漏洞
方法二:采用shouldOverrideUrlLoading攔截URl
編寫好JS代碼
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Carson_Ho</title>
<script>
function callAndroid(){
//事先預(yù)定的URI
document.location = "android://webview?name=zhangsan&age=22";
}
</script>
</head>
<body>
<button type="button" id="button1" onclick="callAndroid()">點擊調(diào)用Android代碼</button>
</body>
</html>
其中"android://webview?name=zhangsan&age=22"是事先預(yù)定好的格式,然后在點擊網(wǎng)頁上的按鈕觸發(fā)JS代碼咱筛,并在Android端捕獲
@Override
public boolean shouldOverrideUrlLoading(WebView view, String url) {
Uri uri = Uri.parse(url);
if(uri.getScheme().equals("android") && uri.getAuthority().equals("webview")){
Set<String> paraNames = uri.getQueryParameterNames();
Iterator it = paraNames.iterator();
while (it.hasNext()){
String name = (String) it.next();
Log.d("aaa", "name:"+name);
}
}
return super.shouldOverrideUrlLoading(view,url);
}
優(yōu)點:沒有方法一的安全漏洞
缺點:使用復(fù)雜搓幌,并且將所有的JS處理都放在shouldOverrideUrlLoading中略顯臃腫
方法三:采用onJsAlert、onJsConfirm迅箩、onJsPrompt捕獲URL
和方法二原理類似溉愁,這里就不在描述
@Override
public boolean onJsAlert(WebView view, String url, String message, JsResult result) {
return super.onJsAlert(view, url, message, result);
}
@Override
public boolean onJsConfirm(WebView view, String url, String message, JsResult result) {
return super.onJsConfirm(view, url, message, result);
}
@Override
public boolean onJsPrompt(WebView view, String url, String message, String defaultValue, JsPromptResult result) {
return super.onJsPrompt(view, url, message, defaultValue, result);
}
優(yōu)點:沒有方法一的安全漏洞
缺點:使用復(fù)雜,并且將所有的JS處理都放在onJsAlert饲趋、onJsConfirm拐揭、onJsPrompt中略顯臃腫。
JS交互總結(jié):
- Android調(diào)用JS代碼推薦使用evaluateJavascript奕塑,他的好處顯而易見堂污,既不會重新刷新頁面,也可以獲取返回值
- 三種JS調(diào)用Android代碼的方法都不推薦使用龄砰,addJavascriptInterface映射的方式是一個高危漏洞盟猖,其他兩種使用太過復(fù)雜讨衣,這里推薦使用JsBridge實現(xiàn)JS交互。
最后分享一個github上開源的JsBridge案例: