作者微信 bishe2022

代码功能演示视频在页面下方,请先观看;如需定制开发,联系页面右侧客服
WebView详解与简单实现Android与H5互调

Custom Tab

为什么要学习Android与H5互调?

原理:其实就是Java代码和JavaScript之间的调用。

开局插入一张文章的目录结构:
这里写图片描述

WebView简介

要实现Android与H5互调,WebView是一个很重要的控件,WebView可以很好地帮助我们展示html页面,所以有必要先了解一下WebView。

一丶WebView常用方法

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);       
    }
}

二丶WebSettings配置

  1. 获取WebSettings对象

    WebSettings webSettings = webView.getSettings();


    常用设置方法

    (1)支持js

    settings.setJavaScriptEnabled(true);

    (2)设置缓存方式,主要有以下几种:
    LOAD_CACHE_ONLY: 不使用网络,只读取本地缓存数据。
    LOAD_DEFAULT: 根据cache-control决定是否从网络上取数据。
    LOAD_CACHE_NORMAL: API level 17中已经废弃, 从API level 11开始作用同LOAD_DEFAULT模式。
    LOAD_NO_CACHE: 不使用缓存,只从网络获取数据。
    LOAD_CACHE_ELSE_NETWORK:只要本地有,无论是否过期,或者no-cache,都使用缓存中的数据。

    settings.setCacheMode(WebSettings.LOAD_NO_CACHE);

    (3)开启DOM storage API功能(HTML5 提供的一种标准的接口,主要将键值对存储在本地,在页面加载完毕后可以通过 JavaScript 来操作这些数据。)

    settings.setDomStorageEnabled(true);

    (4)设置数据库缓存路径

    settings.setDatabasePath(cacheDirPath);

    (5)设置Application Caches缓存目录

    settings.setAppCachePath(cacheDirPath);

    (6)设置默认编码

    settings.setDefaultTextEncodingName(“utf-8”);

    (7)将图片调整到适合webview的大小

    settings.setUseWideViewPort(false);

    (8)支持缩放

    settings.setSupportZoom(true);

    (9)支持内容重新布局

    settings.setLayoutAlgorithm(WebSettings.LayoutAlgorithm.SINGLE_COLUMN);

    (10)多窗口

    settings.supportMultipleWindows();

    (11)设置可以访问文件

    settings.setAllowFileAccess(true);

    (12)当webview调用requestFocus时为webview设置节点

    settings.setNeedInitialFocus(true);

    (13)设置支持缩放

    settings.setBuiltInZoomControls(true);

    (14)支持通过JS打开新窗口

    settings.setJavaScriptCanOpenWindowsAutomatically(true);

    (15)缩放至屏幕的大小

    settings.setLoadWithOverviewMode(true);

    (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)在加载页面资源时会调用,每一个资源(比如图片)的加载都会调用一次。

" list-paddingleft-2">

  • 首先是JS的一段代码:

  • function javaCallJs(arg){
             document.getElementById("content").innerHTML =
                 ("欢迎:"+arg );
        }

    • 然后是在java中调用JS中的方法

    webView.loadUrl("javaCallJs("+"'"+name+"'"+")");

    以上代码就是调用了JS中一个叫javaCallJs(arg)的方法,并传入了一个name参数。(具体效果下面有展示)

    JS调java

    • 配置Javascript接口

    webView.addJavascriptInterface(new JSInterface (),"Android");

    • 实现Javascript接口类

    class JSInterface {
        @JavascriptInterface
         public void showToast(String arg){                   
         Toast.makeText(MainActivity.this,arg,Toast.LENGTH_SHORT).show();
         }
    }

    • JS中调用java代码

    <input type="button" value="点击Android被调用" onclick="window.Android.showToast('JS中传来的参数')"/>

    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>

    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>

    很简单,就是一个输入框和一个确定按钮,点击按钮会调用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("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();
            }
        }
    }

    需要注意的地方

    参考链接:安卓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)方法。

      保存状态

    @Override  protected void onSaveInstanceState(Bundle outState) 
    {  
        super.onSaveInstanceState(outState);  
        wv.saveState(outState);  
        Log.e(TAG, "save state...");  
    }

    恢复状态(在activity的onCreate(bundle savedInstanceState)里)

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

    其他一些常见问题:

    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);
    }




    转载自:http://blog.csdn.net/qq_24530405/article/details/52067474

    Home