整合營銷服務商

          電腦端+手機端+微信端=數據同步管理

          免費咨詢熱線:

          關于android的webView的內容

          關于android的webView的內容

          目前的網速環境而言,應用程序內加載web的速度是非常的快的,所以當一個APP上的頁面需要定期的變動的時候就可以嘗試使用webView來進行實現。

          其實平時使用的APP中很多都是用到了webView的界面,例如很多的電商網頁都使用了webView的功能。

          關于webView的定義,在谷歌的網站上給出的是 :一個能夠展示web界面的View,

          最初始的定義中,webView是不能夠使用JavaScript并且其中的很多web頁面的錯誤會自動忽視,如果只是展示一些最基本的HTML頁面做為UI的一部分那么這樣是OK的。但是這樣是不允許界面進行一些交互的,因為這只是最進本的HTML網頁并沒有JavaScript用來與之交互。

          其中WebView最常使用的方法有

          加載網頁的

          //方式1. 加載一個網頁:

          webView.loadUrl("http://www.google.com/");

          //方式2:加載apk包中的html頁面

          webView.loadUrl("file:///android_asset/test.html");

          //方式3:加載手機本地的html頁面

          webView.loadUrl("content://com.android.htmlfileprovider/sdcard/test.html");

          // 方式4: 加載 HTML 頁面的一小段內容

          WebView.loadData(String data, String mimeType, String encoding)

          其中要注意的是加載apk包中的HTML頁面的時候file之后是三個斜杠

          然后WebView也可以進行網頁的前進后退的操作

          //是否可以后退

          Webview.canGoBack()

          //后退網頁

          Webview.goBack()

          //是否可以前進

          Webview.canGoForward()

          //前進網頁

          Webview.goForward()

          //以當前的index為起始點前進或者后退到歷史記錄中指定的steps

          //如果steps為負數則為后退,正數則為前進

          Webview.goBackOrForward(intsteps)

          當知曉了前進后退的操作的時候那么這里就可以設置一下使用back按鍵的時候將當前的Activity的退出改為網頁的后退

          當需要進行一些自己的一些設置。其中可以創建一個webChromeClient 的子類。這個類是在一些東西影響瀏覽器UI界面的發生時候被調用。例如界面刷新或者一個alert的一個響應網站的標題和圖標什么都會發送到這里。

          // Let's display the progress in the activity title bar, like the

          // browser app does.

          getWindow().requestFeature(Window.FEATURE_PROGRESS);

          webview.getSettings().setJavaScriptEnabled(true);

          final Activity activity=this;

          webview.setWebChromeClient(new WebChromeClient() {

          public void onProgressChanged(WebView view, int progress) {

          // Activities and WebViews measure progress with different scales.

          // The progress meter will automatically disappear when we reach 100%

          activity.setProgress(progress * 1000);

          }

          });

          webview.setWebViewClient(new WebViewClient() {

          public void onReceivedError(WebView view, int errorCode, String description, String failingUrl) {

          Toast.makeText(activity, "Oh no! " + description, Toast.LENGTH_SHORT).show();

          }

          });

          webview.loadUrl("https://developer.android.com/");

          上面Google官網上給出的范例代碼。其中可以看到首先是要getSettings().setJavaScriptEnabled(true)設置使用JavaScript。使用getSetting可以進行很多配置

          //聲明WebSettings子類

          WebSettings webSettings=webView.getSettings();

          //如果訪問的頁面中要與Javascript交互,則webview必須設置支持Javascript

          webSettings.setJavaScriptEnabled(true);

          // 若加載的 html 里有JS 在執行動畫等操作,會造成資源浪費(CPU、電量)

          // 在 onStop 和 onResume 里分別把 setJavaScriptEnabled() 給設置成 false 和 true 即可

          //支持插件

          webSettings.setPluginsEnabled(true);

          //設置自適應屏幕,兩者合用

          webSettings.setUseWideViewPort(true); //將圖片調整到適合webview的大小

          webSettings.setLoadWithOverviewMode(true); // 縮放至屏幕的大小

          //縮放操作

          webSettings.setSupportZoom(true); //支持縮放,默認為true。是下面那個的前提。

          webSettings.setBuiltInZoomControls(true); //設置內置的縮放控件。若為false,則該WebView不可縮放

          webSettings.setDisplayZoomControls(false); //隱藏原生的縮放控件

          //其他細節操作

          webSettings.setCacheMode(WebSettings.LOAD_CACHE_ELSE_NETWORK); //關閉webview中緩存

          webSettings.setAllowFileAccess(true); //設置可以訪問文件

          webSettings.setJavaScriptCanOpenWindowsAutomatically(true); //支持通過JS打開新窗口

          webSettings.setLoadsImagesAutomatically(true); //支持自動加載圖片

          webSettings.setDefaultTextEncodingName("utf-8");//設置編碼格式

          然后分別使用了WebViewClient 和WebChromeClient的兩個子類的部分方法。

          那么這里我們就可以打開androidStudio中的WebViewClient的類查看到底有什么是可以用的上的。

          其中在WebView中比較常用的方法

          shouldOverrideUrlLoading()

          作用是在于打開網頁的時候不調用系統的瀏覽器,而是在本地的WebView中顯示

          使用的方法都是在webViewClient的子類中復寫一下代碼就可以

          webView.setWebViewClient(new WebViewClient(){

          @Override

          public boolean shouldOverrideUrlLoading(WebView view, String url) {

          view.loadUrl(url);

          return true;

          }

          還有比較常用的就是頁面加載開始和頁面加載結束的調用。

          分別是 onPageStarted和onPageFinished 其中的方法都可以在原來的類中找到,只要重寫方法,然后加上自己所需要的操作就可以了。

          個蛋(codeegg)第 723 次推文

          作者: 騎著蝸牛闖紅燈

          原文: https://juejin.im/post/5cff8c27f265da1bae38f1c1

          先簡單介紹一下,Android在4.4之后采用了Chrome內核,所以我們在開發web頁面的時候,es6的語法,css3的樣式等大可放心使用。

          我將分下面幾個模塊去介紹Android上面WebView。

          WebView自身的一些方法


          //方式1. 加載一個網頁:

          webView.loadUrl("http://www.google.com/");

          //方式2:加載apk包中的html頁面
          webView.loadUrl("file:///android_asset/test.html");

          //方式3:加載手機本地的html頁面
          webView.loadUrl("content://com.android.htmlfileprovider/sdcard/test.html");



          正常情況下,在WebView界面,用戶點擊返回鍵是直接退出該頁面的,著當然不是我們想要的,我們想要的是網頁自己的前進和后退,所以下面介紹網頁前進和后退的一些API

          //判斷是否可以后退
          Webview.canGoBack
          //后退網頁
          Webview.goBack

          //判斷是否可以前進
          Webview.canGoForward
          //前進網頁
          Webview.goForward

          // 參數傳負的話表示后退,傳正值的話表示的是前進
          Webview.goBackOrForward(int steps)
          對返回鍵的監聽,來實現網頁的后退
          public boolean onKeyDown(int keyCode, KeyEvent event) {
          if ((keyCode==KEYCODE_BACK) && mWebView.canGoBack) {
          mWebView.goBack;
          return true;
          }
          return super.onKeyDown(keyCode, event);
          }

          如何防止WebView內存泄漏

          防止內存泄漏的一個原則就是:生命周期長的不要跟生命周期短的玩。

          為了防止WebView不造成內存泄漏,

          • 不要在xml里面定義WebView,而是在Activity選中使用代碼去構建,并且Context使用ApplicationContext

          • 在Activity銷毀的時候,先讓WebView加載空內容,然后重rootView中移除WebView,再銷毀WebView,最后置空

          override fun onDestroy {
          if (webView !=) {
          webView!!.loadDataWithBaseURL(, "", "text/html", "utf-8", )
          webView!!.clearHistory
          (webView!!.parent as ViewGroup).removeView(webView)
          webView!!.destroy
          webView=
          }
          super.onDestroy

          }

          WebSetting和WebViewClient,WebChromeClien

          • WebSetting


          作用:對WebView進行配置和管理

          WebSettings webSettings=webView.getSettings;
          // 設置可以與js交互,為了防止資源浪費,我們可以在Activity
          // 的onResume中設置為true,在onStop中設置為false
          webSettings.setJavaScriptEnabled(true);

          //設置自適應屏幕,兩者合用
          //將圖片調整到適合webview的大小
          webSettings.setUseWideViewPort(true);
          // 縮放至屏幕的大小
          webSettings.setLoadWithOverviewMode(true);

          //設置編碼格式
          webSettings.setDefaultTextEncodingName("utf-8");

          // 設置允許JS彈窗
          webSettings.setJavaScriptCanOpenWindowsAutomatically(true);

          //設置緩存的模式
          webSettings.setCacheMode(WebSettings.LOAD_CACHE_ELSE_NETWORK);

          關于緩存的設置

          當加載 html 頁面時,WebView會在/data/data/包名目錄下生成 database 與 cache 兩個文件夾,請求的 URL記錄保存在 WebViewCache.db,而 URL的內容是保存在 WebViewCache 文件夾下

          緩存模式如下:

          //LOAD_CACHE_ONLY: 不使用網絡,只讀取本地緩存數據
          //LOAD_DEFAULT: (默認)根據cache-control決定是否從網絡上取據。
          //LOAD_NO_CACHE: 不使用緩存,只從網絡獲取數據.
          //LOAD_CACHE_ELSE_NETWORK,只要本地有,無論是否過期,或no-cache,都使用緩存中的數據。

          離線加載

          if (NetStatusUtil.isConnected(getApplicationContext)) {
          webSettings.setCacheMode(WebSettings.LOAD_DEFAULT);//根據cache-control決定是否從網絡上取數據。
          } else {
          webSettings.setCacheMode(WebSettings.LOAD_CACHE_ELSE_NETWORK);//沒網,則從本地獲取,即離線加載
          }

          webSettings.setDomStorageEnabled(true); // 開啟 DOM storage API 功能
          webSettings.setDatabaseEnabled(true); //開啟 database storage API 功能
          webSettings.setAppCacheEnabled(true);//開啟 Application Caches 功能

          String cacheDirPath=getFilesDir.getAbsolutePath + APP_CACAHE_DIRNAME;
          webSettings.setAppCachePath(cacheDirPath); //設置 Application Caches 緩存目錄
          • WebViewClient 作用

          • 處理各種通知,請求事件,主要有,網頁開始加載,記載結束,加載錯誤(如404),處理https請求,具體使用請看下面代碼,注釋清晰。


          webView!!.webViewClient=object : WebViewClient {
          // 啟用WebView,而不是系統自帶的瀏覽器
          override fun shouldOverrideUrlLoading(view: WebView, url: String): Boolean {
          view.loadUrl(url)
          return true
          }

          // 頁面開始加載,我們可以在這里設置loading
          override fun onPageStarted(view: WebView?, url: String?, favicon: Bitmap?) {
          super.onPageStarted(view, url, favicon)
          tv_start.text="開始加載了..."
          }
          // 頁面加載結束,關閉loading
          override fun onPageFinished(view: WebView?, url: String?) {
          super.onPageFinished(view, url)
          tv_end.text="加載結束了..."
          }

          // 只要加載html,js,css的資源,每次都會回調到這里
          override fun onLoadResource(view: WebView?, url: String?) {
          loge("onLoadResource invoked")
          }

          // 在這里我們可以加載我們自己的404頁面
          override fun onReceivedError(view: WebView?, request: WebResourceRequest?, error: WebResourceError?) {
          loge("加載錯誤:${error.toString}")
          }

          // webview默認設計是不開啟https的,下面的設置是允許使用https
          override fun onReceivedSslError(view: WebView?, handler: SslErrorHandler?, error: SslError?) {
          handler?.proceed
          }

          // js調用Android的方法,在這里可以,該方法不存在通過注解的方式的內存泄漏,但是想拿到Android的返回值的話很難,
          // 可以通過Android調用js的代碼的形式來傳遞返回值,例如下面的方式
          // Android:MainActivity.java
          // mWebView.loadUrl("javascript:returnResult(" + result + ")");
          // JS:javascript.html
          // function returnResult(result){
          // alert("result is" + result);
          // }

          override fun shouldOverrideUrlLoading(view: WebView?, request: WebResourceRequest?): Boolean {
          val uri=Uri.parse(request?.url.toString)
          // 一般根據scheme(協議格式) & authority(協議名)判斷(前兩個參數)
          //假定傳入進來的 url="js://webview?arg1=111&arg2=222"(同時也是約定好的需要攔截的)
          if (uri.scheme=="js") {
          if (uri.authority=="webview") {
          toast_custom("js調用了Android的方法")
          val queryParameterNames=uri.queryParameterNames
          queryParameterNames.forEach {
          loge(it + ":" + uri.getQueryParameter(it))
          }
          }
          return true
          }
          return super.shouldOverrideUrlLoading(view, request)
          }

          // 攔截資源 通常用于h5的首頁頁面,將常用的一些資源,放到本地
          override fun shouldInterceptRequest(view: WebView?, request: WebResourceRequest?): WebResourceResponse? {
          if(request?.url.toString.contains("logo.gif")){
          var inputStream: InputStream?=
          inputStream=applicationContext.assets.open("images/test.png")
          return WebResourceResponse("image/png","utf-8", inputStream)
          }
          return super.shouldInterceptRequest(view, request)
          }
          }

          注意:

          5.1 以上默認禁止了https和http的混用下面的設置是開啟:

          if (Build.VERSION.SDK_INT >=Build.VERSION_CODES.LOLLIPOP) {
          webView.getSettings.setMixedContentMode(WebSettings.MIXED_CONTENT_ALWAYS_ALLOW);
          }
          • WebChromeClient 作用

            輔助webview的一下回調方法,可以得到網頁加載的進度,網頁的標題,網頁的icon,js的一些彈框,直接看代碼,注釋清晰。


          webView!!.webChromeClient=object : WebChromeClient {

          // 網頁加載的進度
          override fun onProgressChanged(view: WebView?, newProgress: Int) {
          tv_progress.text="$newProgress%"
          }

          // 獲得網頁的標題
          override fun onReceivedTitle(view: WebView?, title: String?) {
          tv_title.text=title
          }

          //js Alert
          override fun onJsAlert(view: WebView?, url: String?, message: String?, result: JsResult?): Boolean {

          AlertDialog.Builder(this@WebActivity)
          .setTitle("JsAlert")
          .setMessage(message)
          .setPositiveButton("OK") { _, _ -> result?.confirm }
          .setCancelable(false)
          .show
          return true
          }

          // js Confirm
          override fun onJsConfirm(view: WebView?, url: String?, message: String?, result: JsResult?): Boolean {
          return super.onJsConfirm(view, url, message, result)
          }

          //js Prompt
          override fun onJsPrompt(
          view: WebView?,
          url: String?,
          message: String?,
          defaultValue: String?,
          result: JsPromptResult?
          ): Boolean {
          return super.onJsPrompt(view, url, message, defaultValue, result)
          }

          }

          Android和js的交互

          Android調用js

          1. 通過webview的loadUrl

          注意:該方式必須在webview加載完畢之后才能調用,也就是webviewClient的onPageFinished方法回調之后,而且該方法的執行 會刷新界面,效率較低

          js代碼:
          function callJs{
          alert("Android 調用了 js代碼)
          }
          kotlin代碼:
          webView?.loadUrl("javascript:callJs")

          2. 通過webview的evaluateJavaScript

          比起第一種方法,效率更高,但是要在4.4之后才能使用


          js代碼:

          function callJs{
          // alert("Android 調用了 js代碼)
          return {name:'wfq',age:25}
          }

          kotlin代碼:
          webView?.evaluateJavascript("javascript:callJs") {
          // 這里直接拿到的是js代碼的返回值
          toast(it) // {name:'wfq',age:25}
          }

          js調用Android

          1. 通過webview的addJavaScriptInterface進行對象映射

          我們可以單獨定義一個類,所有需要交互的方法可以全部寫在這個類里面,當然也可以直接寫在Activity里面,下面以直接定義在Activity里面為例,優點:使用方便,缺點:存在漏洞(4.2之前),請看下面的“WebView的一些漏洞以及如何防止”

          kotlin中定義被js調用的方法
          @JavascriptInterface
          fun hello(name: String) {
          toast("你好,我是來自js的消息:$msg")
          }
          js代碼
          function callAndroid{
          android.hello("我是js的,我來調用你了")
          }
          kotlin中們在webview里面設置Android與js的代碼的映射
          webView?.addJavascriptInterface(this, "android")

          2. 通過webviewClient的shouldOverrideUrlLoading的回調來攔截url

          具體使用:解析該url的協議,如果監測到是預先約定好的協議,那么就調用相應的方法。比較安全,但是使用麻煩,js獲取Android的返回值的話很麻煩,只能通過上面介紹的通過loadurl去執行js代碼把返回值通過參數傳遞回去

          首先在js中約定號協議
          function callAndroid{
          // 約定的url協議為:js://webview?name=wfq&age=24
          document.location="js://webview?name=wfq&age=24"
          }

          在kotlin里面,當loadurl的時候就會回調到shouldOverrideUrlLoading里面

          override fun shouldOverrideUrlLoading(view: WebView?, request: WebResourceRequest?): Boolean {
          val uri=Uri.parse(request?.url.toString)
          // 一般根據scheme(協議格式) & authority(協議名)判斷(前兩個參數)
          //假定傳入進來的 js://webview?name=wfq&age=24
          if (uri.scheme=="js") {
          if (uri.authority=="webview") {
          toast_custom("js調用了Android的方法")
          val queryParameterNames=uri.queryParameterNames
          queryParameterNames.forEach {
          loge(it + ":" + uri.getQueryParameter(it))
          }
          }
          return true
          }
          return super.shouldOverrideUrlLoading(view, request)
          }

          3.通過webChromeClient的onJsAlert,onJsConfirm,onJsPrompt回調來攔截對話框

          通過攔截js對話框,得到他們的消息,然后解析即可,為了安全,建議內容采用上面介紹的url協議, 常用的攔截的話就是攔截prompt,因為它可以返回任意值,alert沒有返回值,confirm只能返回兩種類型,確定和取消

          js代碼
          function clickprompt{
          var result=prompt("wfq://demo?arg1=111&arg2=222");
          alert("demo " + result);
          }

          kotlin代碼
          override fun onJsPrompt(
          view: WebView?,
          url: String?,
          message: String?,
          defaultValue: String?,
          result: JsPromptResult?
          ): Boolean {
          val uri=Uri.parse(message)
          if (uri.scheme=="wfq") {
          if (uri.authority=="demo") {
          toast_custom("js調用了Android的方法")
          val queryParameterNames=uri.queryParameterNames
          queryParameterNames.forEach {
          loge(it + ":" + uri.getQueryParameter(it))
          }
          // 將需要返回的值通過該方式返回
          result?.confirm("js調用了Android的方法成功啦啦啦啦啦")
          }
          return true
          }
          return super.onJsPrompt(view, url, message, defaultValue, result)
          }

          由于攔截了彈框,所以js代碼的alert需要處理 這里的message便是上面代碼的返回值通過alert顯示出來的信息
          override fun onJsAlert(view: WebView?, url: String?, message: String?, result: JsResult?): Boolean {
          AlertDialog.Builder(this@WebActivity)
          .setTitle("JsAlert")
          .setMessage(message)
          .setPositiveButton("OK") { _, _ -> result?.confirm }
          .setCancelable(false)
          .show
          return true
          }

          上面三種方式的區別:

          addJavascriptInterface 方便簡潔,4.0以下存在漏洞,4.0以上通過@JavascriptInterface注解修復漏洞。

          WebViewClient.shouldOverrideUrlLoading回調,不存在漏洞,使用復雜,需要定義協議的約束,但是返回值的話有些麻煩,在不需要返回值的情況下可以使用這個方式。

          通過WebChromeClient的onJsAlerta,onJsConfirm,onJsPrompt,不存在漏洞問題,使用復雜,需要進行協議的約束,可以返回值,能滿足大多數情況下的互調通信。

          WebView的一些漏洞以及如何防止

          密碼明文存儲漏洞

          webview默認開啟了密碼保存功能,在用戶輸入密碼后會彈出提示框詢問用戶是否保存密碼,保存后密碼會被明文保存在 /data/data/com.package.name/databases/webview.db 下面,手機root后可以查看,那么如何解決?

          WebSettings.setSavePassword(false) // 關閉密碼保存提醒功能

          WebView 任意代碼執行漏洞

          addJavascriptInterface漏洞,首先先明白一點,js調用Android代碼的時候,我們經常使用的是addJavascriptInterface, JS調用Android的其中一個方式是通過addJavascriptInterface接口進行對象映射,那么Android4.2之前,既然拿到了這個對象,那么這個對象中的所有方法都是可以調用的,4.2之后,需要被js調用的函數加上@JavascriptInterface注解后來避免該漏洞

          所以怎么解決

          對于Android 4.2以前,需要采用攔截prompt 方式進行漏洞修復

          對于Android 4.2以后,則只需要對被調用的函數以 @JavascriptInterface進行注解

          域控制不嚴格漏洞

          • 原因分析 當我們在Applilcation里面,android:exported="true"的時候,A 應用可以通過 B 應用導出的 Activity 讓 B 應用加載一個惡意的 file 協議的 url,從而可以獲取 B 應用的內部私有文件,從而帶來數據泄露威脅,

          下面來看下WebView中getSettings類的方法對 WebView 安全性的影響 setAllowFileAccess

          // 設置是否允許 WebView 使用 File 協議

          // 默認設置為true,即允許在 File 域下執行任意 JavaScript 代碼

          webView.getSettings.setAllowFileAccess(true);
          如果設置為false的話,便不會存在威脅,但是,webview也無法使用本地的html文件

          setAllowFileAccessFromFileURLs

          // 設置是否允許通過 file url 加載的 Js代碼讀取其他的本地文件
          // 在Android 4.1前默認允許
          // 在Android 4.1后默認禁止
          webView.getSettings.setAllowFileAccessFromFileURLs(true);

          我們應該明確的設置為false,禁止讀取其他文件

          setAllowUniversalAccessFromFileURLs

          // 設置是否允許通過 file url 加載的 Javascript 可以訪問其他的源(包括http、https等源)
          // 在Android 4.1前默認允許(setAllowFileAccessFromFileURLs不起作用)
          // 在Android 4.1后默認禁止
          webView.getSettings.setAllowUniversalAccessFromFileURLs(true);

          WebView預加載以及資源預加載

          為什么需要預加載

          h5頁面加載慢,慢的原因:頁面渲染慢,資源加載慢

          如何優化?

          h5的緩存,資源預加載,資源攔截

          h5的緩存 Android WebView自帶的緩存

          1. 瀏覽器緩存

          根據 HTTP 協議頭里的 Cache-Control(或 Expires)和 Last-Modified(或Etag)

          等字段來控制文件緩存的機制瀏覽器自己實現,我需我們處理


          2. App Cache


          方便構建Web App的緩存,存儲靜態文件(如JS、CSS、字體文件)


          WebSettings settings=getSettings;
          String cacheDirPath=context.getFilesDir.getAbsolutePath+"cache/";
          settings.setAppCachePath(cacheDirPath);
          settings.setAppCacheMaxSize(20*1024*1024);
          settings.setAppCacheEnabled(true);

          3. Dom Storage

          WebSettings settings=getSettings;
          settings.setDomStorageEnabled(true);

          4. Indexed Database

          // 只需設置支持JS就自動打開IndexedDB存儲機制
          // Android 在4.4開始加入對 IndexedDB 的支持,只需打開允許 JS 執行的開關就好了。
          WebSettings settings=getSettings;
          settings.setJavaScriptEnabled(true);
          • 資源預加載 預加載webview對象,首次初始化WebView會比第二次慢很多的原因:初始化后,即使webview已經釋放,但是WebView的一些共享的對象依然是存在的,我們可以在Application里面提前初始化一個Webview的對象,然后可以直接loadurl加載資源

          • 資源攔截 可以將跟新頻率低的一些資源靜態文件放在本地,攔截h5的資源網絡請求并進行檢測,如果檢測到,就直接拿本地的資源進行替換即可


          // 攔截資源 通常用于h5的首頁頁面,將常用的一些資源,放到本地
          override fun shouldInterceptRequest(view: WebView?, request: WebResourceRequest?): WebResourceResponse? {

          if(request?.url.toString.contains("logo.jpg")){
          var inputStream: InputStream?=
          inputStream=applicationContext.assets.open("images/test.jpg")
          return WebResourceResponse("image/png","utf-8", inputStream)
          }

          return super.shouldInterceptRequest(view, request)
          }

          常見的使用注意事項

          1. Android9.0 已經禁止了webview使用http,怎么解決?

          在manifest Application標簽下面使用:

          android:usesCleartextTraffic="true"

          2. 開啟混淆之后,Android無法與h5交互?

          #保留annotation, 例如 @JavascriptInterface 等 annotation
          -keepattributes *Annotation*

          #保留跟 javascript相關的屬性
          -keepattributes JavascriptInterface

          #保留JavascriptInterface中的方法
          -keepclassmembers class * {
          @android.webkit.JavascriptInterface <methods>;
          }
          #這個類是用來與js交互,所以這個類中的 字段 ,方法, 不能被混淆、全路徑名稱.類名
          -keepclassmembers public class com.youpackgename.xxx.H5CallBackAndroid{
          <fields>;
          <methods>;
          public *;
          private *;
          }

          3. 如何調試?

          3.1 在WebViewActivity里面,開啟調試

          // 開啟調試
          WebView.setWebContentsDebuggingEnabled(true)

          3.2 chrome瀏覽器地址欄輸入 chrome://inspect

          3.3 手機打開USB調試,打開webview頁面,點擊chrome頁面的最下面的inspect,這樣,便可以進入了web開發,看控制臺,網絡請求等

          日問題:

          看到WebView是不是很頭疼?

          者:字節移動技術——段文斌

          前言

          眾所周知,精確的推薦離不開大量埋點,常見的埋點采集方案是在響應用戶行為操作的路徑上進行埋點。但是由于 App 通常會有比較多界面和操作路徑,主動埋點的維護成本就會非常大。所以行業的做法是無埋點,而無埋點實現需要 AOP 編程。

          一個常見的場景,比如想在UIViewController出現和消失的時刻分別記錄時間戳用于統計頁面展現的時長。要達到這個目標有很多種方法,但是 AOP 無疑是最簡單有效的方法。Objective-C 的 Hook 其實也有很多種方式,這里以 Method Swizzle 給個示例。

          @interface UIViewController (MyHook)
          
          @end
          
          @implementation UIViewController (MyHook)
          
          + (void)load {
              static dispatch_once_t onceToken;
              dispatch_once(&onceToken, ^{
                  /// 常規的 Method Swizzle封裝
                  swizzleMethods(self, @selector(viewDidAppear:), @selector(my_viewDidAppear:));
                  /// 更多Hook
              });
          }
          
          - (void)my_viewDidAppear:(BOOL)animated {
            /// 一些Hook需要的邏輯
          
            /// 這里調用Hook后的方法,其實現其實已經是原方法了。
            [self my_viewDidAppear: animated];
          }
          
          @end
          

          接下來我們探討一個具體場景:

          UICollectionView或者UITableView是 iOS 中非常常用的列表 UI 組件,其中列表元素的點擊事件回調是通過delegate完成的。這里以UICollectionView為例,UICollectionViewdelegate,有個方法聲明,collectionView:didSelectItemAtIndexPath:,實現這個方法我們就可以給列表元素添加點擊事件。

          我們的目標是 Hook 這個 delegate 的方法,在點擊回調的時候進行額外的埋點操作。

          方案迭代

          方案 1 Method Swizzle

          通常情況下,Method Swizzle 可以滿足絕大部分的 AOP 編程需求。因此首次迭代,我們直接使用 Method Swizzle 來進行 Hook。

          @interface UICollectionView (MyHook)
          
          @end
          
          @implementation UICollectionView (MyHook)
          
          // Hook, setMyDelegate:和setDelegate:交換過
          - (void)setMyDelegate:(id)delegate {
              if (delegate !=nil) {
                  /// 常規Method Swizzle
                  swizzleMethodsXXX(delegate, @selector(collectionView:didSelectItemAtIndexPath:), self, @selector(my_collectionView:didSelectItemAtIndexPath:));
          
              }
          
              [self setMyDelegate:nil];
          }
          
          - (void)my_collectionView:(UICollectionView *)ccollectionView didSelectItemAtIndexPath:(NSIndexPath *)index {
            /// 一些Hook需要的邏輯
          
            /// 這里調用Hook后的方法,其實現其實已經是原方法了。
            [self my_collectionView:ccollectionView didSelectItemAtIndexPath:index];
          }
          
          @end
          

          我們把這個方案集成到 App 里面進行測試驗證,發現沒法辦法驗證通過。

          主要原因 App 是一個龐大的項目,其中引入了非常多的三方庫,比如 IGListKit 等,這些三方庫通常對UICollectionView的使用都進行了封裝,而這些封裝,恰恰導致我們不能使用常規的 Method Swizzle 來 Hook 這個 delegate。直接的原因總結有以下兩點:

          1. setDelegate傳入的對象不是實現UICollectionViewDelegate協議的那個對象

          如圖示,setDelegate傳入的是一個代理對象 proxy,proxy 引用了實際的實現UICollectionViewDelegate協議的delegate,proxy 實際上并沒有實現UICollectionViewDelegate的任何一個方法,它把所有方法都轉發給實際的delegate


          主站蜘蛛池模板: 久久无码人妻精品一区二区三区| 久久亚洲国产精品一区二区| 久久久久久人妻一区二区三区| 国精品无码一区二区三区左线| 无码精品人妻一区| 国产一区二区三区在线看片| 亚洲一区二区三区国产精品| 精品中文字幕一区二区三区四区| 美女福利视频一区二区| 一区二区国产精品| 国产美女视频一区| 亚洲日韩一区精品射精| 精品一区二区三区无码视频 | 欧洲精品一区二区三区在线观看| 无码人妻一区二区三区精品视频 | 国产Av一区二区精品久久| 免费在线视频一区| 亚洲综合无码一区二区| 国产在线观看精品一区二区三区91| 精品一区二区三区水蜜桃| 久久精品动漫一区二区三区| 波多野结衣AV一区二区三区中文| 亚洲国产激情一区二区三区 | 亚洲AV日韩AV一区二区三曲| 精品国产日韩亚洲一区| 大帝AV在线一区二区三区| 国偷自产一区二区免费视频| 久久精品无码一区二区三区不卡| 亚洲国产综合精品一区在线播放| 无码一区二区三区AV免费| 日韩人妻无码一区二区三区综合部| 中文乱码字幕高清一区二区| 国产激情无码一区二区| 一区二区三区在线看| 色多多免费视频观看区一区| 日韩精品人妻一区二区三区四区| 亚洲一区二区在线免费观看| 国产日韩一区二区三免费高清| 国产一区二区三区小向美奈子| 国模视频一区二区| 韩国精品一区二区三区无码视频|