1. 程式人生 > >WebView實用功能與技巧

WebView實用功能與技巧

引子

web?native?這是個爭論了很久的問題。
自從微信開放了更多的JS介面之後,移動web開發重新火了起來,前端程式猿也水漲船高。

毫無疑問,web頁面有諸多優點:

  • 跨平臺:一次開發,可以同時在Android,iOS和Other Phone上執行(美好的願望)
  • 快速迭代,內容統一:內容修改後,不同版本都能展示最新內容。可以避免頻繁的客戶端升級,也無需經過App Store的稽核
  • 語言優勢:龐大JavaScript開發人員,能夠帶來移動端內容的繁榮

本人多年開發研究web與native混合APP,因為這裡面有很多的坑,很有必要把經驗歸納一番。

準備

區分APP和瀏覽器

我們的web頁面是通過哪個應用開啟的呢?這是要解決的第一個問題,這樣才能做到APP與系統瀏覽器的內容差異化。

  • 通過域名
    將不同用途的頁面歸類到不同伺服器或Web專案下,這是最簡單也最笨的方法,如果同一個頁面要在三端上都展示,那麼就要複製3份了
  • 通過元資料
    用meta標籤區分,這樣相對於把檔案做區分,同樣會面臨上面的問題
  • 通過UA標識
    這是web頁面統計訪問終端的品牌和解析度的常用方法
    我會在WebView的預設UA後面,加上自定義的標識,包括APP的標識 (AndroidApp、iPhoneApp)、應用包名APP的版本號
    判斷是否微信瀏覽器就可以使用這個方法,同時最好檢查是否載入了微信自定義的weixin.js檔案

配置WebView基礎引數

final WebSettings settings = getSettings();
//  允許JS彈出提示框
settings.setJavaScriptCanOpenWindowsAutomatically(true);
//  允許web執行JS
settings.setJavaScriptEnabled(true);
//  設定瀏覽器標識
settings.setUserAgentString(buildAppUserAgent(getContext(), settings.getUserAgentString()));
//  是否支援縮放,預設不支援(看起來更native)
settings.setSupportZoom(false); settings.setBuiltInZoomControls(false); // 開啟H5的離線快取 settings.setAppCacheEnabled(true); final String cachedir = getContext().getDir("cache", Context.MODE_PRIVATE).getPath(); settings.setAppCachePath(cachedir); // 開啟H5的Dom Storage(localStorage,sessionStorage) settings.setDomStorageEnabled(true); // 如果要使用離線快取和DomStorage必須要設定web database final String dbdir = getContext().getDir("database", Context.MODE_PRIVATE).getPath(); settings.setDatabaseEnabled(true); settings.setDatabasePath(dbdir);

WebViewClient物件

監聽頁面載入情況(開始載入、資源載入、完成載入、頁面錯誤)

通常情況下,我們都會有一個loading介面覆蓋在webview上面,當頁面載入完成隱藏loading。
這裡存在2個小問題:

  • webview只有當所有內容都載入完成後,才會回撥onPageFinished。
    也就是說,當DOM元素載入完成,並且所有圖片也載入完成才會觸發,這對於追求體驗的移動APP來說,顯然無法接受。尤其是當網路不好或圖片太大時尤為明顯,使用者看到的是明明頁面已經基本出來了,卻仍然在loading,無法操作。
    其實JS端可以監聽到DOMContentLoaded事件,此時是關閉loading的最佳時機
  • 出現載入錯誤時,不僅會呼叫onReceivedError,仍然會呼叫onPageFinished
    有些人喜歡把loading介面和error介面寫在同一個layout裡,出現錯誤時顯示error,完成載入時隱藏整個layout。
    這對於普通頁面來說沒有問題,但是對於webview就會出現error無法被顯示的情況。
    所以最好將loading和error分開,在onPageFinished時,只需要隱藏loading部分。

攔截頁面連結

重寫shouldOverrideUrlLoading方法,當返回值為true時,需要自己處理url請求,webview將不會插手。
此方法只在涉及頁面跳轉時被觸發。
這裡存在2個問題:

  • 低版本的某些請求不會觸發此方法,直接在本webview打開了新頁面。
    如果想攔截,則需要在onPageStarted方法裡判斷url是否已經改變,更改了的話就表明打開了新的連結。
  • 頁面重定向無法識別
    當頁面存在重定向時,API並沒有提供方法作區分,這時需要自己處理。

