Web开发&WebView
前言
目前现行的跨平台框架有很多, 需要我们做好技术选型, 适合自己的才是最好的, 今天讲的是最简单的跨平台实现方案:Web开发
- 目前混合开发技术
- Web开发
优势:
开发成本低, 效率高, 与客户端解耦, 及时上线
弱势:
纯WebView环境,体验较差
- Hybird混合开发
优势:
介于原生和Web之间,包含Web开发的所有优点
部分功能接近原生APP体验
能够调用原生能力
- React Native
优势:
摆脱WebView, 体验可与原生媲美
支持热更新
弱势:
页面加载较慢
控件不够完善
- Flutter
优势:
最佳运行体验
性能强劲
弱势:
需学习Dart
跨平台做的不够彻底
Web开发
Layout
Crazy Coding
- WebView初始化
private lateinit var webView: WebView
private fun initWebView() {
webView = vb.mainWebView//ViewBinding的WebView
webView.addJavascriptInterface(this, "android")
webView.webViewClient = webViewClient
webView.webChromeClient = webChromeClient
webView.settings.apply {
javaScriptEnabled = true
cacheMode = WebSettings.LOAD_NO_CACHE
domStorageEnabled = true
}
}
- 重定向WebView->页面切换
private fun refreshWebView(pageMode: MainViewModel.PageMode) {
isPageLoading = true
when (pageMode) {
MainViewModel.PageMode.DATA -> {
webView.loadUrl(UrlConstant.BASE_URL + UrlConstant.MAIN_DATA)
}
MainViewModel.PageMode.RECORD -> {
webView.loadUrl(UrlConstant.BASE_URL + UrlConstant.MAIN_RECORD)
}
}
}
记得加上:用来绕过缓存
webView.loadUrl("javascript: window.location.reload(true)")
override fun initData(savedInstanceState: Bundle?) {
super.initData(savedInstanceState)
vb.vm = vm
initWebView()
refreshWebView(MainViewModel.PageMode.DATA)
webView.loadUrl("javascript: window.location.reload(true)")
}
- 生命周期销毁
override fun onDestroy() {
webView.loadUrl("javascript: localStorage.clear()")
webView.destroy()
super.onDestroy()
}
- 两个重要的client
- webViewClient: 监听页面的响应,刷新进度条的显示与隐藏
private val webViewClient = object : WebViewClient() {
override fun onPageStarted(view: WebView?, url: String?, favicon: Bitmap?) {
//显示进度条
vb.mainWebProgress.visibility = View.VISIBLE
}
RequiresApi(Build.VERSION_CODES.KITKAT)
override fun onPageFinished(view: WebView?, url: String?) {
"url: $url".i()//日志扩展函数
if (isPageLoading) {
isPageLoading = false
//webView传数据到localStorage,供前端拿取
webView.loadUrl("javascript: const siteData= {enterpriseToken: \"${MainPreference.service_token}\", deviceAccessPermissions: ${MainPreference.select_location}, audioSwitch: ${MainPreference.select_voice.toInt()}, privilege: \"${MainPreference.service_privilege}\"}; localStorage.setItem(\"siteData\", JSON.stringify(siteData));")
//打印传值结果
view?.evaluateJavascript("javascript: window.localStorage.getItem('siteData');") { "VALUE--->$it".i() }
GlobalScope.launch(Dispatchers.Main) {
//进度条动画
AnimUtil.operateAlphaOutAnim(true, vb.mainWebProgress)
delay(2000)
AnimUtil.operateAlphaOutAnim(false, vb.mainWebProgress)
vb.mainWebProgress.visibility = View.GONE
}
}
}
}
- webChromeClient: 拦截前端的Alert弹框
private val webChromeClient = object : WebChromeClient() {
override fun onJsAlert(view: WebView?,url: String?,message: String?,result: JsResult?): Boolean {
//前端json,显示弹框的类型
message?.let {
val alertBean = Gson().fromJson(it, AlertBean::class.java)
"JS拦截: type: ${alertBean.type} data: ${alertBean.data}".i()
when (alertBean.type) {
"error" -> {
//ViewModel中的定义的观察者对象,由activity监听
vm.mAlertType.postValue(DetailDialog.DialogType.ERROR)
vm.mMessage = alertBean.data
}
"detail" -> {
vm.getDetail(DetailDialog.DialogType.DETAIL, alertBean.data)
}
"verify" -> {
vm.getDetail(DetailDialog.DialogType.VERIFY, alertBean.data)
}
"exit" -> {
GlobalScope.launch() {
MainPreference.login_is_login = false
delay(1000)
finish()
}
}
"audio"->{
operateAudioPlayer(true)
}
else -> {
}
}
}
//一定要有这句,不然不能拦截下一个弹框
result?.cancel()
return true
}
override fun onProgressChanged(view: WebView?, newProgress: Int) {
//进度条进度
vb.mainWebProgress.progress = newProgress
}
}
layout
<ProgressBar
android:id="@+id/main_web_progress"
style="?android:attr/progressBarStyleHorizontal"
android:layout_width="match_parent"
android:layout_height="@dimen/dp_3"
android:layout_gravity="top"
android:max="100"
android:progress="0"
android:progressDrawable="@drawable/main_progressbar_net"
android:visibility="visible" />
drawble
<?xml version="1.0" encoding="utf-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
<!--背景色-->
<item
android:id="@android:id/background"
android:drawable="@color/transparent" />
<!--设置进度条颜色-->
<item android:id="@android:id/progress">
<clip>
<shape>
<solid android:color="@color/main_progressbar"/>
</shape>
</clip>
</item>
</layer-list>
color
<color name="main_progressbar">#3860D1</color>
一些用到的JS语句
刷新界面
javascript: window.location.reload(true);
回调调用前端定义的函数(非系统方法)
javascript: window.onUpdata();
清除localStorage
javascript: localStorage.clear();
存localStorage
javascript: const siteData= {enterpriseToken: \"${MainPreference.service_token}\", deviceAccessPermissions: ${MainPreference.select_location}, audioSwitch: ${MainPreference.select_voice.toInt()}, privilege: \"${MainPreference.service_privilege}\"}; localStorage.setItem(\"siteData\", JSON.stringify(siteData));
读localStorage
javascript: window.localStorage.getItem('siteData');
可能会好奇为啥用localStorge呢,因为登陆啥的是原生的