Android专栏-WebView

Android专栏-WebView

0x00 常规WebViewActivity

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
package com.dench.webviewlib

import android.annotation.SuppressLint
import android.graphics.Bitmap
import android.net.http.SslError
import android.os.Build
import android.os.Bundle
import android.text.TextUtils
import android.util.Log
import android.view.Gravity
import android.webkit.*
import android.widget.TextView
import android.widget.Toast
import androidx.appcompat.app.AppCompatActivity
import androidx.databinding.DataBindingUtil
import com.alibaba.android.arouter.facade.annotation.Autowired
import com.alibaba.android.arouter.facade.annotation.Route
import com.alibaba.android.arouter.launcher.ARouter
import com.dench.baselib.provider.WebViewService
import com.dench.baselib.utlis.StatusBarHelper
import com.dench.webviewlib.bridge.JsInterface
import com.dench.webviewlib.databinding.ActivityWebViewBinding
import kotlinx.android.synthetic.main.activity_web_view.*

@Route(path = WebViewService.activityPath)
class WebViewActivity : AppCompatActivity() {
@Autowired
lateinit var title: String

@Autowired
lateinit var url: String

private lateinit var dataViewBinding: ActivityWebViewBinding

override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
ARouter.getInstance().inject(this)
StatusBarHelper.fitSystemBar(this, false)

dataViewBinding = DataBindingUtil.setContentView(this, R.layout.activity_web_view)

initToolbar()

initWebView()
}

override fun onBackPressed() {
if (webView.canGoBack()) {
webView.goBack()
return
}
super.onBackPressed()
}

private fun initToolbar() {
dataViewBinding.toolbar.titleTv.text = title
val backIv = dataViewBinding.toolbar.leftIv
backIv.setImageResource(R.drawable.ic_close)
backIv.setOnClickListener {// 关闭
finish()
}
}

@SuppressLint("JavascriptInterface")
private fun initWebView() {
// webView Settings
initWebViewSetting()

// init Client
initClient()

// add Javascript Interface
webView.addJavascriptInterface(JsInterface(this), JsInterface.NAME)

// register Scroll Listener
registerScrollListener()

loadUrl()
}

// webView Settings
@SuppressLint("SetJavaScriptEnabled")
private fun initWebViewSetting() {
//声明WebSettings子类
val webSettings = webView.settings

//如果访问的页面中要与Javascript交互,则webView必须设置支持Javascript
webSettings.javaScriptEnabled = true
webSettings.javaScriptCanOpenWindowsAutomatically = true //支持通过JS打开新窗口

//设置自适应屏幕,两者合用
webSettings.useWideViewPort = true //将图片调整到适合webView的大小
webSettings.loadWithOverviewMode = true // 缩放至屏幕的大小
//缩放操作
webSettings.setSupportZoom(true)//支持缩放,默认为true。是下面那个的前提。
webSettings.builtInZoomControls = true //设置内置的缩放控件。若为false,则该WebView不可缩放
webSettings.displayZoomControls = false //隐藏原生的缩放控件
// 缓存
webSettings.cacheMode = WebSettings.LOAD_DEFAULT //webView缓存策略
webSettings.domStorageEnabled = true
webSettings.databaseEnabled = true
webSettings.setAppCacheEnabled(true)

//其他
webSettings.allowFileAccess = true //设置可以访问文件
webSettings.loadsImagesAutomatically = true //支持自动加载图片
webSettings.defaultTextEncodingName = "utf-8" //设置编码格式

// 在安卓5.0之后,默认不允许加载http与https混合内容,需要手动设置
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
webSettings.mixedContentMode = WebSettings.MIXED_CONTENT_ALWAYS_ALLOW
}
}

