目錄
一,簡介
二,WebView,WebSettings、WebViewClient、WebChromeClient方法
三,Native與Js交互
四,騰訊瀏覽服務X5內核
五,X5內核WebView +全屏視頻 + JsBridge框架javascript
這部分主要介紹下 WebView,WebView 是一個用來顯示 Web 網頁的控件,繼承自 AbsoluteLayout,和使用系統其餘控件沒什麼區別,只是 WeView 控件方法比較多比較豐富。由於它就是一個微型瀏覽器,包含一個瀏覽器該有的基本功能,例如:滾動、縮放、前進、後退下一頁、搜索、執行 Js等功能。html
在 Android 4.4 以前使用 WebKit 做爲渲染內核,4.4 以後採用 chrome 內核。Api 使用兼容低版本。前端
主要包含 WebView 的使用方法。 咱們基於這些方法能擴展不少其餘功能,例如:JsBridge、緩存等。
WebView方法:html5
方法 | 說明 |
---|---|
void loadUrl(String url) | 加載網絡連接 url |
boolean canGoBack() | 判斷 WebView 當前是否能夠返回上一頁 |
goBack() | 回退到上一頁 |
boolean canGoForward() | 判斷 WebView 當前是否能夠向前一頁 |
goForward() | 回退到前一頁 |
onPause() | 相似 Activity 生命週期,頁面進入後臺不可見狀態 |
pauseTimers() | 方法面向全局整個應用程序的webview,它會暫停全部webview的layout,parsing,JavaScript Timer。當程序進入後臺時,該方法的調用能夠下降CPU功耗。 |
onResume() | 在調用 onPause()後,能夠調用該方法來恢復 WebView 的運行 |
resumeTimers() | 恢復pauseTimers時的全部操做。(注:pauseTimers和resumeTimers 方法必須一塊兒使用,不然再使用其它場景下的 WebView 會有問題) |
destroy() | 銷燬 WebView |
clearHistory() | 清除當前 WebView 訪問的歷史記錄。 |
clearCache(boolean includeDiskFiles): | 清空網頁訪問留下的緩存數據。須要注意的時,因爲緩存是全局的,因此只要是WebView用到的緩存都會被清空,即使其餘地方也會使用到。該方法接受一個參數,從命名便可看出做用。若設爲false,則只清空內存裏的資源緩存,而不清空磁盤裏的。 |
reload() | 從新加載當前請求 |
setLayerType(int layerType, Paint paint) | 設置硬件加速、軟件加速 |
removeAllViews() | 清除子view。 |
clearSslPreferences(): | 清除ssl信息 |
clearMatches() | 清除網頁查找的高亮匹配字符 |
removeJavascriptInterface(String interfaceName) | 刪除interfaceName 對應的注入對象 |
addJavascriptInterface(Object object,String interfaceName) | 注入 java 對象 |
setVerticalScrollBarEnabled(boolean verticalScrollBarEnabled) | 設置垂直方向滾動條。 |
setHorizontalScrollBarEnabled(boolean horizontalScrollBarEnabled) | 設置橫向滾動條 |
loadUrl(String url, Map additionalHttpHeaders) | 加載制定url並攜帶http header數據 |
stopLoading() | 中止 WebView 當前加載 |
freeMemory() | 釋放內存,不過貌似很差用。 |
clearFormData() | 清除自動完成填充的表單數據。須要注意的是,該方法僅僅清除當前表單域自動完成填充的表單數據,並不會清除WebView存儲到本地的數據。 |
WebSettings方法java
方法 | 說明 |
---|---|
setJavaScriptEnabled(boolean flag) | 是否支持 Js 使用 |
setCacheMode(int mode) | 設置 WebView 的緩存模式 |
setAppCacheEnabled(boolean flag) | 是否啓用緩存模式 |
setAppCachePath(String appCachePath) | Android 私有緩存存儲,若是你不調用setAppCachePath方法,WebView將不會產生這個目錄 |
setGeolocationEnabled | 是否支持定位 |
setGeolocationDatabasePath | 設置定位數據庫路徑 |
setSupportZoom(boolean support) | 是否支持縮放。 |
setTextZoom(int textZoom) | 設置頁面中文本縮放百分比,默認100% |
setAllowFileAccess(boolean allow) | 是否容許加載本地 html 文件/false |
setDatabaseEnabled(boolean flag) | 是否開啓數據庫緩存 |
setDomStorageEnabled(boolean flag) | 是否開啓DOM緩存 |
setUserAgentString(String ua) | 設置 UserAgent 屬性 |
setLoadsImagesAutomatically(boolean flag) | 支持自動加載圖片 |
boolean getLoadsImagesAutomatically() | 是否支持自動加載圖片 |
setMixedContentMode | 設置混合模式,當一個安全的https請求加載不安全的http時 |
setLoadWithOverviewMode | 設置webview加載在override模式,就是縮小內容以適應屏幕寬,當加載的內容寬大於Webview控制的寬時 |
setDisplayZoomControls | 設置webview是否顯示縮放控制按鈕 |
項目中使用設置:android
WebSettings settings = mWebView.getSettings();
settings.setJavaScriptEnabled(true);
settings.setDatabaseEnabled(false);
settings.setSaveFormData(false);
settings.setBuiltInZoomControls(true);
if (Build.VERSION.SDK_INT >= 11)
settings.setDisplayZoomControls(false);
settings.setAllowFileAccess(true);
settings.setUseWideViewPort(true);
settings.setLoadWithOverviewMode(true);
settings.setDomStorageEnabled(true);
settings.setAppCacheEnabled(true);
settings.setGeolocationEnabled(true);
settings.setGeolocationDatabasePath(getActivity().getCacheDir().toString());
settings.setUserAgentString(settings.getUserAgentString() + " " + DeviceUtil.getUserAgent(getActivity()));
// android 5.0以上默認不支持Mixed Content
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
settings.setMixedContentMode(
WebSettings.MIXED_CONTENT_COMPATIBILITY_MODE);
}
複製代碼
WebViewClient方法:
主要做用:主要幫助WebView處理各類通知、請求事件的web
方法 | 說明 |
---|---|
onPageStarted(WebView view, String url, Bitmap favicon) | WebView 開始加載頁面時回調,一次Frame加載對應一次回調 |
onLoadResource(WebView view, String url) | WebView 加載頁面資源時會回調,每個資源產生的一次網絡加載,除非本地有當前 url 對應有緩存,不然就會加載 |
shouldInterceptRequest(WebView view, String url) | WebView 能夠攔截某一次的 request 來返回咱們本身加載的數據,這個方法在後面緩存會有很大做用 |
shouldOverrideUrlLoading(WebView view, String url) | 是否在 WebView 內加載頁面,返回true表明當前Webview完成這次加載,false表明交給系統瀏覽器加載 |
onReceivedSslError(WebView view, SslErrorHandler handler, SslError error) | WebView ssl 訪問證書出錯,handler.cancel()取消加載,handler.proceed()對然錯誤也繼續加載 |
onPageFinished(WebView view, String url) | WebView 完成加載頁面時回調,一次Frame加載對應一次回調 |
onReceivedError(WebView view, int errorCode, String description, String failingUrl) | WebView 訪問 url 出錯 |
WebChromeClient方法
要輔助WebView處理Javascript的對話框、網站圖標、網站title、加載進度等chrome
方法 | 說明 |
---|---|
onConsoleMessage(String message, int lineNumber,String sourceID) | 輸出 Web 端日誌 |
onProgressChanged(WebView view, int newProgress) | 當前 WebView 加載網頁進度 |
onJsPrompt(WebView view, String url, String message, String defaultValue, JsPromptResult result) | 處理 JS 中的 Prompt對話框 |
onJsAlert(WebView view, String url, String message, JsResult result) | Js 中調用 alert() 函數,產生的對話框 |
onReceivedTitle(WebView view, String title): | 接收web頁面的 Title |
onReceivedIcon(WebView view, Bitmap icon) | 接收web頁面的icon |
View getVideoLoadingProgressView() | 獲取在全屏視頻正在緩衝的同時顯示的視圖。宿主應用程序能夠重寫此方法,以提供包含旋轉器或相似物的視圖。 |
onShowCustomView(View view, CustomViewCallback callback) | 網頁中有H5播放flash video的時候按下全屏按鈕將會調用到這個方法,通常用做設置網頁播放全屏操做 |
onHideCustomView() | 取消全屏方法 |
首先說Native調用js代碼,或者說執行js代碼很簡單:數據庫
Native 調用 Js:mWebView.loadUrl(js);
複製代碼
Js代碼調用Native的代碼,一共有三種方式:json
標準方式使用清單:
//開啓Js可用
mWebView.getSettings().setJavaScriptEnabled(true);
// 建立要注入的 Java 類
public class NativeInterface {
private Context mContext;
public NativeInterface(Context context) {
mContext = context;
}
// 使用JavascriptInterface註解標註的方法能夠被JS代碼調用
@JavascriptInterface
public void hello() {
Toast.makeText(mContext, "hello", Toast.LENGTH_SHORT).show();
}
@JavascriptInterface
public void hello(String params) {
Toast.makeText(mContext, params, Toast.LENGTH_SHORT).show();
}
@JavascriptInterface
public String getAndroid() {
Toast.makeText(mContext, "getAndroid", Toast.LENGTH_SHORT).show();
return "Android data";
}
}
// WebView 注入便可,第二個參數是注入的java對象的別名
mWebView.addJavascriptInterface(new NativeInterface(this), "AndroidNative");
//Js編寫
<script>
function callHello(){
AndroidNative.hello();
}
function callHello1(){
// 在js代碼中別名AndroidNative就至關於java中實例對象new NativeInterface(this),可調用方法
AndroidNative.hello('hello Android');
}
function callAndroid(){
var temp = AndroidNative.getAndroid();
console.log(temp);
alert(temp);
}
</script>
複製代碼
最經常使用的方式使用清單:
在shouldOverrideUrlLoading
接口下,攔截js回調操做,使用scheme協議,即scheme://host[:post]/path/jsonparams
,雙方定義格式解析
假如 :協定的格式爲 appname://jsbridge/call/func/jsonparams
jsonparams 的是格式爲:
{ "callback" : "js用於區分此操做的標識", // 這一項是必須的
"key1": value1,
"key2": value2,
"key3": value3
……
}
// js獲取userinfo爲例:
// func = userinfo
// jsonparams 的是格式爲:
// { "callback" : "cbUserInfo_1"
// }
@Override
public boolean shouldOverrideUrlLoading(WebView view, String url) {
if (!TextUtils.isEmpty(url) && url.startsWith("appname://jsbridge/call/")) {
String[] values = url.replace("appname://jsbridge/call/", "").split("/", 2);
String func = values[0];
String params = values[1];
// 1,js調用native代碼,處理Native端的邏輯
switch(func) {
case "userinfo":
JSONObject job = new JSONObject(params);
String callback = job.optString("callback"); // 此處是「cbUserInfo_1」
// 客戶端獲取一些信息等等
JSONObject result = new JSONObject();
result.put("isLogin",Config.isLogin());
result.put("nickName", Config.getUserName());
result.put("userId", Config.getUserId());
result.put("headimgUrl", Config.getPicUrl());
/// 等等
// 2, Native調用js代碼將處理的結果返回給js,Js須要用callback區分這次請求
String js = "callback 和 params組成js";
// 這裏須要把callback和params組成js,須要和前端商定,而後使用loadUrl("javascript:" + js) 就能夠將結果傳給js
if (mWebView != null) {
mWebView.loadUrl("javascript:" + js);
}
break;
}
return true;
}
}
複製代碼
以上就是JsBridge的原理,固然,在業務複雜時能夠進行封裝
騰訊瀏覽服務網址:https://x5.tencent.com/
接入指南:https://x5.tencent.com/tbs/guide/sdkInit.html
使用很簡單和系統的WebView同樣。
爲何要更換X5內核
Android 4.4如下web內核是WebKit,4.4 以後採用 chrome 內核,當前端在開發時,會出現前端使用的新特性在低版本安卓手機上不能表現出來,因此須要統一內核
X5內核的技術特性
騰訊瀏覽服務是致力於優化移動端webview體驗的整套解決方案。該方案由SDK、手機QQ瀏覽器X5內核和X5雲端服務組成,解決移動端webview使用過程當中出現的一切問題,優化用戶的瀏覽體驗。同時,騰訊還將持續提供後續的更新和優化,爲開發者提供最新最優秀的功能和服務。
其中,SDK是經過共享使用用戶手機上微信、手機QQ、空間等軟件已經下載好的X5內核,低成本實現對系統webview的替代。該SDK大小隻有200+K,接入時僅需修改幾行代碼。
其中,X5內核相對於系統webview,具備下述明顯優點:
1) 速度快:相比系統webview的網頁打開速度有30+%的提高;
2) 省流量:使用雲端優化技術使流量節省20+%;
3) 更安全:安全問題能夠在24小時內修復;
4) 更穩定:通過億級用戶的使用考驗,CRASH率低於0.15%;
5) 兼容好:無系統內核的碎片化問題,更少的兼容性問題;
6) 體驗優:支持夜間模式、適屏排版、字體設置等瀏覽加強功能;
7) 功能全:在Html五、ES6上有更完整支持;
8) 更強大:集成強大的視頻播放器,支持視頻格式遠多於系統webview;
9) 視頻和文件格式的支持x5內核多於系統內核
10) 防劫持是x5內核的一大亮點
下載TbsSDK,會有不少文檔:
// 注意:代碼中的WebView,均爲騰訊的WebView
import com.tencent.smtt.sdk.WebChromeClient;
import com.tencent.smtt.sdk.WebView;
import com.tencent.smtt.sdk.WebChromeClient;
// X5WebView.java:繼承自騰訊的x5內核的WebView
public class X5WebView extends WebView {
public static final String TAG = X5WebView.class.getSimpleName();
private boolean addedJavascriptInterface;
public X5WebView(Context context) {
super(context);
addedJavascriptInterface = false;
}
public X5WebView(Context context, AttributeSet attributeSet) {
super(context, attributeSet);
addedJavascriptInterface = false;
}
public X5WebView(Context context, AttributeSet attributeSet, int i) {
super(context, attributeSet, i);
addedJavascriptInterface = false;
}
public X5WebView(Context context, AttributeSet attributeSet, int i, boolean b) {
super(context, attributeSet, i, b);
addedJavascriptInterface = false;
}
public X5WebView(Context context, AttributeSet attributeSet, int i, Map<String, Object> map, boolean b) {
super(context, attributeSet, i, map, b);
addedJavascriptInterface = false;
}
@Override
@SuppressLint("SetJavaScriptEnabled")
public void setWebChromeClient(WebChromeClient client) {
getSettings().setJavaScriptEnabled(true);
super.setWebChromeClient(client);
}
@Override
public void loadData(String data, String mimeType, String encoding) {
addJavascriptInterface();
super.loadData(data, mimeType, encoding);
}
@Override
public void loadDataWithBaseURL(String baseUrl, String data,
String mimeType, String encoding,
String historyUrl) {
addJavascriptInterface();
super.loadDataWithBaseURL(baseUrl, data, mimeType, encoding, historyUrl);
}
@Override
public void loadUrl(String url) {
addJavascriptInterface();
super.loadUrl(url);
}
@Override
public void loadUrl(String url, Map<String, String> additionalHttpHeaders) {
addJavascriptInterface();
super.loadUrl(url, additionalHttpHeaders);
}
private void addJavascriptInterface() {
if (!addedJavascriptInterface) {
// Add javascript interface to be called when the video ends (must be done before page load)
addJavascriptInterface(new Object() {
}, "_VideoEnabledWebView"); // Must match Javascript interface name of VideoEnabledWebChromeClient
addedJavascriptInterface = true;
}
}
// X5WebViewClient.java: 繼承自騰訊的x5內核的WebViewClient
public class X5WebViewClient extends WebViewClient {
}
// X5WebChromeClient.java :構建一個全屏顯示Video的WebChromeClient, 網上能夠找到實現,只是作了繼承X5的WebChromeClient
public class X5WebChromeClient extends WebChromeClient implements MediaPlayer.OnPreparedListener, MediaPlayer.OnCompletionListener, MediaPlayer.OnErrorListener {
// 將被隱藏的View
private View willHiddenView;
// 將全屏顯示的ViewGroup
private ViewGroup willDisplayedViewGroup;
// 加載View
private View loadingView;
// 核心的WebView
private X5WebView webView;
// 進度條
private ProgressBar mProgressBar;
// 代表是否在使用自定義View 顯示全屏Video
private boolean isVideoFullscreen;
// 保存全屏Video容器
private FrameLayout videoViewContainer;
// 保存自定義View回調
private IX5WebChromeClient.CustomViewCallback videoViewCallback;
// 全屏Video回調
private ToggledFullscreenCallback toggledFullscreenCallback;
// 接收標題回調
private onReceivedTitleCallback receivedTitleCallback;
/**
* Never use this constructor alone.
* This constructor allows this class to be defined as an inline inner class in which the user can override methods
*/
public X5WebChromeClient() {
}
/**
* 構建一個全屏顯示Video的WebChromeClient
*
* @param willHiddenView 當video全屏時,activity佈局中的應該被隱藏的View
* @param willDisplayedViewGroup activity佈局中,將要顯示的 ViewGroup,特別是你想使此ViewGroup全屏顯示。
*/
public X5WebChromeClient(View willHiddenView, ViewGroup willDisplayedViewGroup) {
this.willHiddenView = willHiddenView;
this.willDisplayedViewGroup = willDisplayedViewGroup;
this.loadingView = null;
this.webView = null;
this.isVideoFullscreen = false;
}
/**
* 構建一個全屏顯示Video的WebChromeClient
*
* @param willHiddenView 當video全屏時,activity佈局中的應該被隱藏的View
* @param willDisplayedViewGroup activity佈局中,將要顯示的 ViewGroup,特別是你想使此ViewGroup全屏顯示
* @param loadingView 加載視頻時顯示的視圖(一般僅在API級別<11時使用)。必須被預先inflate,且無父佈局
*/
public X5WebChromeClient(View willHiddenView, ViewGroup willDisplayedViewGroup, View loadingView) {
this.willHiddenView = willHiddenView;
this.willDisplayedViewGroup = willDisplayedViewGroup;
this.loadingView = loadingView;
this.webView = null;
this.isVideoFullscreen = false;
}
/**
* 構建一個全屏顯示Video的WebChromeClient
*
* @param willHiddenView 當video全屏時,activity佈局中的應該被隱藏的View
* @param willDisplayedViewGroup activity佈局中,將要顯示的 ViewGroup,特別是你想使此ViewGroup全屏顯示
* @param loadingView 加載視頻時顯示的視圖(一般僅在API級別<11時使用)。必須被預先inflate,且無父佈局
* @param webView X5WebView
*
* 經過它將使X5WebChromeClient檢測HTML5視頻結束事件並退出全屏。
* 注意:爲了使HTML5視頻結束事件正常工做,網頁只能包含一個視頻標記。若是須要的話,能夠改進這一點(參見javascript代碼)。
*/
public X5WebChromeClient(View willHiddenView, ViewGroup willDisplayedViewGroup, View loadingView, X5WebView webView) {
this.willHiddenView = willHiddenView;
this.willDisplayedViewGroup = willDisplayedViewGroup;
this.loadingView = loadingView;
this.webView = webView;
this.isVideoFullscreen = false;
}
/**
* 手動 進去全屏時 調用
* @param view
* @param callback
*/
@Override
public void onShowCustomView(View view, IX5WebChromeClient.CustomViewCallback callback) {
if (view instanceof FrameLayout) {
PalLog.d(X5WebView.TAG, "onShowCustomView");
// A video wants to be shown
FrameLayout frameLayout = (FrameLayout) view;
// 焦點View
View focusedChild = frameLayout.getFocusedChild();
// 保存Video相關變量
this.isVideoFullscreen = true;
this.videoViewContainer = frameLayout;
this.videoViewCallback = callback;
// 隱藏non-video的View
willHiddenView.setVisibility(View.GONE);
// 添加video的View並顯示
willDisplayedViewGroup.addView(videoViewContainer, new RelativeLayout.LayoutParams(ActionBar.LayoutParams.MATCH_PARENT, ActionBar.LayoutParams.MATCH_PARENT));
willDisplayedViewGroup.setVisibility(View.VISIBLE);
// 焦點View是VideoView
if (focusedChild instanceof VideoView) {
// VideoView (typically API level <11)
VideoView videoView = (VideoView) focusedChild;
// VideoView設置準備監聽器,完成監聽器,錯誤監聽器
videoView.setOnPreparedListener(this);
videoView.setOnCompletionListener(this);
videoView.setOnErrorListener(this);
} else {
// Usually android.webkit.HTML5VideoFullScreen$VideoSurfaceView, sometimes android.webkit.HTML5VideoFullScreen$VideoTextureView
// HTML5VideoFullScreen (typically API level 11+)
// Handle HTML5 video ended event
if (webView != null && webView.getSettings().getJavaScriptEnabled()) {
// Run javascript code that detects the video end and notifies the interface
String js = "javascript:";
js += "_ytrp_html5_video = document.getElementsByTagName('video')[0];";
js += "if (_ytrp_html5_video !== undefined) {";
{
js += "function _ytrp_html5_video_ended() {";
{
js += "_ytrp_html5_video.removeEventListener('ended', _ytrp_html5_video_ended);";
js += "_VideoEnabledWebView.notifyVideoEnd();"; // Must match Javascript interface name and method of VideoEnableWebView
}
js += "}";
js += "_ytrp_html5_video.addEventListener('ended', _ytrp_html5_video_ended);";
}
js += "}";
webView.loadUrl(js);
}
}
// 最後通知全屏改變
if (toggledFullscreenCallback != null) {
toggledFullscreenCallback.toggledFullscreen(true);
}
}
}
@Override
public void onShowCustomView(View view, int requestedOrientation, IX5WebChromeClient.CustomViewCallback callback) // Only available in API level 14+
{
onShowCustomView(view, callback);
}
/**
* 手動取消全屏時調用
*/
@Override
public void onHideCustomView() {
PalLog.d(X5WebView.TAG, "onHideCustomView");
// This method must be manually (internally) called on video end in the case of VideoView (typically API level <11)
// This method must be manually (internally) called on video end in the case of HTML5VideoFullScreen (typically API level 11+) because it's not always called automatically
// This method must be manually (internally) called on back key press (from this class' onBackPressed() method)
if (isVideoFullscreen) {
// Hide the video view, remove it, and show the non-video view
willDisplayedViewGroup.setVisibility(View.GONE);//播放視頻的
willDisplayedViewGroup.removeView(videoViewContainer);
willHiddenView.setVisibility(View.VISIBLE);
// Call back
if (videoViewCallback != null) {
videoViewCallback.onCustomViewHidden();
}
// Reset video related variables
isVideoFullscreen = false;
videoViewContainer = null;
videoViewCallback = null;
// Notify full-screen change
if (toggledFullscreenCallback != null) {
toggledFullscreenCallback.toggledFullscreen(false);
}
}
}
/**
* Video will start loading, only called in the case of VideoView (typically API level <11)
* @return
*/
@Override
public View getVideoLoadingProgressView() {
if (loadingView != null) {
loadingView.setVisibility(View.VISIBLE);
return loadingView;
} else {
return super.getVideoLoadingProgressView();
}
}
/**
* Video will start playing, only called in the case of VideoView (typically API level <11)
* @param mp
*/
@Override
public void onPrepared(MediaPlayer mp) {
PalLog.d(X5WebView.TAG, "onPrepared");
if (loadingView != null) {
loadingView.setVisibility(View.GONE);
}
}
/**
* Video finished playing, only called in the case of VideoView (typically API level <11)
* @param mp
*/
@Override
public void onCompletion(MediaPlayer mp) {
PalLog.d(X5WebView.TAG, "onCompletion");
onHideCustomView();
}
/**
* Error while playing video, only called in the case of VideoView (typically API level <11)
* @param mp
* @param what
* @param extra
* @return
*/
@Override
public boolean onError(MediaPlayer mp, int what, int extra) {
// By returning false, onCompletion() will be called
return false;
}
/**
* 設置標題回調
* @param callback
*/
public void setOnReceivedTitleCallback(onReceivedTitleCallback callback) {
receivedTitleCallback = callback;
}
/**
* 設置進度條
* @param progressBar
*/
public void setProgressBar(ProgressBar progressBar) {
this.mProgressBar = progressBar;
}
/**
* 指示是否使用自定義視圖(一般爲全屏)顯示視頻
*
* @return true 使用自定義視圖(一般爲全屏)顯示視頻
*/
public boolean isVideoFullscreen() {
return isVideoFullscreen;
}
/**
* 設置全屏切換回調
*
* @param callback 全屏切換回調
*/
public void setOnToggledFullscreen(ToggledFullscreenCallback callback) {
this.toggledFullscreenCallback = callback;
}
@Override
public void onProgressChanged(WebView view, int newProgress) {
super.onProgressChanged(view, newProgress);
if (mProgressBar != null) {
mProgressBar.setProgress(newProgress);
if (newProgress == 100) {
// onPageFinished
mProgressBar.setVisibility(View.GONE);
}
}
}
@Override
public void onReceivedTitle(WebView view, String title) {
super.onReceivedTitle(view, title);
if (receivedTitleCallback != null) {
receivedTitleCallback.onReceivedTitle(title);
}
}
/**
* Notifies the class that the back key has been pressed by the user.
* This must be called from the Activity's onBackPressed(), and if it returns false, the activity itself should handle it. Otherwise don't do anything.
*
* @return Returns true if the event was handled, and false if it is not (video view is not visible)
*/
public boolean onBackPressed() {
if (isVideoFullscreen) {
onHideCustomView();
return true;
} else {
return false;
}
}
@Override
public boolean onJsAlert(WebView view, String url, String message, final JsResult result) {
return true;
}
/**
* 切換全屏,取消全屏時回調
*/
public interface ToggledFullscreenCallback {
public void toggledFullscreen(boolean fullscreen);
}
/**
* 接收標題回調
*/
public interface onReceivedTitleCallback {
public void onReceivedTitle(String title);
}
}
複製代碼
// Native調Js代碼的接口
public interface JSCallBack {
// callback 和 params 是 上面「三,Native與Js交互# 最多見方式的使用清單」
void jsCallback(String callback, String params);
void jsCallback(String callback, Object object);
}
複製代碼
下面重點介紹是Js調用Native代碼的封裝AppJsBridge.java
// IJSBridge是Js調用Native代碼的接口
public interface IJSBridge {
// 見「三,Js與Native交互」
void processJsBridge(String func, String params);
void setJSCallBack(JSCallBack jsCallBack);
}
// AppJsBridge.java 是Js調用Native代碼的封裝
public class AppJSBridge implements IJSBridge {
private static final String TAG = "AppJSBridge";
public static final String KEY_CALLBACK = "callback";
// Activity
private BaseActivity mActivity;
// 實際執行Native代碼的接口
private OnInvokeCallback mOnInvokeCallback;
// 客戶端執行js代碼,把結果傳給js
private JSCallBack mJSCallBack;
//當前頁面的host,用於判斷白名單
private String mCurHost;
//白名單
private List<JSBridgeSpecialBean> jsList;
@Override
public void setJSCallBack(JSCallBack jsCallBack) {
mJSCallBack = jsCallBack;
}
/**
* 是不是白名單
*/
private boolean isJsBridgeSpecial(String func) {
if (jsList == null || mCurHost == null)
return false;
for (JSBridgeSpecialBean jsSpecial : jsList) {
if (jsSpecial.allow == null || jsSpecial.allow.isEmpty()) continue;
if (jsSpecial.domain.equals(JSBridgeSpecialBean.TAG_ALL) ||
mCurHost.endsWith(jsSpecial.domain.toLowerCase())) {
if (jsSpecial.allow.get(0).equals(JSBridgeSpecialBean.TAG_ALL))
return true;
else if (jsSpecial.allow.contains(func))
return true;
}
}
return false;
}
@Override
public void processJsBridge(String func, String params, String callbackId) {
PalLog.d(TAG, "func=" + func);
PalLog.d(TAG, "params=" + params);
if (!isJsBridgeSpecial(func)) {
PalLog.e(TAG, func + " 不屬於白名單,禁止使用該JS接口");
return;
}
switch (func) {
case "deviceInfo": //獲取設備信息
getDeviceInfo(params);
break;
case "userInfo": //獲取用戶信息
getUserInfo(params);
break;
/// 等等其餘case
default:
PalLog.e(TAG, "沒法處理的事件: " + func);
break;
}
}
/**
* 獲取用戶信息
*/
private void getUserInfo(String params) {
if (mJSCallBack == null) return;
try {
// 1,執行Native代碼
JSONObject job = new JSONObject(params);
String callback = job.optString(KEY_CALLBACK);
// 從客戶端獲取一些信息等等
JSONObject result = new JSONObject();
result.put("isLogin", Config.isLogin());
result.put("nickName", Config.getUserName());
result.put("userId", Config.getUserId());
result.put("headimgUrl", Config.getPicUrl());
/// ....
// 2,Native調用Js代碼,將結果回傳給JS
mJSCallBack.jsCallback(callback, result.toString());
} catch (JSONException e) {
e.printStackTrace();
}
}
/**
* 獲取設備信息
*/
private void getDeviceInfo(String params) {
if (mJSCallBack == null) return;
try {
// 1,執行Native代碼
JSONObject job = new JSONObject(params);
String callback = job.optString(KEY_CALLBACK);
// 從客戶端獲取一些信息等等
JSONObject result = new JSONObject();
result.put("os", "Android");
result.put("version", Config.getVersion());
result.put("deviceId", Config.getVersion());
///...
// 2,Native調用Js代碼,將結果回傳給JS
mJSCallBack.jsCallback(callback, result.toString());
} catch (JSONException e) {
e.printStackTrace();
}
}
}
複製代碼