Android原生webView概述

1.基本使用
    首先要添加网络权限<uses-permission android:name="android.permission.INTERNET">
从Android9.0(API 28)开始默认不再支持直接load未加密的url,解决方法是在manifest 中application节点添加android:usesCleartextTraffic="true"

加载方式:

webView.loadUrl("http://game.163.com")
val webSetting: WebSettings = webView.settings
webSetting.javaScriptEnabled = true

webView.webViewClient = object: WebViewClient() {
    override fun shouldOverrideUrlLoading(view: WebView?, url: String?): Boolean {
        url?.let { view?.loadUrl(it) }
        return super.shouldOverrideUrlLoading(view, url)
    }
}

如果不给webView.webViewClient赋值那么它会在浏览器中打开这个网页,而不是在WebView中打开,如果是加载本地的html文件,需要以file为协议开头:

webView.loadUrl("file:///android_asset/my.html")

注意:加载本地的html时不需要设置WebViewClient
还有几个方法:

webSettings webSetting = mWebView.getSettings();
webSetting.setJavaScriptEnabled(true);//启用js
webSetting.setCacheMode(WebSettings.LOAD_CACHE_ELSE_NETWORK);//优先使用缓存
//自适应屏幕
webSetting.setUseWideViewPort(true);
webSetting.setLoadWithOverviewMode(true);
webSetting.setSupportZoom(true);//支持缩放
webSetting.setBuiltInZoomControls(true);//设置出现缩放工具
webSetting.requestFocusFromTouch();//支持获取手势焦点

2.JS调用Android

Android代码:

val webSettings = mWebView.getSettings() 
webSettings.javaScriptEnabled = true
webView.addJavascriptInterface(this, "android")
webView.loadUrl("file:///android_asset/web.html")

fun toastMessage(message: String) {
    Toast.makeText(applicationContext, "通过Natvie传递的Toast:$message", Toast.LENGTH_LONG).show()
}

js中的代码:

<!DOCTYPE html>
<html lang="en">
<head>
     <meta charset="UTF-8">
     <title>Title</title>
     <h1>webview示例</h1>
     <input type="button" value="js调native" onclick="ok()">
</head>
<body>
    <script type="text/javascript">
        function ok() {
            android.toastMessage("哈哈,i m webview msg");
        }
    </script>
</body>
</html>

我们可以看到直接用addJavaScriptInterface(this, "android")把整个类作为对象传给了WebView,这个对象名为"android"。但这样会有很大风险,一般我们不会直接传this,而是传入一个内部类对象,而这个内部类是专门用来和js交互的:

inner class JsBridge {
    @JavascriptInterface
    fun toastMessage(message: String) {
        Toast.makeText(applicationContext, "通过Natvie传递的Toast:$message", Toast.LENGTH_LONG).show()
    }
}

注入方法:mWebView.addJavascriptInterface(new JSBridge(), "android");   为了更好地处理addJavascriptInterface()的安全性问题,在android4.2(17)之后只允许有@JavascriptInterface注解的方法可以和js交互

3.Java调JS
JS代码:

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
     <title>Title</title>
     <h1 id="h">欢迎光临启舰的blog</h1>
</head>
<body>
     <script type="text/javascript">
         function sum(i,m) {
             document.getElementById("h").innerHTML= (i+m);
         }
     </script>
</body>
</html>

Android调用代码:

val webSettings = mWebView.getSettings()
webSettings.javaScriptEnabled = true
webView.loadUrl("file:///android_asset/web.html");
mBtn.setOnClickListener {
    webView.loadUrl("javascript:sum(3,8)")
}

如果js的方法有返回值,要在java中搞到这个返回值要怎么做呢?Android4.4之后:
js:

function sum(i, m) {
    document.getElementById("h").innerHTML = (i + m);
    return i + m;
}

Android:

webView.evaluateJavascript("sum(i, m)", object: ValueCallback<String> {
    override fun onReceiveValue(p0: String?) {
        Log.i("TAG", "result: $p0")
    }
})

4.WebViewClient
这个类提供了一些加载网页时的回调方法:

//在开始加载网页时会回调
public void onPageStarted(WebView view, String url, Bitmap favicon) 
//在结束加载网页时会回调
public void onPageFinished(WebView view, String url)
//拦截 url 跳转,在里边添加点击链接跳转或者操作
public boolean shouldOverrideUrlLoading(WebView view, String url)
//加载错误的时候会回调,在其中可做错误处理,比如再请求加载一次,或者提示404的错误页面
public void onReceivedError(WebView view, int errorCode,String description, String failingUrl)
//当接收到https错误时,会回调此函数,在其中可以做错误处理
public void onReceivedSslError(WebView view, SslErrorHandler handler,SslError error)
//在每一次请求资源时,都会通过这个函数来回调
public WebResourceResponse shouldInterceptRequest(WebView view, String url) {
    return null;
}

