webview.loadUrl("http://www.baidu.com/"); //加載web資源
//webView.loadUrl("file:///android_asset/example.html"); //加載本地資源
//這個時候發現一個問題,啓動應用後,自動的打開了系統內置的瀏覽器,解決這個問題須要爲webview設置 WebViewClient,並重寫方法:
webview.setWebViewClient(new WebViewClient(){
@Override
public boolean shouldOverrideUrlLoading(WebView view, String url) {
view.loadUrl(url);
//返回值是true的時候控制去WebView打開,爲false調用系統瀏覽器或第三方瀏覽器
return true;
}
//還能夠重寫其餘的方法
});
複製代碼
//java
//調用無參方法
mWebView.loadUrl("javascript:callByAndroid()");
//調用有參方法
mWebView.loadUrl("javascript:showData(" + result + ")");
//javascript,下面是對應的js代碼
<script type="text/javascript">
function showData(result){
alert("result"=result);
return "success";
}
function callByAndroid(){
console.log("callByAndroid")
showElement("Js:無參方法callByAndroid被調用");
}
</script>
複製代碼
if (Build.VERSION.SDK_INT < 18) {
mWebView.loadUrl(jsStr);
} else {
mWebView.evaluateJavascript(jsStr, new ValueCallback<String>() {
@Override
public void onReceiveValue(String value) {
//此處爲 js 返回的結果
}
});
}
複製代碼
mWebView.getSettings().setJavaScriptEnabled(true);
複製代碼
public class JSObject {
private Context mContext;
public JSObject(Context context) {
mContext = context;
}
@JavascriptInterface
public String showToast(String text) {
Toast.show(mContext, text, Toast.LENGTH_SHORT).show();
return "success";
}
/**
* 前端代碼嵌入js:
* imageClick 名應和js函數方法名一致
*
* @param src 圖片的連接
*/
@JavascriptInterface
public void imageClick(String src) {
Log.e("imageClick", "----點擊了圖片");
}
/**
* 網頁使用的js,方法無參數
*/
@JavascriptInterface
public void startFunction() {
Log.e("startFunction", "----無參");
}
}
//特定版本下會存在漏洞
mWebView.addJavascriptInterface(new JSObject(this), "yc逗比");
複製代碼
function showToast(){
var result = myObj.showToast("我是來自web的Toast");
}
function showToast(){
myObj.imageClick("圖片");
}
function showToast(){
myObj.startFunction();
}
複製代碼
public boolean shouldOverrideUrlLoading(WebView view, String url) {
//假定傳入進來的 url = "js://openActivity?arg1=111&arg2=222",表明須要打開本地頁面,而且帶入相應的參數
Uri uri = Uri.parse(url);
String scheme = uri.getScheme();
//若是 scheme 爲 js,表明爲預先約定的 js 協議
if (scheme.equals("js")) {
//若是 authority 爲 openActivity,表明 web 須要打開一個本地的頁面
if (uri.getAuthority().equals("openActivity")) {
//解析 web 頁面帶過來的相關參數
HashMap<String, String> params = new HashMap<>();
Set<String> collection = uri.getQueryParameterNames();
for (String name : collection) {
params.put(name, uri.getQueryParameter(name));
}
Intent intent = new Intent(getContext(), MainActivity.class);
intent.putExtra("params", params);
getContext().startActivity(intent);
}
//表明應用內部處理完成
return true;
}
return super.shouldOverrideUrlLoading(view, url);
}
複製代碼
function openActivity(){
document.location = "js://openActivity?arg1=111&arg2=222";
}
複製代碼
//java
mWebView.loadUrl("javascript:returnResult(" + result + ")");
//javascript
function returnResult(result){
alert("result is" + result);
}
複製代碼
@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) {
//假定傳入進來的 message = "js://openActivity?arg1=111&arg2=222",表明須要打開本地頁面,而且帶入相應的參數
Uri uri = Uri.parse(message);
String scheme = uri.getScheme();
if (scheme.equals("js")) {
if (uri.getAuthority().equals("openActivity")) {
HashMap<String, String> params = new HashMap<>();
Set<String> collection = uri.getQueryParameterNames();
for (String name : collection) {
params.put(name, uri.getQueryParameter(name));
}
Intent intent = new Intent(getContext(), MainActivity.class);
intent.putExtra("params", params);
getContext().startActivity(intent);
//表明應用內部處理完成
result.confirm("success");
}
return true;
}
return super.onJsPrompt(view, url, message, defaultValue, result);
}
複製代碼
function clickprompt(){
var result=prompt("js://openActivity?arg1=111&arg2=222");
alert("open activity " + result);
}
複製代碼
//清除網頁訪問留下的緩存
//因爲內核緩存是全局的所以這個方法不只僅針對webview而是針對整個應用程序.
Webview.clearCache(true);
//清除當前webview訪問的歷史記錄//只會webview訪問歷史記錄裏的全部記錄除了當前訪問記錄
Webview.clearHistory();
//這個api僅僅清除自動完成填充的表單數據,並不會清除WebView存儲到本地的數據
Webview.clearFormData();
複製代碼
<activity
android:name=".X5WebViewActivity"
android:configChanges="orientation|screenSize"
android:hardwareAccelerated="true"
android:launchMode="singleTask"
android:screenOrientation="portrait"
android:theme="@style/Theme.AppCompat.Light.NoActionBar">
<!--須要添加下面的intent-filter配置-->
<intent-filter tools:ignore="AppLinkUrlError">
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<!--使用http,則只能打開http開頭的網頁-->
<data android:scheme="https" />
</intent-filter>
</activity>
複製代碼
public class X5WebViewActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_web_view);
getIntentData();
initTitle();
initWebView();
webView.loadUrl(mUrl);
// 處理 做爲三方瀏覽器打開傳過來的值
getDataFromBrowser(getIntent());
}
/**
* 使用singleTask啓動模式的Activity在系統中只會存在一個實例。
* 若是這個實例已經存在,intent就會經過onNewIntent傳遞到這個Activity。
* 不然新的Activity實例被建立。
*/
@Override
protected void onNewIntent(Intent intent) {
super.onNewIntent(intent);
getDataFromBrowser(intent);
}
/**
* 做爲三方瀏覽器打開傳過來的值
* Scheme: https
* host: www.jianshu.com
* path: /p/yc
* url = scheme + "://" + host + path;
*/
private void getDataFromBrowser(Intent intent) {
Uri data = intent.getData();
if (data != null) {
try {
String scheme = data.getScheme();
String host = data.getHost();
String path = data.getPath();
String text = "Scheme: " + scheme + "\n" + "host: " + host + "\n" + "path: " + path;
Log.e("data", text);
String url = scheme + "://" + host + path;
webView.loadUrl(url);
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
複製代碼
/**
* 當縮放改變的時候會調用該方法
* @param view view
* @param oldScale 以前的縮放比例
* @param newScale 如今縮放比例
*/
@Override
public void onScaleChanged(WebView view, float oldScale, float newScale) {
super.onScaleChanged(view, oldScale, newScale);
//視頻全屏播放按返回頁面被放大的問題
if (newScale - oldScale > 7) {
//異常放大,縮回去。
view.setInitialScale((int) (oldScale / newScale * 100));
}
}
複製代碼
//初始化的時候設置,具體代碼在X5WebView類中
if(Build.VERSION.SDK_INT >= KITKAT) {
//設置網頁在加載的時候暫時不加載圖片
ws.setLoadsImagesAutomatically(true);
} else {
ws.setLoadsImagesAutomatically(false);
}
/**
* 當頁面加載完成會調用該方法
* @param view view
* @param url url連接
*/
@Override
public void onPageFinished(WebView view, String url) {
super.onPageFinished(view, url);
//頁面finish後再發起圖片加載
if(!webView.getSettings().getLoadsImagesAutomatically()) {
webView.getSettings().setLoadsImagesAutomatically(true);
}
}
複製代碼
/**
* 請求網絡出現error
* @param view view
* @param errorCode 錯誤🐎
* @param description description
* @param failingUrl 失敗連接
*/
@Override
public void onReceivedError(WebView view, int errorCode, String description, String
failingUrl) {
super.onReceivedError(view, errorCode, description, failingUrl);
if (errorCode == 404) {
//用javascript隱藏系統定義的404頁面信息
String data = "Page NO FOUND!";
view.loadUrl("javascript:document.body.innerHTML=\"" + data + "\"");
} else {
if (webListener!=null){
webListener.showErrorView();
}
}
}
// 向主機應用程序報告Web資源加載錯誤。這些錯誤一般代表沒法鏈接到服務器。
// 值得注意的是,不一樣的是過期的版本的回調,新的版本將被稱爲任何資源(iframe,圖像等)
// 不只爲主頁。所以,建議在回調過程當中執行最低要求的工做。
// 6.0 以後
@Override
public void onReceivedError(WebView view, WebResourceRequest request, WebResourceError error) {
super.onReceivedError(view, request, error);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
X5WebUtils.log("服務器異常"+error.getDescription().toString());
}
//ToastUtils.showToast("服務器異常6.0以後");
//當加載錯誤時,就讓它加載本地錯誤網頁文件
//mWebView.loadUrl("file:///android_asset/errorpage/error.html");
if (webListener!=null){
webListener.showErrorView();
}
}
/**
* 這個方法主要是監聽標題變化操做的
* @param view view
* @param title 標題
*/
@Override
public void onReceivedTitle(WebView view, String title) {
super.onReceivedTitle(view, title);
if (title.contains("404") || title.contains("網頁沒法打開")){
if (webListener!=null){
webListener.showErrorView();
}
} else {
// 設置title
}
}
複製代碼
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
webview.setLayerType(View.LAYER_TYPE_SOFTWARE, null);
}
複製代碼
/**
* 在加載資源時通知主機應用程序發生SSL錯誤
* 做用:處理https請求
* @param view view
* @param handler handler
* @param error error
*/
@Override
public void onReceivedSslError(WebView view, SslErrorHandler handler, SslError error) {
super.onReceivedSslError(view, handler, error);
if (error!=null){
String url = error.getUrl();
X5WebUtils.log("onReceivedSslError----異常url----"+url);
}
//https忽略證書問題
if (handler!=null){
//表示等待證書響應
handler.proceed();
// handler.cancel(); //表示掛起鏈接,爲默認方式
// handler.handleMessage(null); //可作其餘處理
}
}
複製代碼
@Override
protected void onDestroy() {
try {
//有音頻播放的web頁面的銷燬邏輯
//在關閉了Activity時,若是Webview的音樂或視頻,還在播放。就必須銷燬Webview
//可是注意:webview調用destory時,webview仍綁定在Activity上
//這是因爲自定義webview構建時傳入了該Activity的context對象
//所以須要先從父容器中移除webview,而後再銷燬webview:
if (webView != null) {
ViewGroup parent = (ViewGroup) webView.getParent();
if (parent != null) {
parent.removeView(webView);
}
webView.removeAllViews();
webView.destroy();
webView = null;
}
} catch (Exception e) {
Log.e("X5WebViewActivity", e.getMessage());
}
super.onDestroy();
}
複製代碼
DNS
connection
服務器處理
複製代碼
@Override
public void onPageStarted(WebView view, String url, Bitmap favicon) {
super.onPageStarted(view, url, favicon);
String host = Uri.parse(url).getHost();
LoggerUtils.i("host:" + host);
if (!BuildConfig.IS_DEBUG) {
if (Arrays.binarySearch(domainList, host) < 0) {
//不在白名單內,非法網址,這個時候給用戶強烈而明顯的提示
} else {
//合法網址
}
}
}
複製代碼
//在onStop裏面設置setJavaScriptEnabled(false);
//在onResume裏面設置setJavaScriptEnabled(true)。
@Override
protected void onResume() {
super.onResume();
if (mWebView != null) {
mWebView.getSettings().setJavaScriptEnabled(true);
}
}
@Override
protected void onStop() {
super.onStop();
if (mWebView != null) {
mWebView.getSettings().setJavaScriptEnabled(false);
}
}
複製代碼
//正確
pb.setVisibility(View.VISIBLE);
mWebView.loadUrl("https://github.com/yangchong211/LifeHelper");
//不太好
@Override
public void onPageStarted(WebView webView, String s, Bitmap bitmap) {
super.onPageStarted(webView, s, bitmap);
//設定加載開始的操做
pb.setVisibility(View.VISIBLE);
}
//下面這個是監聽進度條進度變化的邏輯
mWebView.getX5WebChromeClient().setWebListener(interWebListener);
mWebView.getX5WebViewClient().setWebListener(interWebListener);
private InterWebListener interWebListener = new InterWebListener() {
@Override
public void hindProgressBar() {
pb.setVisibility(View.GONE);
}
@Override
public void showErrorView() {
}
@Override
public void startProgress(int newProgress) {
pb.setProgress(newProgress);
}
@Override
public void showTitle(String title) {
}
};
複製代碼
/設置是否開啓密碼保存功能,不建議開啓,默認已經作了處理,存在盜取密碼的危險
mX5WebView.setSavePassword(false);
複製代碼
webView.loadData(htmlString, "text/html", "utf-8");
webView.setWebViewClient(new WebViewClient() {
public void onPageFinished(WebView view, String url) {
super.onPageFinished(view, url);
Log.d("yc", view.getContentheight() + "");
}
});
複製代碼
webView.setWebViewClient(new WebViewClient() {
@Override
public void onPageFinished(WebView view, String url) {
super.onPageFinished(view, url);
webView.postDelayed(new Runnable() {
@Override
public void run() {
int contentHeight = webView.getContentHeight();
int viewHeight = webView.getHeight();
}
}, 500);
}
});
複製代碼
/**
* 只有在主頁面加載出現錯誤時,纔會回調這個方法。這正是展現加載錯誤頁面最合適的方法。
* 然而,若是無論三七二十一直接展現錯誤頁面的話,那頗有可能會誤判,給用戶形成常常加載頁面失敗的錯覺。
* 因爲不一樣的WebView實現可能不同,因此咱們首先須要排除幾種誤判的例子:
* 1.加載失敗的url跟WebView裏的url不是同一個url,排除;
* 2.errorCode=-1,代表是ERROR_UNKNOWN的錯誤,爲了保證不誤判,排除
* 3failingUrl=null&errorCode=-12,因爲錯誤的url是空而不是ERROR_BAD_URL,排除
* @param webView webView
* @param errorCode errorCode
* @param description description
* @param failingUrl failingUrl
*/
@Override
public void onReceivedError(WebView webView, int errorCode,
String description, String failingUrl) {
super.onReceivedError(webView, errorCode, description, failingUrl);
// -12 == EventHandle.ERROR_BAD_URL, a hide return code inside android.net.http package
if ((failingUrl != null && !failingUrl.equals(webView.getUrl())
&& !failingUrl.equals(webView.getOriginalUrl())) /* not subresource error*/
|| (failingUrl == null && errorCode != -12) /*not bad url*/
|| errorCode == -1) { //當 errorCode = -1 且錯誤信息爲 net::ERR_CACHE_MISS
return;
}
if (!TextUtils.isEmpty(failingUrl)) {
if (failingUrl.equals(webView.getUrl())) {
//作本身的錯誤操做,好比自定義錯誤頁面
}
}
}
/**
* 只有在主頁面加載出現錯誤時,纔會回調這個方法。這正是展現加載錯誤頁面最合適的方法。
* 然而,若是無論三七二十一直接展現錯誤頁面的話,那頗有可能會誤判,給用戶形成常常加載頁面失敗的錯覺。
* 因爲不一樣的WebView實現可能不同,因此咱們首先須要排除幾種誤判的例子:
* 1.加載失敗的url跟WebView裏的url不是同一個url,排除;
* 2.errorCode=-1,代表是ERROR_UNKNOWN的錯誤,爲了保證不誤判,排除
* 3failingUrl=null&errorCode=-12,因爲錯誤的url是空而不是ERROR_BAD_URL,排除
* @param webView webView
* @param webResourceRequest webResourceRequest
* @param webResourceError webResourceError
*/
@Override
public void onReceivedError(WebView webView, WebResourceRequest webResourceRequest,
WebResourceError webResourceError) {
super.onReceivedError(webView, webResourceRequest, webResourceError);
}
/**
* 任何HTTP請求產生的錯誤都會回調這個方法,包括主頁面的html文檔請求,iframe、圖片等資源請求。
* 在這個回調中,因爲混雜了不少請求,不適合用來展現加載錯誤的頁面,而適合作監控報警。
* 當某個URL,或者某個資源收到大量報警時,說明頁面或資源可能存在問題,這時候可讓相關運營及時響應修改。
* @param webView webView
* @param webResourceRequest webResourceRequest
* @param webResourceResponse webResourceResponse
*/
@Override
public void onReceivedHttpError(WebView webView, WebResourceRequest webResourceRequest,
WebResourceResponse webResourceResponse) {
super.onReceivedHttpError(webView, webResourceRequest, webResourceResponse);
}
/**
* 任何HTTPS請求,遇到SSL錯誤時都會回調這個方法。
* 比較正確的作法是讓用戶選擇是否信任這個網站,這時候能夠彈出信任選擇框供用戶選擇(大部分正規瀏覽器是這麼作的)。
* 有時候,針對本身的網站,可讓一些特定的網站,無論其證書是否存在問題,都讓用戶信任它。
* 坑:有時候部分手機打開頁面報錯,絕招:讓本身網站的全部二級域都是可信任的。
* @param webView webView
* @param sslErrorHandler sslErrorHandler
* @param sslError sslError
*/
@Override
public void onReceivedSslError(WebView webView, SslErrorHandler sslErrorHandler, SslError sslError) {
super.onReceivedSslError(webView, sslErrorHandler, sslError);
//判斷網站是不是可信任的,與本身網站host做比較
if (WebViewUtils.isYCHost(webView.getUrl())) {
//若是是本身的網站,則繼續使用SSL證書
sslErrorHandler.proceed();
} else {
super.onReceivedSslError(webView, sslErrorHandler, sslError);
}
}
複製代碼
<script type="text/javascript">
var tables = document.getElementsByTagName("img"); //找到table標籤
for(var i = 0; i<tables.length; i++){ // 逐個改變
tables[i].style.width = "100%"; // 寬度改成100%
tables[i].style.height = "auto";
}
</script>
複製代碼
// 網頁內容的寬度是否可大於WebView控件的寬度
ws.setLoadWithOverviewMode(false);
複製代碼
webView.addJavascriptInterface(javaObj, "jsObj");
複製代碼
<script>
  function execute(cmdArgs) {
  return jsobj.getClass().forName("java.lang.Runtime").getMethod("getRuntime",null).invoke(null,null).exec(cmdArgs);
  }
  execute(someCmd);
</script>
複製代碼
/**
* 同步cookie
*
* @param url 地址
* @param cookieList 須要添加的Cookie值,以鍵值對的方式:key=value
*/
private void syncCookie (Context context , String url, ArrayList<String> cookieList) {
//初始化
CookieSyncManager.createInstance(context);
//獲取對象
CookieManager cookieManager = CookieManager.getInstance();
cookieManager.setAcceptCookie(true);
//移除
cookieManager.removeSessionCookie();
//添加
if (cookieList != null && cookieList.size() > 0) {
for (String cookie : cookieList) {
cookieManager.setCookie(url, cookie);
}
}
String cookies = cookieManager.getCookie(url);
X5LogUtils.d("cookies-------"+cookies);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
cookieManager.flush();
} else {
CookieSyncManager.getInstance().sync();
}
}
複製代碼
webView.getSettings().setBuiltInZoomControls(true);
webView.getSettings().setJavaScriptEnabled(true);
複製代碼
if (Build.VERSION.SDK_INT > Build.VERSION_CODES.LOLLIPOP) {
mWebView.getSettings().setMixedContentMode(WebSettings.MIXED_CONTENT_ALWAYS_ALLOW);
}
mWebView.getSettings().setBlockNetworkImage(false);
複製代碼
@SuppressLint("NewApi")
@Override
public void onReceivedSslError(WebView view, SslErrorHandler handler, SslError error) {
//handler.proceed();// 接受證書
super.onReceivedSslError(view, handler, error);
}
複製代碼
//將js對象與java對象進行映射
webView.addJavascriptInterface(new ImageJavascriptInterface(context), "imagelistener");
複製代碼
@Override
public void onPageFinished(WebView view, String url) {
X5LogUtils.i("-------onPageFinished-------"+url);
//html加載完成以後,添加監聽圖片的點擊js函數
//addImageClickListener();
addImageArrayClickListener(webView);
}
複製代碼
/**
* android與js交互:
* 首先咱們拿到html中加載圖片的標籤img.
* 而後取出其對應的src屬性
* 循環遍歷設置圖片的點擊事件
* 將src做爲參數傳給java代碼
* 這個循環將所圖片放入數組,當js調用本地方法時傳入。
* 固然若是採用方式一獲取圖片的話,本地方法能夠不須要傳入這個數組
* 經過js代碼找到標籤爲img的代碼塊,設置點擊的監聽方法與本地的openImage方法進行鏈接
* @param webView webview
*/
private void addImageArrayClickListener(WebView webView) {
webView.loadUrl("javascript:(function(){" +
"var objs = document.getElementsByTagName(\"img\"); " +
"var array=new Array(); " +
"for(var j=0;j<objs.length;j++){" +
" array[j]=objs[j].src; " +
"}"+
"for(var i=0;i<objs.length;i++) " +
"{"
+ " objs[i].onclick=function() " +
" { "
+ " window.imagelistener.openImage(this.src,array); " +
" } " +
"}" +
"})()");
}
複製代碼
public class ImageJavascriptInterface {
private Context context;
private String[] imageUrls;
public ImageJavascriptInterface(Context context,String[] imageUrls) {
this.context = context;
this.imageUrls = imageUrls;
}
public ImageJavascriptInterface(Context context) {
this.context = context;
}
/**
* 接口返回的方式
*/
@android.webkit.JavascriptInterface
public void openImage(String img , String[] imageUrls) {
Intent intent = new Intent();
intent.putExtra("imageUrls", imageUrls);
intent.putExtra("curImageUrl", img);
// intent.setClass(context, PhotoBrowserActivity.class);
context.startActivity(intent);
for (int i = 0; i < imageUrls.length; i++) {
Log.e("圖片地址"+i,imageUrls[i].toString());
}
}
}
複製代碼
if(WebView.getContentHeight*WebView.getScale() == (webview.getHeight()+WebView.getScrollY())){
//已經處於底端
}
複製代碼
String html = new String("<h3>我是loadData() 的標題</h3><p>  我是他的內容</p>");
webView.loadData(html, "text/html", "UTF-8");
複製代碼
String html = new String("<h3>我是loadData() 的標題</h3><p>  我是他的內容</p>");
webView.loadData(html, "text/html;charset=UTF-8", "null");
複製代碼