替換載入內容

重寫shouldInterceptRequest方法,可以替換JS、CSS、img等內容。
建立WebResourceResponse物件,並傳入檔案輸入流,即可用其他資源替換本來要載入的內容。
通過此方法,我們可以預先下載頁面所需的JS和CSS檔案,儲存到本地;然後當開啟頁面時,直接使用本地已下載的檔案。這樣可以大幅提高頁面載入速度。
注:此方法只能API Level 11以後使用,低版本可通過ContentProvider實現類似功能。

WebChromeClient物件

WebViewClient類主要設計連結和資源載入的功能實現
WebChromeClient類則會涉及更底層的內容,如控制檯除錯、JS彈出框、顯示自定義view等。

替換Alert對話方塊

web頁面一般會通過alert方法,顯示一些提示資訊,但是對話方塊的樣式卻因不同的品牌差異很大,為了使我們的APP保持統一風格,有必要替換成我們自己設計的對話方塊。
重寫onJsAlert方法,將message顯示到Dialog。
該方法是模態的,必須返回內容才能關閉對話方塊,呼叫JsResult.cancel或者JsResult.confirm。如果只調用了Dialog.dismiss而沒有呼叫JsResult的方法,會出現,雖然對話方塊消失,但是執行緒一直處於阻塞狀態,造成假死現象,無法進行任何操作。

全屏播放視訊

webview預設不能全屏播放,需要client端提供全屏的window。
程式碼如下:

@Override
public void onShowCustomView (View view, WebChromeClient.CustomViewCallback callback) {
    mCustomViewCallback = callback;
    android.view.Window window = WebActivity.this.getWindow();
    window.setFlags(
            android.view.WindowManager.LayoutParams.FLAG_FULLSCREEN,
            android.view.WindowManager.LayoutParams.FLAG_FULLSCREEN);
    setRequestedOrientation(android.content.pm.ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);
    mTitleBar.setVisibility(View.INVISIBLE);
    mWebView.setVisibility(View.INVISIBLE);
    mExitFullscreenBtn.setVisibility(View.VISIBLE);
    mVideoViewContainer.setVisibility(View.VISIBLE);
    mVideoViewContainer.addView(view);
}

