【Android】跨平台:Web开发(WebView)

Web开发&WebView


前言

目前现行的跨平台框架有很多, 需要我们做好技术选型, 适合自己的才是最好的, 今天讲的是最简单的跨平台实现方案:Web开发

  • 目前混合开发技术
  1. Web开发
优势:
	开发成本低, 效率高, 与客户端解耦, 及时上线
弱势:
	纯WebView环境,体验较差
  1. Hybird混合开发
优势:
	介于原生和Web之间,包含Web开发的所有优点
	部分功能接近原生APP体验
	能够调用原生能力
  1. React Native
优势:
	摆脱WebView, 体验可与原生媲美
	支持热更新
弱势:
	页面加载较慢
	控件不够完善
  1. 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
  1. 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
			}
		}
	}
}
  1. 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呢,因为登陆啥的是原生的

猜你喜欢

转载自blog.csdn.net/weixin_42473228/article/details/121617223