// init Client
private fun initClient() {
webView.webViewClient = object : WebViewClient() {

// 在网页上的所有加载都经过这个方法
override fun shouldOverrideUrlLoading(
view: WebView?,
request: WebResourceRequest?
): Boolean {
Log.i(_tag, "shouldOverrideUrlLoading()")
request?.url?.let {
return when (it.scheme) {
"http", "https" -> { // 加载网络html
super.shouldOverrideUrlLoading(view, request)
}
"file", "content" -> { // 加载本地html
super.shouldOverrideUrlLoading(view, request)
}
else -> { // 特殊 scheme 不处理
showToast("$it")
true
}
}
}
return true
}

// 加载页面的服务器出现错误时(如404)调用
override fun onReceivedError(
view: WebView?,
request: WebResourceRequest?,
error: WebResourceError?
) {
Log.i(_tag, "onReceivedError()")
super.onReceivedError(view, request, error)
}

// ssl 证书错误
override fun onReceivedSslError(
view: WebView?,
handler: SslErrorHandler?,
error: SslError?
) {
Log.i(_tag, "onReceivedSslError()")
handler?.proceed() //表示等待证书响应
// handler?.cancel() //表示挂起连接,为默认方式
// handler?.handleMessage(null) //可做其他处理
}

// 开始载入页面调用的。我们可以设定一个loading的页面,告诉用户程序在等待网络响应
override fun onPageStarted(view: WebView?, url: String?, favicon: Bitmap?) {
Log.i(_tag, "onPageStarted()")
Log.d(
_tag,
"onPageStarted() called with: view = $view, url = $url, favicon = $favicon"
)
super.onPageStarted(view, url, favicon)
webViewProgress.show()
}

// 在页面加载结束时调用。我们可以关闭loading 条,切换程序动作
override fun onPageFinished(view: WebView?, url: String?) {
Log.i(_tag, "onPageFinished()")
super.onPageFinished(view, url)
webViewProgress.hide()
}

// 在加载页面资源时会调用,每一个资源(比如图片)的加载都会调用一次
override fun onLoadResource(view: WebView?, url: String?) {
Log.d(_tag, "onLoadResource()")
Log.d(_tag, "onLoadResource() called with: view = $view, url = $url")
super.onLoadResource(view, url)
}
}

webView.webChromeClient = object : WebChromeClient() {
// 加载进度
override fun onProgressChanged(view: WebView?, newProgress: Int) {
webViewProgress.progress = newProgress
}

// Title
override fun onReceivedTitle(view: WebView?, title: String?) {
if (!TextUtils.isEmpty(title)) {
findViewById<TextView>(R.id.titleTv).text = title
}
}
}
}

// 加载URL
private fun loadUrl() {
//方式1. 加载一个网页
webView.loadUrl(url)
// //方式2:加载apk包中的html页面
// webView.loadUrl("file:///android_asset/test.html")
// //方式3:加载手机本地的html页面
// webView.loadUrl("content://com.android.htmlfileprovider/sdcard/test.html")
}

// 注册监听
private fun registerScrollListener() {
// if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
// webView.setOnScrollChangeListener { _, _, scrollY, _, oldScrollY ->
// Log.d(_tag, "scrollY: $scrollY, oldScrollY: $oldScrollY")
// }
// }
}

private val _tag = "WebViewActivity"

private fun showToast(info: String?) {
val toast = Toast.makeText(this, info, Toast.LENGTH_SHORT)
toast.setGravity(Gravity.CENTER_VERTICAL, 0, 0)
toast.show()
}
}

0x01 白屏问题

情景一:如果访问的页面中要与Javascript交互,则webView必须设置支持Javascript

1
2
3
4
val webSettings = webView.settings
//如果访问的页面中要与Javascript交互,则webView必须设置支持Javascript
webSettings.javaScriptEnabled = true
webSettings.javaScriptCanOpenWindowsAutomatically = true //支持通过JS打开新窗口

情景二:在安卓5.0之后,默认不允许加载http与https混合内容,需要手动设置

1
2
3
4
// 在安卓5.0之后,默认不允许加载http与https混合内容,需要手动设置
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
webSettings.mixedContentMode = WebSettings.MIXED_CONTENT_ALWAYS_ALLOW
}

其他情景: 设置domStorageEnabled 和背景色需要验证,暂时没遇到。

0x02 卡顿问题

由于设置了 android:layerType="software"导致的 webview 卡顿。

关于三个layerType属性介绍:https://blog.csdn.net/a345017062/article/details/7478667

解决:

开启 Activity 硬件加速 ``android:hardwareAccelerated=”true”, 并且设置 webview 的android:layerType=”none”`。

作者

Dench

发布于

2020-12-26

更新于

2020-12-26

许可协议

CC BY-NC-SA 4.0

Your browser is out-of-date!

Update your browser to view this website correctly.&npsb;Update my browser now

×