另外通过重写shouldOverrideUrlLoading,可以实现对网页中超链接的拦截

webView.webViewClient = object: WebViewClient() {
    override fun shouldOverrideUrlLoading(view: WebView?, url: String?): Boolean {
        url?.let {
            if (it.contains("blog.csdn.net")) {
                view?.loadUrl("http://www.baidu.com");//在当前webview加载的url中包含“blog.csdn.net”,则将其转换成”www.baidu.com”         
            } else {
                view?.loadUrl(it)
            }
            return true
        }
        return false
    }
}

而且如果是return true就告诉系统我们已经拦截了URL并进行了处理,不要再去加载默认的URL
所以要想在不满足条件的情况下继续加载原URL就必须手写else代码块重新加载原URL,如果不写
这个else判断,那么当前URL不满足if里面的条件时他也不会去加载原URL,就会产生白屏。所以,一般我们建议使用return false; 这两段代码的效果是一样的:

webView.webViewClient = object: WebViewClient() {
    override fun shouldOverrideUrlLoading(view: WebView?, url: String?): Boolean {
        url?.let {
            if (it.contains("blog.csdn.net")) {
                view?.loadUrl("http://www.baidu.com");//在当前webview加载的url中包含“blog.csdn.net”,则将其转换成”www.baidu.com”         
            }
            return false
        }
        return false
    }
}

5.webView内下载文件监听:DownloadListener

在webview加载的html页面中如果发生下载文件的事件就会触发DownloadListener响应,前提是必须webSettings.javaScriptEnabled置为true,否则点击下载按钮的时候无法触发任何响应:

webView.setDownloadListener { url, userAgent, contentDisposition, mimeType, contentLength ->
    Log.i("TAG", "url:$url \n" +
                 "userAgent:$userAgent \n" +
                 "contentDisposition:$contentDisposition \n" +
                 "mimeType:$mimeType \n" +
                 "contentLength:$contentLength")
    progressDialog.show()
    lifecycleScope.launch {
        if (url.endsWith(".apk")) {
            val msg = download(url)
            Toast.makeText(this@MainActivity, "$msg", Toast.LENGTH_SHORT).show()
//          val uri = Uri.parse(url) //或者直接跳系统的下载进程
//          val intent = Intent(Intent.ACTION_VIEW, uri)
//          startActivity(intent)
        }
        progressDialog.dismiss()
    }
}

具体的下载逻辑:

private suspend fun download(url: String): String {
    return withContext(Dispatchers.IO) {
        try {
            val httpUrl = URL(url)
            val conn = httpUrl.openConnection() as HttpURLConnection
            conn.doInput = true
            conn.readTimeout = 3000;//设置读取超时的毫秒数
            conn.connectTimeout = 3000;//设置连接超时的毫秒数
            val connIn = conn.inputStream
            val loadFile = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS)
            val apkFile = File(loadFile, "111.apk")
            val connOut = FileOutputStream(apkFile)
            val b = ByteArray(8 * 1024)
            var len = 0
            do {
                len = connIn.read(b)
                if (len != -1) {
                    connOut.write(b, 0, len)
                } else {
                    break
                }
            } while (true)
            connOut.close()
            connIn.close()
            "success!"
        } catch (e: Exception) {
            Log.e("TAG", "$e")
            "fail!"
        }
    }
}

6.返回键
    如果用webview点链接看了很多页以后,如果不做任何处理,点击系统“Back”键,整个浏览器会调用finish()而结束自身,如果希望浏览的网页回退而不是退出浏览器,需要在当前Activity中处理并消费掉该Back事件,覆盖Activity类的onKeyDown(int keyCoder,KeyEvent event)方法

override fun onKeyDown(keyCode: Int, event: KeyEvent?): Boolean {
    //改写物理返回键的逻辑
    if (keyCode == KeyEvent.KEYCODE_BACK) {
        if (webView.canGoBack()) {
            webView.goBack() //返回上一页面
            return true
        } else {
            exitProcess(0) //退出程序
        }
    }
    return super.onKeyDown(keyCode, event)
}

7.强制使用外部浏览器加载

val uri = Uri.parse("http://www.example.com")
val intent = Intent(Intent.ACTION_VIEW, uri)
startActivity(intent)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

我们间的空白格

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值