@Override
public void onHideCustomView () {
    mCustomViewCallback.onCustomViewHidden();
    android.view.Window window = WebActivity.this.getWindow();
    window.setFlags(
            0,
            android.view.WindowManager.LayoutParams.FLAG_FULLSCREEN);
    setRequestedOrientation(android.content.pm.ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
    mTitleBar.setVisibility(View.VISIBLE);
    mWebView.setVisibility(View.VISIBLE);
    mExitFullscreenBtn.setVisibility(View.INVISIBLE);
    mVideoViewContainer.setVisibility(View.INVISIBLE);
    mVideoViewContainer.removeAllViews();
}

Cookie

如果web端需要使用者登入的操作,那麼就涉及到native和web端同步登入狀態,這就需要用到cookie。

//  開啟Cookie支援
CookieSyncManager.createInstance(this);
CookieManager cookieManager = CookieManager.getInstance();
cookieManager.setAcceptCookie(true);
//  設定cookie
sBuff.append(key).append("=").append(value);
sBuff.append("; path=/");
sBuff.append("; domain=").append(domain);
cookieManager.setCookie(url, sBuff.toString());
......
CookieSyncManager.getInstance().sync();
//  刪除cookie
//  清除過期的cookie
cookieManager.removeExpiredCookie();
//  清除所有cookie
cookieManager.removeAllCookie();

因為並不存在單獨刪除cookie的某個欄位的方法,所有要清除某個欄位,要先將其設為已過期,再呼叫removeExpiredCookie

如何同步登入狀態

簡單的情況,只需要有userid即可認為已經登入,分如下兩種情況:
- 先從native登入
native端呼叫登入介面,拿到useid後,當需要開啟web時,在loadUrl之前,將userid儲存到cookie中,服務端就會從cookie中讀出userid。
- 先從web端登入
web登入後,webview會自動儲存cookie。web端需要與native端定義介面,將username和userid通知給native端,native儲存起來。

Web端與Native端互相通訊

WebView呼叫web端JS方法

mWebView.loadUrl("javascript:JSMethod()");

API 19以後,提供了更加便捷的方法,可以直接獲取JS的返回值。(iOS本身已經提供類似API)

WebView.evaluateJavascript (String script, ValueCallback<String> resultCallback)

web端呼叫native程式碼

早期API提供的方法:

WebView.addJavascriptInterface(Object object, String name)
WebView會將object注入到web端的window物件中,name是object物件定義的方法,web端通過object.name即可呼叫native端的功能。

but,這個方法存在安全漏洞 漏洞詳細說明
如果你的targetSdkVersion>=17,那麼必須將java方法加上註解@JavascriptInterface,否則web端是無法呼叫的。

自定義scheme

通過自定義scheme的方式,在shouldOverrideUrlLoading攔截,並定向到相應的native邏輯。
iOS端需通過此方法實現。

利用WebChromeClient.onJsPrompt

因為JS端很少使用prompt(一般使用alert)方法,所以我們可以利用這個方法,通過自定義協議格式,來實現web與native端的通訊。
因為此方法需要返回JsResult物件,所以利用此機制,可以實現方法的同步呼叫(web端可以獲取到介面方法的返回值,有利於程式碼和邏輯的簡化)
大名鼎鼎PhoneGap(現在叫Cordova)就是利用此方法實現的web與native通訊。

我會專門寫一篇,我是如何實現web與native通訊的。

其他技巧

遮蔽長按事件

重寫performLongClick
或者setOnLongClickListener實現空的listener

隱藏選擇框

所有標籤加上onclick事件後都會出現邊框或背景
可以使用全域性的一個CSS
*{-webkit-tap-highlight-color: rgba(0, 0, 0, 0);}

縮放按鈕引起的崩潰

在某些機型上,當顯示webview的縮放按鈕時,退出Activity,就會報如下錯誤:

android.view.WindowLeaked: Activity com.secoo.activity.web.WebActivity has leaked window android.widget.ZoomButtonsController$Container{438e8248 V.E..... ........ 0,0-1536,194} that was originally added here
    at android.view.ViewRootImpl.<init>(ViewRootImpl.java:382)
    at android.view.WindowManagerGlobal.addView(WindowManagerGlobal.java:261)
    at android.view.WindowManagerImpl.addView(WindowManagerImpl.java:76)
    at android.widget.ZoomButtonsController.setVisible(ZoomButtonsController.java:371)
    ......

這是因為按鈕的隱藏是延遲觸發,在activity退出之後,造成了window的洩露。

解決方法:

//  API 11之後,不顯示縮放按鈕
WebView.getSettings().setDisplayZoomControls(false);
//  API 11之前,在finish時,從view層級中刪除webview
ViewGroup viewgroup = (ViewGroup)(mWebView.getParent());
viewgroup.removeView(mWebView);

技術交流請留言…
To Be Continue…

相關推薦

WebView實用功能技巧

引子 web?native?這是個爭論了很久的問題。 自從微信開放了更多的JS介面之後,移動web開發重新火了起來,前端程式猿也水漲船高。 毫無疑問,web頁面有諸多優點: 跨平臺:一次開發,可以同時在Android,iOS和Other Phone上執

五分鐘搞定Bash功能使用技巧

一個完整計算機的體系結構包括:硬體與軟體,而軟體又分為系統軟體與應用軟體,負責對硬體僅需管理與操作的是系統軟體的核心部分,使用者是無法與硬體或核心打交道的,使用者通過應用程式或部分系統軟體發出指令(可能是通過瀏覽器傳送一封郵件),這些指令會被翻譯並傳給核心,核心在得知使用者的需求後排程硬體資

利用canvas陰影功能雙線技巧繪製軌道交通大屏專案效果

# 利用canvas陰影功能與雙線技巧繪製軌道交通大屏專案效果 ## 前言 近日公司接到一個軌道系統的需求,需要將地鐵線路及列車實時位置展示在大屏上。既然是大屏專案,那視覺效果當然是第一重點,咱們可以先來看看專案完成後的效果圖。 ![line.gif](https://p3-juejin.byteimg.c

Numpy - Pandas - Matplot 功能函數名 速查

返回 隨機數組 -- 函數名 基本 隨機數 速查 apply val 用Python做數據分析,涉及到的函數實在是太多了,容易忘記,去網上查中文基本上差不到,英文有時候描述不清楚問題。 這裏搞個針對個人習慣的函數匯總速查手冊,下次需要用一個什麽功能,就在這裏面查到對應的函數

jquery部分實用功能

視頻播放 itl ngs ide one select bmi 取消 input ‘‘‘ 主要目的是回顧相關jquery部分功能 ‘‘‘ <!DOCTYPE html><html lang="en"><head> <met

使用CGLIB實現AOP功能AOP概念解釋

spa 責任 構建 tca ebe invoke urn img use 使用CGLIB實現AOP功能 在Java裏面,我們要產生某個對象的代理對象,這個對象必須要有一個特點,即這個對象必須實現一個接口,動態代理技術只能基於接口進行代理。有時候我們在做開發的時候,這個對象

Android開發系列(二十四):Notification的功能使用方法

font _id when ice extends 開發 content androi mark 關於消息的提示有兩種:一種是Toast,一種就是Notification。前者維持的時間比較短暫,後者維持的時間比較長。 並且我們尋常手機的應用比方網易、貼吧等等都有非常多

分頁功能 分類查詢功能合並

nat hidden 後臺 item ble fas turn items where 功能的合並 首要的就是要找出兩個功能的共同點。這裏主要是用Response展示數據,通過tsql語句查詢的集合作為數據源,所以這裏將兩個tsql合並,來實現功能的合並。 面板界面代碼:

【轉】WebView的JavaScript本地代碼三種交互方式

添加 lba 文件 abr cli 漏洞 大致 execution span WebView的漏洞分析 漏洞產生的原因 最近在開發過程中遇到一個問題,就是 WebView 使用的時候,還是需要解決之前系統(4.2之前)導致的一個漏洞,雖然現在這個系統版本用戶很少了,但是也不

Iterm2實用功能1:顯示時間線

功能 div .com cnblogs iterm2 img es2017 mage nbsp 快捷鍵:command + shift + e Iterm2實用功能1:顯示時間線

Cisco 的基本配置實例之五----交換機的路由功能DHCP 功能

sign 網關 tin enter com -- config cisco assigned 5、配置交換機的路由功能 說明:只有在三層交換機上才有路由功能,其他的二層接入交換機要想在不同的vlan之間傳送數據需要通過trunk口到核心交換機上進行完路由交換後才可以。

StringUtils的實用功能

lan bsp subst tween mon name common 獲取 string 1、獲取"GET /topic/1410-5813 -"中 1410-5813 import org.apache.commons.lang3.StringUtils; String

flask登陸功能權限

action down 邏輯 self 技術分享 contact request eth ews from datetime import datetime from app import db # 會員 class User(db.Model): __tab

C#WebBrowser控件使用教程技巧收集

運算 gre OS 延遲執行 progress pch parent event ieda 常用屬性和方法 Navigate(string urlString):瀏覽urlString表示的網址 Navigate(System.Uri url):瀏覽url表示的

js判斷奇偶數實現隱藏顯示功能 css立體按鈕

border ng- con position otto erb white adc JD hello! 好久不見了 ,今天也沒準備什麽技術,知識想和大家就見個面,一個js判斷奇數偶數來實現css樣式 ,感覺最大的用途就是頁面的導航。就這麽一個小小的

DockPanel的使用技巧

個人 part images visible bsp foreach using 大致 會計 DockPanel的使用 1.建立一個WinForm工程,默認生成了一個WinForm窗體Form1。 2.引用—>添加引用—>瀏覽—>weiF

maven使用技巧

pos setting 文件 true depend CI AS central 測試 1、Pom文件介紹與基本組成   說明:全稱是Project Object Model,通俗點的話說就是要對構建的項目進行建模。   組成的基本元素: 元素 描述 modelV

bash常用命令技巧

Bash特性簡單介紹一、bash介紹: bash是一個為GNU計劃編寫的Unix shell,它的名字是一系列縮寫:Bourne-Again SHell。 Bash是許多Linux發行版默認的Shell。還有許多其他傳統UNIX上用的Shell,如:tcsh、csh、ash、bsh、ksh等。二、bas

Python爬蟲基礎技巧

修改 request對象 enc 語言 是我 res 加密 firefox int 基於Python2.71 基本抓取網頁get 方法import urllib2url = "http://www.baidu.com"response = urllib2

linux內部路由轉發功能11選5平臺搭建

file 配置 cat see con linux control enabled 它的 一組內部機器需要連接外網,11選5平臺搭建(企 娥:217 1793 408)則需要找一臺機器,即可以連接外網,內網機器也可以連接。 因此將這臺外網機器當做路由器,做路由轉發 1、在需