整合營銷服務商

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

          免費咨詢熱線:

          騰訊TBS X5 WebView的簡單使用

          騰訊TBS X5 WebView的簡單使用

          很多APP都嵌套了webview,騰訊的webview的簡單使用。

          • 1
          • 2

          騰訊webview的sdk下載地址

          用法:

          1、布局文件:

          • 1
          • 2
          • 3

          <LinearLayout

          android:layout_width="match_parent"

          android:layout_height="match_parent"

          android:background="@color/white"

          android:orientation="vertical">

          <com.xinggui.wz.chuangjiaoplatform.view.MyCustomViewTitleLayout

          android:id="@+id/custom1_webview"

          android:layout_height="wrap_content"

          android:layout_width="wrap_content"/>

          <ProgressBar

          android:id="@+id/progressBar1"

          style="?android:attr/progressBarStyleHorizontal"

          android:layout_width="match_parent"

          android:layout_height="3dp"

          android:progressDrawable="@drawable/progressbar"

          android:visibility="gone"/>

          <com.tencent.smtt.sdk.WebView

          android:id="@+id/webview"

          android:layout_width="match_parent"

          android:layout_height="match_parent"

          android:scrollbars="none" />

          </LinearLayout>

          • 1
          • 2
          • 3
          • 4
          • 5
          • 6
          • 7
          • 8
          • 9
          • 10
          • 11
          • 12
          • 13
          • 14
          • 15
          • 16
          • 17
          • 18
          • 19
          • 20
          • 21
          • 22

          2、再就是onCreate里面的代碼:

          • 1
          • 2

          myWebView=(WebView) findViewById(R.id.webview);

          progressBar=(ProgressBar) findViewById(R.id.progressBar1);

          initWebView();//初始化webview的設置,注釋寫的很清楚

          String url="你的URL"

          myWebView.loadUrl(url);

          • 1
          • 2
          • 3
          • 4
          • 5

          private void initWebView() {

          WebSettings webSettings=myWebView.getSettings();

          webSettings.setJavaScriptEnabled(true);//支持js

          // myWebView.requestFocusFromTouch();//如果webView中需要用戶手動輸入用戶名、密碼或其他,則webview必須設置支持獲取手勢焦點

          webSettings.setJavaScriptCanOpenWindowsAutomatically(true); //允許js彈窗

          myWebView.setWebViewClient(new WebViewClient() {

          @Override

          public boolean shouldOverrideUrlLoading(WebView webView, String s) {

          webView.loadUrl(s);

          return true;

          }

          });

          myWebView.setWebChromeClient(new WebChromeClient() {

          @Override

          public boolean onJsAlert(WebView webView, String s, String s1, JsResult jsResult) {

          new AlertDialog.Builder(WebViewActivity.this).setTitle("提示消息").setMessage(s1).setPositiveButton("OK", null).show();

          jsResult.confirm(); //不調用,alert只彈出一次

          return true;

          }

          @Override

          public void onProgressChanged(WebView webView, int newProgress) {

          super.onProgressChanged(webView, newProgress);

          if (newProgress==100) {

          progressBar.setVisibility(View.GONE);

          } else {

          progressBar.setVisibility(View.VISIBLE);

          progressBar.setProgress(newProgress);//設置加載進度

          }

          }

          });

          }

          • 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

          @Override

          public boolean onKeyDown(int keyCode, KeyEvent event) {

          //如果不做任何處理,瀏覽網頁,點擊系統“Back”鍵,整個Browser會調用finish()而結束自身,

          // 如果希望瀏覽的網 頁回退而不是推出瀏覽器,需要在當前Activity中處理并消費掉該Back事件。

          if (keyCode==KeyEvent.KEYCODE_BACK && myWebView.canGoBack()) {

          myWebView.goBack();

          return true;

          }

          return super.onKeyDown(keyCode, event);

          }

          • 1
          • 2
          • 3
          • 4
          • 5
          • 6
          • 7
          • 8
          • 9
          • 10

          項目就不貼出來了,代碼很齊全,直接新建項目拷貝就能用

          什么要學習Android與H5互調?

          微信,QQ空間等大量軟件都內嵌了H5,不得不說是一種趨勢。Android與H5互調可以讓我們的實現混合開發,至于混合開發就是在一個App中內嵌一個輕量級的瀏覽器,一部分原生的功能改為Html 5來開發。

          優勢:使用H5實現的功能能夠在不升級App的情況下動態更新,而且可以在Android或iOS的App上同時運行,節約了成本,提高了開發效率。

          原理:其實就是Java代碼和JavaScript之間的調用。

          開局插入一張文章的目錄結構:

          WebView簡介

          要實現Android與H5互調,WebView是一個很重要的控件,WebView可以很好地幫助我們展示html頁面,所以有必要先了解一下WebView。

          一丶WebView常用方法

          • loadUrl
          • 加載界面,其次還有LoadData和LoadDataWithBase方法
          //加載assets目錄下的test.html文件
          webView.loadUrl("file:///android_asset/test.html");
          //加載網絡資源(注意要加上網絡權限)
          webView.loadUrl("http://blog.csdn.net");
          
          • 1
          • 2
          • 3
          • 4
          • 1
          • 2
          • 3
          • 4
          • setWebViewClient(如果用戶設置了WebViewClient,則在點擊新的鏈接以后就不會跳轉到系統瀏覽器了,而是在本WebView中顯示。注意:并不需要覆蓋 shouldOverrideUrlLoading 方法,同樣可以實現所有的鏈接都在 WebView 中打開。)
          • WebViewClient主要用來輔助WebView處理各種通知、請求等事件,通過setWebViewClient方法設置。以下是它的幾種常見用法:
          1. 實現對網頁中超鏈接的攔截(比如如果是極客導航的主頁,則直接攔截轉到百度主頁):
          2. 當點擊頁面中的鏈接后,會在WebView加載URL前回調shouldOverrideUrlLoading(WebView view, String url)方法,一般點擊一個鏈接此方法調用一次。
           webView.setWebViewClient(new WebViewClient(){
          @Override
          public boolean shouldOverrideUrlLoading(WebView view, String url) {
           if("http://www.jikedaohang.com/".equals(url)) {
           view.loadUrl("https://www.baidu.com/");
           }
           
           return true;
           }
           });
          
          • 1
          • 2
          • 3
          • 4
          • 5
          • 6
          • 7
          • 8
          • 9
          • 10
          • 11
          • 1
          • 2
          • 3
          • 4
          • 5
          • 6
          • 7
          • 8
          • 9
          • 10
          • 11
          1. 關于shouldOverrideUrlLoading返回值的誤區:網上很多解釋是return true代表在本WebView中打開鏈接,return false代表調用系統瀏覽器打開鏈接。其實只要設置了WebViewClient,則就不會調用系統瀏覽器。
          2. 那么shouldOverrideUrlLoading的返回值到底代表什么呢?return true,則在打開新的url時WebView就不會再加載這個url了,所有處理都需要在WebView中操作,包含加載;return false,則系統就認為上層沒有做處理,接下來還是會繼續加載這個url的;默認return false。具體的區別展示如下:
          3. 加載百度主頁,設置WebViewClient后,重寫shouldOverrideUrlLoading(WebView view, String url)方法,第一張是返回false的截圖(點擊后正常跳轉),第二章是返回true的截圖(點擊無反應,如果希望能夠跳轉,則需要我們自己進行處理):

          1. 還有一點需要注意的是,如果我們攔截了某個url,那么return false 和 return true區別不大,所以一般建議 return false。
          2. 加載網頁時替換某個資源(比如在加載一個網頁時,需要加載一個logo圖片,而我們想要替換這個logo圖片,用我們assets目錄下的一張圖片替代)
          3. 我們知道我們在加載一個網頁的同時也會加載js,css,圖片等資源,所以會多次調用shouldInterceptRequest方法,我們可以在shouldInterceptRequest中進行圖片替換。
          4. 注意:shouldInterceptRequest有兩個重載:
          5. ①public WebResourceResponse shouldInterceptRequest (WebView view, String url) 【已過時】
          6. ②public WebResourceResponse shouldInterceptRequest (WebView view, WebResourceRequest request)
          7. 這兩種方法主要是第二個參數的不同,WebResourceRequest 將能夠獲取更多的信息,提供了getUrl(),getMethod,getRequestHeaders等方法。這里主要是為了展示效果,使用了第一種回調方法。實現方法如下:
          mWebView.setWebViewClient(new WebViewClient(){
           @Override
           public WebResourceResponse shouldInterceptRequest(WebView view, String url) {
           WebResourceResponse response=null;
           if (url.contains("logo")) {
           try {
           InputStream logo=getAssets().open("logo.png");
           response=new WebResourceResponse("image/png", "UTF-8", logo);
           } catch (IOException e) {
           e.printStackTrace();
           }
           }
           return response;
           }
           });
          
          • 1
          • 2
          • 3
          • 4
          • 5
          • 6
          • 7
          • 8
          • 9
          • 10
          • 11
          • 12
          • 13
          • 14
          • 15
          • 1
          • 2
          • 3
          • 4
          • 5
          • 6
          • 7
          • 8
          • 9
          • 10
          • 11
          • 12
          • 13
          • 14
          • 15
          1. 設置開始加載網頁、加載完成、加載錯誤時處理
          webView.setWebViewClient(new WebViewClient() { 
           
           @Override 
           public void onPageStarted(WebView view, String url, Bitmap favicon) { 
           super.onPageStarted(view, url, favicon); 
           // 開始加載網頁時處理 如:顯示"加載提示" 的加載對話框 
           ...
           } 
           
           @Override 
           public void onPageFinished(WebView view, String url) { 
           super.onPageFinished(view, url); 
           // 網頁加載完成時處理 如:讓 加載對話框 消失 
           ...
           } 
           
           @Override 
           public void onReceivedError(WebView view, int errorCode, String description, String failingUrl) { 
           super.onReceivedError(view, errorCode, description, failingUrl); 
           // 加載網頁失敗時處理 如:提示失敗,或顯示新的界面
           ...
           } 
          }); 
          
          • 1
          • 2
          • 3
          • 4
          • 5
          • 6
          • 7
          • 8
          • 9
          • 10
          • 11
          • 12
          • 13
          • 14
          • 15
          • 16
          • 17
          • 18
          • 19
          • 20
          • 21
          • 22
          • 23
          • 24
          • 1
          • 2
          • 3
          • 4
          • 5
          • 6
          • 7
          • 8
          • 9
          • 10
          • 11
          • 12
          • 13
          • 14
          • 15
          • 16
          • 17
          • 18
          • 19
          • 20
          • 21
          • 22
          • 23
          • 24
          1. 處理https請求,為WebView處理ssl證書設置WebView默認是不處理https請求的,需要在WebViewClient子類中重寫父類的onReceivedSslError函數
          webView.setWebViewClient(new WebViewClient() { 
           
           @Override 
           public void onReceivedSslError(WebView view, SslErrorHandler handler, SslError error) { 
           handler.proceed(); // 接受信任所有網站的證書 
           // handler.cancel(); // 默認操作 不處理 
           // handler.handleMessage(null); // 可做其他處理 
           } 
          }); 
          
          • 1
          • 2
          • 3
          • 4
          • 5
          • 6
          • 7
          • 8
          • 9
          • 1
          • 2
          • 3
          • 4
          • 5
          • 6
          • 7
          • 8
          • 9
          • setWebChromeClient
          • WebChromeClient主要用來輔助WebView處理Javascript的對話框、網站圖標、網站標題以及網頁加載進度等。通過WebView的setWebChromeClient()方法設置。
          1. 顯示頁面加載進度在WebChromeClient子類中重寫父類的onProgressChanged函數,progress表示當前頁面加載的進度,為1至100的整數
          webView.setWebChromeClient(new WebChromeClient() { 
           
           public void onProgressChanged(WebView view, int progress) { 
           setTitle("頁面加載中,請稍候..." + progress + "%"); 
           setProgress(progress * 100); 
           
           if (progress==100) { 
           //... 
           } 
           } 
          }); 
          
          • 1
          • 2
          • 3
          • 4
          • 5
          • 6
          • 7
          • 8
          • 9
          • 10
          • 11
          • 1
          • 2
          • 3
          • 4
          • 5
          • 6
          • 7
          • 8
          • 9
          • 10
          • 11
          1. 加快HTML網頁加載完成速度(默認情況html代碼下載到WebView后,webkit開始解析網頁各個節點,發現有外部樣式文件或者外部腳本文件時,會異步發起網絡請求下載文件,但如果在這之前也有解析到image節點,那勢必也會發起網絡請求下載相應的圖片。在網絡情況較差的情況下,過多的網絡請求就會造成帶寬緊張,影響到css或js文件加載完成的時間,造成頁面空白loading過久。解決的方法就是告訴WebView先不要自動加載圖片,等頁面finish后再發起圖片加載。)
          //1.首先在WebView初始化時添加如下代碼
          if(Build.VERSION.SDK_INT >=19) { 
          /*對系統API在19以上的版本作了兼容。因為4.4以上系統在onPageFinished時再恢復圖片加載時,如果存在多張圖片引用的是相同的src時,會只有一個image標簽得到加載,因而對于這樣的系統我們就先直接加載。*/ webView.getSettings().setLoadsImagesAutomatically(true); 
           } else { 
           webView.getSettings().setLoadsImagesAutomatically(false); 
           } 
           
          //2.在WebView的WebViewClient子類中重寫onPageFinished()方法添加如下代碼: 
           @Override 
          public void onPageFinished(WebView view, String url) { 
           if(!webView.getSettings().getLoadsImagesAutomatically()) { 
           webView.getSettings().setLoadsImagesAutomatically(true); 
           } 
          } 
          
          • 1
          • 2
          • 3
          • 4
          • 5
          • 6
          • 7
          • 8
          • 9
          • 10
          • 11
          • 12
          • 13
          • 14
          • 1
          • 2
          • 3
          • 4
          • 5
          • 6
          • 7
          • 8
          • 9
          • 10
          • 11
          • 12
          • 13
          • 14
          • setDownloadListener
          • 通常webview渲染的界面中含有可以下載文件的鏈接,點擊該鏈接后,應該開始執行下載的操作并保存文件到本地中。
          1. 創建DownloadListener
          class MyDownloadListenter implements DownloadListener{
           @Override
           public void onDownloadStart(String url, String userAgent,String contentDisposition, String mimetype, long contentLength) {
           //下載任務...,主要有兩種方式
           //(1)自定義下載任務
           //(2)調用系統的download的模塊
           Uri uri=Uri.parse(url);
           Intent intent=new Intent(Intent.ACTION_VIEW, uri);
           startActivity(intent);
           }
          }
          
          • 1
          • 2
          • 3
          • 4
          • 5
          • 6
          • 7
          • 8
          • 9
          • 10
          • 11
          • 1
          • 2
          • 3
          • 4
          • 5
          • 6
          • 7
          • 8
          • 9
          • 10
          • 11
          1. 給webview加入監聽
          webview.setDownloadListener(new MyDownloadListenter());
          
          • 1
          • 1
          • goBack()
          • 返回上一瀏覽頁面,通過重寫onKeyDown方法實現點擊返回鍵返回上一瀏覽頁面而非退出程序
          public boolean onKeyDown(int keyCode, KeyEvent event) { 
          //其中webView.canGoBack()在webView含有一個可后退的瀏覽記錄時返回true
           
           if ((keyCode==KeyEvent.KEYCODE_BACK) && webView.canGoBack()) { 
           webView.goBack(); 
           return true; 
           } 
           return super.onKeyDown(keyCode, event); 
           }
          }
          
          • 1
          • 2
          • 3
          • 4
          • 5
          • 6
          • 7
          • 8
          • 9
          • 10
          • 1
          • 2
          • 3
          • 4
          • 5
          • 6
          • 7
          • 8
          • 9
          • 10

          二丶WebSettings配置

          1. 獲取WebSettings對象

          WebSettings webSettings=webView.getSettings();

          1. 常用設置方法
          2. (1)支持js

          settings.setJavaScriptEnabled(true);

          1. (2)設置緩存方式,主要有以下幾種:
          2. LOAD_CACHE_ONLY: 不使用網絡,只讀取本地緩存數據。
          3. LOAD_DEFAULT: 根據cache-control決定是否從網絡上取數據。
          4. LOAD_CACHE_NORMAL: API level 17中已經廢棄, 從API level 11開始作用同LOAD_DEFAULT模式。
          5. LOAD_NO_CACHE: 不使用緩存,只從網絡獲取數據。
          6. LOAD_CACHE_ELSE_NETWORK:只要本地有,無論是否過期,或者no-cache,都使用緩存中的數據。

          settings.setCacheMode(WebSettings.LOAD_NO_CACHE);

          1. (3)開啟DOM storage API功能(HTML5 提供的一種標準的接口,主要將鍵值對存儲在本地,在頁面加載完畢后可以通過 JavaScript 來操作這些數據。)

          settings.setDomStorageEnabled(true);

          1. (4)設置數據庫緩存路徑

          settings.setDatabasePath(cacheDirPath);

          1. (5)設置Application Caches緩存目錄

          settings.setAppCachePath(cacheDirPath);

          1. (6)設置默認編碼

          settings.setDefaultTextEncodingName(“utf-8”);

          1. (7)將圖片調整到適合webview的大小

          settings.setUseWideViewPort(false);

          1. (8)支持縮放

          settings.setSupportZoom(true);

          1. (9)支持內容重新布局

          settings.setLayoutAlgorithm(WebSettings.LayoutAlgorithm.SINGLE_COLUMN);

          1. (10)多窗口

          settings.supportMultipleWindows();

          1. (11)設置可以訪問文件

          settings.setAllowFileAccess(true);

          1. (12)當webview調用requestFocus時為webview設置節點

          settings.setNeedInitialFocus(true);

          1. (13)設置支持縮放

          settings.setBuiltInZoomControls(true);

          1. (14)支持通過JS打開新窗口

          settings.setJavaScriptCanOpenWindowsAutomatically(true);

          1. (15)縮放至屏幕的大小

          settings.setLoadWithOverviewMode(true);

          1. (16)支持自動加載圖片

          settings.setLoadsImagesAutomatically(true);

          三丶WebViewClient 的回調方法列表

          WebViewClient主要用來輔助WebView處理各種通知、請求等事件,通過setWebViewClient方法設置。

          (1)更新歷史記錄

          doUpdateVisitedHistory(WebView view, String url, boolean isReload)

          (2)應用程序重新請求網頁數據

          onFormResubmission(WebView view, Message dontResend, Message resend)

          (3)在加載頁面資源時會調用,每一個資源(比如圖片)的加載都會調用一次。

          onLoadResource(WebView view, String url)

          (4)開始載入頁面調用,通常我們可以在這設定一個loading的頁面,告訴用戶程序在等待網絡響應。

          onPageStarted(WebView view, String url, Bitmap favicon)

          (5)在頁面加載結束時調用。同樣道理,我們知道一個頁面載入完成,于是我們可以關閉loading 條,切換程序動作。

          onPageFinished(WebView view, String url)

          (6)報告錯誤信息

          onReceivedError(WebView view, int errorCode, String description, String failingUrl)

          (7)獲取返回信息授權請求

          onReceivedHttpAuthRequest(WebView view, HttpAuthHandler handler, String host,String realm)

          (8)重寫此方法可以讓webview處理https請求。

          onReceivedSslError(WebView view, SslErrorHandler handler, SslError error)

          (9)WebView發生改變時調用

          onScaleChanged(WebView view, float oldScale, float newScale)

          (10)Key事件未被加載時調用

          onUnhandledKeyEvent(WebView view, KeyEvent event)

          (11)重寫此方法才能夠處理在瀏覽器中的按鍵事件。

          shouldOverrideKeyEvent(WebView view, KeyEvent event)

          (12)在網頁跳轉時調用,這個函數我們可以做很多操作,比如我們讀取到某些特殊的URL,于是就可以不打開地址,取消這個操作,進行預先定義的其他操作,這對一個程序是非常必要的。

          shouldOverrideUrlLoading(WebView view, String url)

          (13)在加載某個網頁的資源的時候多次調用(已過時)

          shouldInterceptRequest(WebView view, String url)

          (14)在加載某個網頁的資源的時候多次調用

          shouldInterceptRequest(WebView view, WebResourceRequest request)

          注意:

          shouldOverrideUrlLoading在網頁跳轉的時候調用,且一般每跳轉一次只調用一次。

          shouldInterceptRequest只要是網頁加載的過程中均會調用,資源加載的時候都會回調該方法,會多次調用。

          四丶WebChoromeClient的回調方法列表

          WebChromeClient主要用來輔助WebView處理Javascript的對話框、網站圖標、網站標題以及網頁加載進度等。通過WebView的setWebChromeClient()方法設置。

          (1)監聽網頁加載進度

          onProgressChanged(WebView view, int newProgress)

          (2)監聽網頁標題 : 比如百度頁面的標題是“百度一下,你就知道”

          onReceivedTitle(WebView view, String title)

          (3)監聽網頁圖標

          onReceivedIcon(WebView view, Bitmap icon)

          Java和JavaScript互調

          為方便展示,使用addJavascriptInterface方式實現與本地js交互(存在漏洞)。也可通過其他方式實現,比如攔截ur進行參數解析l等。

          Java調JS

          • 首先是JS的一段代碼:
          function javaCallJs(arg){
           document.getElementById("content").innerHTML=("歡迎:"+arg );
           }
          
          • 1
          • 2
          • 3
          • 4
          • 1
          • 2
          • 3
          • 4
          • 然后是在java中調用JS中的方法
          webView.loadUrl("javascript:javaCallJs("+"'"+name+"'"+")");
          
          • 1
          • 1

          以上代碼就是調用了JS中一個叫javaCallJs(arg)的方法,并傳入了一個name參數。(具體效果下面有展示)

          JS調java

          • 配置Javascript接口
          webView.addJavascriptInterface(new JSInterface (),"Android");
          
          • 1
          • 2
          • 1
          • 2
          • 實現Javascript接口類
          class JSInterface {
           @JavascriptInterface
           public void showToast(String arg){
           Toast.makeText(MainActivity.this,arg,Toast.LENGTH_SHORT).show();
           }
          }
          
          • 1
          • 2
          • 3
          • 4
          • 5
          • 6
          • 1
          • 2
          • 3
          • 4
          • 5
          • 6
          • JS中調用java代碼
          <input type="button" value="點擊Android被調用" onclick="window.Android.showToast('JS中傳來的參數')"/>
          
          • 1
          • 1

          window.Android.showToast(‘JS中傳來的參數’)”中的”Android”即addJavascriptInterface()中指定的,并且JS向java傳遞了參數,類型為String。而showToast(String arg)會以Toast的形式彈出此參數。

          java與JS互調代碼示例

          先看效果圖:

          不好意思,傳錯了,是這張:

          代碼非常簡單,并且加了注釋,直接看代碼就可以了。

          • 首先是本地的JavaAndJavaScriptCall.html文件,放在asstes目錄下
          <html>
          <head>
           <meta http-equiv="Content-Type" content="text/html;charset=UTF-8">
           <script type="text/javascript">
           
           function javaCallJs(arg){
           document.getElementById("content").innerHTML=("歡迎:"+arg );
           }
           
           </script>
          </head>
          <body>
           <div id="content"> 請在上方輸入您的用戶名</div>
           <input type="button" value="點擊Android被調用" onclick="window.Android.showToast('JS中傳來的參數')"/>
          </body>
          </html>
          
          • 1
          • 2
          • 3
          • 4
          • 5
          • 6
          • 7
          • 8
          • 9
          • 10
          • 11
          • 12
          • 13
          • 14
          • 15
          • 16
          • 17
          • 1
          • 2
          • 3
          • 4
          • 5
          • 6
          • 7
          • 8
          • 9
          • 10
          • 11
          • 12
          • 13
          • 14
          • 15
          • 16
          • 17

          javaCallJs是java調用JS的方法,showToast方法是JS調用java的方法

          • 接下來是布局文件,activity_main.xml
          <?xml version="1.0" encoding="utf-8"?>
          <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
           xmlns:tools="http://schemas.android.com/tools"
           android:id="@+id/ll_root"
           android:layout_width="match_parent"
           android:layout_height="match_parent"
           android:orientation="vertical"
           tools:context=".MainActivity">
           <LinearLayout
           android:layout_width="match_parent"
           android:layout_height="wrap_content"
           android:orientation="horizontal"
           android:padding="20dp"
           android:background="#000088">
           <EditText
           android:id="@+id/et_user"
           android:layout_width="0dp"
           android:layout_height="wrap_content"
           android:hint="輸入WebView中要顯示的用戶名"
           android:background="#008800"
           android:textSize="16sp"
           android:layout_weight="1"/>
           <Button
           android:layout_width="wrap_content"
           android:layout_height="wrap_content"
           android:layout_marginLeft="40dp"
           android:layout_marginRight="20dp"
           android:textSize="16sp"
           android:text="確定"
           android:onClick="click"/>
           </LinearLayout>
           
          </LinearLayout>
          
          • 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
          • 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

          很簡單,就是一個輸入框和一個確定按鈕,點擊按鈕會調用JS中的方法。

          • MainActivity
          package com.wangjian.webviewdemo;
           
          import android.annotation.SuppressLint;
          import android.support.v7.app.AppCompatActivity;
          import android.os.Bundle;
          import android.view.View;
          import android.view.ViewGroup;
          import android.webkit.JavascriptInterface;
          import android.webkit.WebSettings;
          import android.webkit.WebView;
          import android.webkit.WebViewClient;
          import android.widget.EditText;
          import android.widget.LinearLayout;
          import android.widget.Toast;
           
          public class MainActivity extends AppCompatActivity {
           
           private WebView webView;
           private LinearLayout ll_root;
           private EditText et_user;
           
           @Override
           protected void onCreate(Bundle savedInstanceState) {
           super.onCreate(savedInstanceState);
           setContentView(R.layout.activity_main);
           ll_root=(LinearLayout) findViewById(R.id.ll_root);
           et_user=(EditText) findViewById(R.id.et_user);
           initWebView();
           }
           
           //初始化WebView
           
           private void initWebView() {
           //動態創建一個WebView對象并添加到LinearLayout中
           webView=new WebView(getApplication());
           LinearLayout.LayoutParams params=new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT);
           webView.setLayoutParams(params);
           ll_root.addView(webView);
           //不跳轉到其他瀏覽器
           webView.setWebViewClient(new WebViewClient() {
           @Override
           public boolean shouldOverrideUrlLoading(WebView view, String url) {
           view.loadUrl(url);
           return true;
           }
           });
           WebSettings settings=webView.getSettings();
           //支持JS
           settings.setJavaScriptEnabled(true);
           //加載本地html文件
           webView.loadUrl("file:///android_asset/JavaAndJavaScriptCall.html");
           webView.addJavascriptInterface(new JSInterface(),"Android");
           }
           
           //按鈕的點擊事件
           public void click(View view){
           //java調用JS方法
           webView.loadUrl("javascript:javaCallJs(" + "'" + et_user.getText().toString()+"'"+")");
           }
           
           //在頁面銷毀的時候將webView移除
           @Override
           protected void onDestroy() {
           super.onDestroy();
           ll_root.removeView(webView);
           webView.stopLoading();
           webView.removeAllViews();
           webView.destroy();
           webView=null;
           }
           
           private class JSInterface {
           //JS需要調用的方法
           @JavascriptInterface
           public void showToast(String arg){
           Toast.makeText(MainActivity.this,arg,Toast.LENGTH_SHORT).show();
           }
           }
          }
          
          • 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
          • 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

          需要注意的地方

          參考鏈接:安卓webview的一些坑

          1. webView.addJavascriptInterface()方法在API 17之前有一些漏洞(有興趣的可以參考本篇文章,WebView 遠程代碼執行漏洞淺析),所以在API 17以后,需要在JavaScript接口類的方法加上@JavascriptInterface注解。
          2. 仔細看的話你會發現我們上面的WebView對象并不是直接寫在布局文件中的,而是通過一個LinearLayout容器,使用addview(webview)動態向里面添加的。另外需要注意創建webview需要使用applicationContext而不是activity的context,銷毀時不再占有activity對象,最后離開的時候需要及時銷毀webview,onDestory()中應該先從LinearLayout中remove掉webview,再調用webview.removeAllViews();webview.destory();
          3. 如果想要webView在產生OOM的時候不影響主進程,可以另開一個進程,在androidmanifest.xml的activity標簽里加上Android:process屬性就可以了。
          4. 在activity被殺死之后,依然保持webView的狀態,方便用戶下次打開的時候可以回到之前的狀態。webview支持saveState(bundle)和restoreState(bundle)方法。
          5. 保存狀態
          @Override 
          protected void onSaveInstanceState(Bundle outState) { 
           super.onSaveInstanceState(outState); 
           wv.saveState(outState); 
           Log.e(TAG, "save state..."); 
          } 
          
          • 1
          • 2
          • 3
          • 4
          • 5
          • 6
          • 1
          • 2
          • 3
          • 4
          • 5
          • 6

          恢復狀態(在activity的onCreate(bundle savedInstanceState)里)

          if(null!=savedInstanceState){ 
           wv.restoreState(savedInstanceState); 
           Log.i(TAG, "restore state"); 
          }else{ 
           wv.loadUrl("http://3g.cn"); 
          } 
          
          • 1
          • 2
          • 3
          • 4
          • 5
          • 6
          • 1
          • 2
          • 3
          • 4
          • 5
          • 6

          其他一些常見問題:

          1. WebViewClient.onPageFinished()。

          你永遠無法確定當WebView調用這個方法的時候,網頁內容是否真的加載完畢了。當前正在加載的網頁產生跳轉的時候這個方法可能會被多次調用,StackOverflow上有比較具體的解釋(How to listen for a Webview finishing loading a URL in Android?), 但其中列舉的解決方法并不完美。所以當你的WebView需要加載各種各樣的網頁并且需要在頁面加載完成時采取一些操作的話,可能WebChromeClient.onProgressChanged()比WebViewClient.onPageFinished()都要靠譜一些。

          2. WebView后臺耗電問題。

          當你的程序調用了WebView加載網頁,WebView會自己開啟一些線程(?),如果你沒有正確地將WebView銷毀的話,這些殘余的線程(?)會一直在后臺運行,由此導致你的應用程序耗電量居高不下。對此我采用的處理方式比較偷懶,簡單又粗暴(不建議),即在Activity.onDestroy()中直接調用System.exit(0),使得應用程序完全被移出虛擬機,這樣就不會有任何問題了。

          3. 切換WebView閃屏問題。

          如果你需要在同一個ViewGroup中來回切換不同的WebView(包含了不同的網頁內容)的話,你就會發現閃屏是不可避免的。這應該是Android硬件加速的Bug,如果關閉硬件加速這種情況會好很多,但無法獲得很好的瀏覽體驗,你會感覺網頁滑動的時候一卡一卡的,不跟手。

          4. 在某些手機上,Webview有視頻時,activity銷毀后,視頻資源沒有被銷毀,甚至還能聽到在后臺播放。即便是像剛才那樣各種銷毀webview也無濟于事,解決辦法:在onDestory之前修改url為空地址。

          5.WebView硬件加速導致頁面渲染閃爍問題

          關于Android硬件加速 開始于Android 3.0 (API level 11),開啟硬件加速后,WebView渲染頁面更加快速,拖動也更加順滑。但有個副作用就是容易會出現頁面加載白塊同時界面閃爍現象。解決這個問題的方法是設置WebView暫時關閉硬件加速 代碼如下:

          if (Build.VERSION.SDK_INT >=Build.VERSION_CODES.HONEYCOMB) {

          webview.setLayerType(View.LAYER_TYPE_SOFTWARE, null);

          }

          JavaScript簡介

          JavaScript是一種動態類型的腳本語言;在1995年時,由Netscape公司的Brendan Eich,在網景導航者瀏覽器上首次設計實現而成。因為Netscape與Sun合作,Netscape管理層希望它外觀看起來像Java,因此取名為JavaScript。

          JavaScript腳本語言具有以下特點:

          (1) 腳本語言。JavaScript是一種解釋型的腳本語言,是在程序的運行過程中逐行進行解釋執行,不需要預編譯。而Java、C++等語言需要先編譯后執行;

          (2) 動態性。JavaScript能夠動態修改對象的屬性,沒有辦法在編譯的時候知道變量的類型,只有在運行的時候才能確定。而Java、C++等都是靜態類型語言,他們在編譯的時候就能夠知道每個變量的類型;

          (3) 跨平臺性。JavaScript腳本語言不依賴于操作系統,僅需要瀏覽器的支持。可以在多種平臺下運行(如Windows、Linux、Mac、Android、IOS等)。

          JavaScript與Java語言區別

          從上面介紹的JavaScript語言特點會發現JavaScript的效率會比Java、C++低很多;看以下這個實例:

          當JavaScript引擎分析到該段代碼時,根本不知道a和b是什么類型,唯一的辦法就是運行的時候根據實際傳過來的對象再來計算,這顯然會導致嚴重的性能問題。

          當編譯上面Java代碼的時候,根據右邊類型Class1的定義,獲取對象a的屬性x的時候,其實就是對象a的地址,大小是一個整形。同時獲取對象b的屬性y的時候,其實就是對象b的地址加上4個字節,這些都是在生成本地代碼時確定的,無需在運行本地代碼的時候再決定他們的地址和類型是什么,這顯然能夠節省時間。

          再看一下兩者分別是怎樣存儲對象a和b的:

          對于傳統的JavaScript解釋器來說,因為不知道a和b的具體類型,就用屬性名-屬性值對來保存,之后訪問對象的屬性值時就需要通過屬性名匹配來獲取對應的值;對象b也是同樣的結果來保存相同的屬性。隨著對象的增多,這顯然帶來了巨大的空間浪費。

          而上面的Java代碼在編譯時就確定了類Class1的成員類型,訪問x就是對象a的地址,y就是a的地址加上4個字節;所以字符“x”和“y”運行時都不再需要,因為不再需要額外查找這些屬性地址的工作。

          從上面實例可以看到JavaScript和Java語言的區別包括以下幾個部分:

          編譯確定位置:Java有編譯和執行兩個階段,位置的偏移信息都是在編譯器編譯的時候決定的,當Java生成本地代碼之后,對象的屬性和偏移信息都計算完成。而JavaScript沒有類型,只有在對象執行創建的時候才確定這些信息,而且JavaScript語言能夠在執行時修改對象的屬性。

          偏移信息共享:Java有類型定義,所有的對象都是共享偏移信息的,訪問他們只需要按照編譯時確定的偏移量即可。JavaScript則不同,每個對象都有自我描述,屬性和位置偏移信息都包含在自身的結構中。

          偏移信息查找:Java查找偏移地址很簡單,都是在編譯代碼時,對使用到的類型成員變量直接設置偏移量。而JavaScript則需要通過屬性名匹配才能查找到對應的值。

          Java語言有明顯的兩個階段:編譯和運行,如下圖所示:

          Java代碼經過編譯器編譯之后生成的是字節碼,字節碼是跨平臺的一種中間表示,不同于本地代碼。該字節碼與平臺無關,能夠在不同的操作系統上運行。在運行字節碼階段,Java的運行環境是Java虛擬機加載字節碼。Java虛擬機一般都引入JIT技術來將字節碼轉變成本地代碼來提高執行效率。第一階段對時間要求不嚴格,第二階段對每個步驟所花費的時間非常敏感,時間越短越好。

          JavaScript語言的編譯和執行都是在運行階段執行的,如下圖所示:

          因為都是在代碼運行過程中來處理這些步驟,所以每個階段的時間越短越好,而且每引入一個階段都是額外的時間開銷,所以一個JavaScript引擎主要包含以下幾個部分:

          • 編譯器:主要工作是將源代碼編譯成抽象語法樹;

          • 解釋器:主要是接受字節碼,解釋執行這個字節碼;

          • JIT工具:將字節碼或抽象語法樹轉換成本地代碼;

          • 垃圾回收期和分析工具(Profiler):負責垃圾回收和收集引擎中的信息,幫助改善引擎的性能。


          V8引擎介紹

          V8是一個JavaScript引擎實現的開源項目,最開始由一些語言學家設計出來,后被Google收購,成為了JavaScript引擎和眾多相關技術的引領者。V8支持眾多的操作系統,包括Windows、Linux、Android、Mac OS X等。同時它也能夠支持眾多的硬件架構,如IA32、X64、ARM、MIPS等,將主流軟硬件平臺一網打盡。由于它是一個開源項目,開發者可以自由使用它的強大能力,目前炙手可熱的NodeJs項目就是基于V8項目研發的。

          1. 調用V8編程接口的例子和對應的內存管理方式

          第一條語句:表示建立一個域,用于包含一組Handle對象,便于管理和釋放它們;

          第二條語句:根據Isolate對象來獲取一個Context對象,使用Handle來管理。Handle對象本身存放在棧上,而實際的Context對象保存在堆中;

          第三條語句:根據兩個對象Isolate和Context來創建一個函數間使用的對象,使用Persistent類來管理;

          第四條語句:表示為Context對象創建一個基于棧的域,下面的執行步驟都是在該域中對應的上下文中來進行的;

          第五條語句:讀入一段JavaScript代碼;

          第六條語句:將代碼字符串編譯成V8的內部表示,并保存成一個Script對象;

          第七條語句:執行編譯后的內部表示,獲得生成的結果。

          2. V8的編譯

          首先通過編譯器將源代碼編譯成抽象語法樹:

          不同于JavaScriptCore引擎,V8引擎并不將抽象語法樹轉變成字節碼,而是通過JIT編譯器的全代碼生成器從抽象語法樹直接生成本地代碼。

          其過程中的主要類圖如下:

          Script:表示的是JavaScript代碼,既包含源代碼,又包含編譯之后生成的本地代碼,所以它既是編譯入口,又是運行入口;

          Compiter:編譯器類,輔助Script類來編譯生成代碼,它主要起一個協調者的作用,會調用解析器(Parse)來生成抽象語法樹和全代碼生成器,來為抽象語法樹生成本地代碼;

          Parse:將源代碼解析并構建成抽象語法樹,使用AstNodeFactory類來創建他們,并使用Zone類來分配內存;

          AstNode:抽象語法樹節點類,是其他所有節點的基類;

          AstVisitor:抽象語法樹的訪問者類,主要用來遍歷抽象語法樹;

          FullCodeGenerator:AstVisitor類的子類,通過遍歷抽象語法樹來為JavaScript生成本地可執行的代碼。

          3. V8運行

          V8運行階段的主要類圖如下:

          Script:前面介紹過,包含編譯之后生成的本地代碼,運行代碼的入口;

          Execution:運行代碼的輔助類,包含一些重要的函數“call”,它輔助進入和執行Script中的本地代碼;

          JSFunction:需要執行的JavaScript函數表示類;

          Runtime:運行本地代碼的輔助類,主要提供運行時各種輔助函數;

          Heap:運行本地代碼需要使用的內存堆;

          MarkCompactCollector:垃圾回收機制的主要實現類,用來標記,清除和整理等基本的垃圾回收過程;

          SweeperThread:負責垃圾回收的線程。

          V8中代碼的執行過程如下圖:

          V8引擎所做優化

          1. 優化回滾

          Crankshaft編譯器主要針對熱點函數進行優化,它是基于JS源碼分析的,而不是本地代碼。為了性能考慮Crankshaft編譯器會進行一些樂觀的預測,認為這些代碼比較穩定,變量類型不會發生變化,所以能夠生成高效的本地代碼。然而進行優化之后,V8發現并不是最優的,會執行優化回滾操作。

          2. 隱藏類

          將對象劃分成不同的組,相同的組內對象擁有相同的屬性名和屬性值,組內的所有對象貢獻該信息:

          實例中對象a和b包含相同的屬性名,V8就會將其歸為同一個組,也就是隱藏類。這些屬性在隱藏類中有相同的偏移值,這樣,對象a和b可以共享這個類型信息,當訪問這些對象屬性的時候,根據隱藏類的偏移值就可以知道它們的位置并進行訪問。

          3. 內存管理

          V8使用堆來管理JavaScript使用的數據,以及生成的代碼、哈希表等;為了更方便地實現垃圾回收,同很多虛擬機一樣,V8將堆分成三個部分,第一個是年輕分代,第二個是年老分代,第三個是大對象保留的空間;如下圖:

          4. 快照(Snapshot)

          V8引擎開始啟動的時候,需要加載很多內置的全局對象,同時也要建立內置的函數,比如Array、String、Math等。為了讓引擎更加整潔,加載對象與建立函數等任務都是使用JS文件來實現的,V8引擎負責在編譯和執行輸入的JavaScript代碼之前,先加載他們。

          快照機制就是將一些內置的對象和函數加載之后的內存保存并序列化。序列化之后的結果很容易被反序列化,經過快照機制的啟動時間,可以縮短啟動時間。快照機制也能夠將開發者認為需要的JS文件序列化,減少以后處理的時間。

          5. 綁定和擴展

          V8提供兩種機制來擴展引擎的能力,第一是Extension機制,就是通過V8提供的基類Extension來達到擴展JavaScript能力的目的。第二是綁定,使用IDL文件或者接口文件來生成綁定文件,然后將這些文件同V8引擎代碼一起編譯。

          實踐——寫JavaScript需要注意地方

          1. 不要破壞隱藏類

          建議:在構造函數中初始化所有對象成員,不要在以后更改類型;以相同的順序初始化對象成員。

          2. 數據表示

          在V8中,數據的表示分成兩個部分,第一個部分是數據的實際內容,它們是變長的;第二部分是數據的句柄,句柄的大小是固定的,句柄中包含指向數據的指針。為什么要這樣設計呢?主要是因為V8需要進行垃圾回收,并需要移動這些數據內容,如果直接使用指針的話就會出問題或者需要比較大的開銷,使用句柄的話就不存在這些問題,只需要將句柄中的指針修改即可。

          具體的定義如下:

          一個Handler的大小是4字節(32位機器),整數直接從value_中獲取值,而無需從堆中分配,然后分配一個指針指向它,這可以減少內存的使用并增加數據的訪問速度。

          所以,對于數值來說,只要能夠使用整數的,盡量不要使用浮點數。

          3. 數組初始化

          建議:初始化使用數組常量小型固定大小的數組;

          不要儲存在數字數組非數字值(對象);

          不要刪除數組中的元素,尤其是數字數組;

          不要裝入未初始化或刪除元素。

          4. 內存

          對引用不再使用的對象的變量設置為空(a=),引入delete關鍵字,刪除無用對象。

          5. 優化回滾

          不要書寫出觸發優化回滾的代碼,否則會大幅降低代碼的性能。執行多次之后,不要出現修改對象類型的語句。


          主站蜘蛛池模板: 麻豆果冻传媒2021精品传媒一区下载| 合区精品久久久中文字幕一区 | 无码一区二区三区视频| 国产精品一级香蕉一区| 亚洲第一区视频在线观看| 久久se精品一区精品二区| 无码国产精品一区二区免费式影视| 国精产品一区一区三区免费视频 | 亚洲A∨精品一区二区三区| 精品国产日韩亚洲一区| bt7086福利一区国产| 亚洲片一区二区三区| 中文字幕av一区| 亚洲日韩精品无码一区二区三区 | 国产麻豆精品一区二区三区v视界| 亚洲永久无码3D动漫一区| 精品国产一区二区三区麻豆| 日韩一区二区三区视频| 国产精品视频一区二区三区经| 久久久不卡国产精品一区二区| 无码中文字幕一区二区三区| 亚洲国产精品一区二区第一页 | 国产成人无码AV一区二区| 亚洲丶国产丶欧美一区二区三区| 国产美女精品一区二区三区| 一区二区三区福利视频| 国产精品亚洲一区二区三区久久 | 手机看片福利一区二区三区| 亚洲香蕉久久一区二区| 狠狠色婷婷久久一区二区三区 | 在线观看日本亚洲一区| 日本高清不卡一区| 亚洲av片一区二区三区| 国产午夜精品片一区二区三区| 亚洲性日韩精品国产一区二区| 精品无码一区二区三区电影| 亚洲日韩精品无码一区二区三区| 免费看AV毛片一区二区三区| 无码国产精品一区二区免费16| 国产福利视频一区二区| 国产丝袜美女一区二区三区|