Android WebView与H5交互方式混合开发

前言

所谓这个混合开发,也就是比较流行hybird,就是一些简单的html5和native 代码之间的交互。很多电商之类的app里面都有类似的功能,其优点就是可以实现跨平台,有新功能也或bug不需要再重新发版本。

概括

本章介绍基础属性WebSettings的使用,它可以设置webview所支持的功能,如页码缩放、支持JS交互、支持多窗口等。另外,webview自身也公开了一些方法提供调用,如加载某个Url网页、页码回退和前进、拦截url等。重点讲解Android与JS交互技术,这也是面试最必问的技术点。最后简单介绍了webview的优化及缓存处理。

一、WebSettings  设置属性

在开发中,我们一般都会先进行设置属性,通过 WebView.getSettings(); 可以获取webSettings的对象实例。

(1)支持webview进行页面缩放;

webSettings.setBuiltInZoomControls(true);      

*这里有个坑,为了防止webview缩放时退出崩溃,最好写上如下代码:

@Override
public void finish() {
    ViewGroup view = (ViewGroup) getWindow().getDecorView();
    view.removeAllViews();
    super.finish();
}

(2) 支持JS;

webSettings.setJavaScriptEnabled(true);

(3)支持页面多窗口;

webSettings.setSupportMultipleWindows(true);

(4)支持JS打开对话窗口;

webSettings.setJavaScriptCanOpenWindowsAutomatically(true);

(5)支持启动技术;

webSettings.setUseWideViewPort(true); 

(6)webSettings.setLoadWithOverviewMode(true); //支持加载时相关页面适配
(7)String cacheDirPath = //设置缓存路径

mWebView.getSettings().setDatabasePath(cacheDirPath);       //设置数据库缓存路径       
mWebView.getSettings().setAppCachePath(cacheDirPath);    //设置 应用 缓存目录       
mWebView.getSettings().setDomStorageEnabled(true);     //开启 DOM 存储功能         
  mWebView.getSettings().setDatabaseEnabled(true);   //开启 数据库 存储功能        
mWebView.getSettings().setAppCacheEnabled(true);  //开启 应用缓存 功能        

二、网页返回&前进

(1)是否可以后退,返回值Boolean

Webview.canGoBack() 

(2)后退

Webview.goBack()

(3)是否可以前进,返回值Boolean                     

Webview.canGoForward()

(4)前进

Webview.goForward()

(5)以当前的页码为起始点,前进或者后退到历史记录中指定的steps。如果steps为负数则为后退,正数则为前进。

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

三、Android与JS交互  

JS是什么???即JavaScript,它不是Java程序的设计语言,而是一种脚本语言。JS是可以一行一行运行的脚本程序代码,可以直接内嵌于HTML网页,属于HTML网页的一部分。本章介绍的交互有两种方式。分别是Android调用JS方法和JS调用Android的方法。

(1)Android调用JS的方法:有2种

1、WebView的loadUrl()//会重新刷新页面

// 调用js中的函数:jsFun(msg)
  webView.loadUrl("javascript:jsFun('" + msg + "')"); 

2、evaluateJavascript()// 该方法的执行不会使页面刷新

mWebView.evaluateJavascript("javascript:callJS()", new ValueCallback<String>() {
    @Override
    public void onReceiveValue(String value) {
    //此处为 js 返回的结果
    }
  });
} 

小结论:

建议两种方法混合使用,即Android 4.4以下使用loadUrl,Android 4.4以上evaluateJavascript
 

(2)JS调用Android的方法:有三种

1、通过WebView的addJavascriptInterface()

// 参数1:Android的本地对象
// 参数2:JS的对象
mWebView.addJavascriptInterface(new MyJSInterface(),"androidJsInterface");


private static class MyJSInterface{
      public void showMessage(String msg){
        Toast.makeText(WebViewActivity.this, "成功"+ msg, Toast.LENGTH_LONG).show();
      }
}

原理: 通过对象映射将Android中的本地对象和JS中的对象进行关联,从而实现JS调用Android的对象和方法。

漏洞:因为addJavascriptInterface绑定了一个Java对象实例,根据Java的反射机制,就可以获得更多方法,而且间接获得更多的实例对象。

解决:

  • Android 4.2版本之后,Google 在Android 4.2 版本中规定对被调用的函数以 @JavascriptInterface进行注解从而避免漏洞攻击:
    private static class MyJSInterface{
          @JavascriptInterface
          public void showMessage(String msg){
              Toast.makeText(WebViewActivity.this, "成功"+ msg, Toast.LENGTH_LONG).show();
          }
    }
    
  • Android 4.2版本以前,需要采用拦截prompt()的方式进行漏洞修复,下面?就是讲解内容。

2、通过 WebChromeClient 的onJsAlert()、onJsConfirm()、onJsPrompt()方法回调,拦截JS对话框alert()、confirm()、prompt() 消息。JS的消息方法如下:

原理:Android通过 WebChromeClient 的onJsAlert()onJsConfirm()onJsPrompt()方法回调分别拦截JS对话框 ,得到他们的消息内容,然后分析意图执行相应操作。(需要和前端开发人员做好约定好消息内容的意图)

选择:常用拦截prompt()方法,因为onJsPrompt调用较少,而onJsAlert、onJsConfirm调用频繁。只有prompt()可以返回任意类型的值,而alert()没有返回值、confirm()只能返回两种状态(确定 / 取消)两个值。

3、通过WebViewClient.shouldOverrideUrlLoading  拦截url,分析意图执行相应操作

网易云音乐的app 里面的积分商城就是使用这种方式实现H5与native交互。微信也是和网易一样通过拦截url 分析url 意图来执行相应的操作的,native 回调js代码也是走的js里的_handleMessageFromWeixin 这种方法。但你其实想一想, 微信这个方法也是有缺陷的,因为url是可以伪造的,好在微信自己会在native代码里 验证他的appid。所以一定程度上可以避免大部分的攻击。

//步骤1. 定义Webview组件
Webview webview = (WebView) findViewById(R.id.webView1);

//步骤2. 加载一个网页:
webView.loadUrl("http://www.google.com/");
  //2:apk包中的html页面 webView.loadUrl("file:///android_asset/test.html");
  //3:本地页面webView.loadUrl("content://com.android.htmlfileprovider/sdcard/test.html");

//步骤3. 复写shouldOverrideUrlLoading()方法
webView.setWebViewClient(new WebViewClient(){
      @Override
      public boolean shouldOverrideUrlLoading(WebView view, String url) {
        //使得打开网页时不调用系统浏览器, 而是在本WebView中显示 
         view.loadUrl(url);
      return true;
      }
  });

2、当网页加载后有一个url,可以进行判断。需要与前端人员沟通如何定义Url,此url可以作为内部交互的意图。

a.比如,点击网页按钮关闭Activity。这里url字符串查找是否包含“finish_app”关键字。

if(url.contains("finish_app")) {//退出Activity
    finish();
}

b.比如,这个是从WebView页面跳转另外一个Activity页面。

 if (url.contains("MyLuckLog")){//会员页面
    startActivity(new Intent(WebViewActivity.this,NewActivity.class));
}

四、WebView 优化

1、内存优化

@Override
protected void onDestroy() {
    super.onDestroy();
    webView.removeAllViews();
    webView.destroy();
}
//清除网页访问留下的缓存
//由于内核缓存是全局的因此这个方法不仅仅针对webview而是针对整个应用程序.
Webview.clearCache(true);

//清除当前webview访问的历史记录
//只会webview访问历史记录里的所有记录除了当前访问记录
Webview.clearHistory();

//这个api仅仅清除自动完成填充的表单数据,并不会清除WebView存储到本地的数据
Webview.clearFormData();

2、播放视频时,返回页面,声音依然存在问题。

@Override
protected void onResume() {
    try {
        webView.getClass().getMethod("onResume").invoke(webView,(Object[])null);//继续播放视频
    } catch (IllegalAccessException e) {
        e.printStackTrace();
    } catch (InvocationTargetException e) {
        e.printStackTrace();
    } catch (NoSuchMethodException e) {
        e.printStackTrace();
    }
    super.onResume();
}

@Override
protected void onPause() {
    try {
        webView.getClass().getMethod("onPause").invoke(webView,(Object[])null);//停止视频播放
    } catch (IllegalAccessException e) {
        e.printStackTrace();
    } catch (InvocationTargetException e) {
        e.printStackTrace();
    } catch (NoSuchMethodException e) {
        e.printStackTrace();
    }
    super.onPause();
}

3、后台耗电问题 :activity不可见时要停用webview 

4、webview硬件加速导致页面渲染问题-白屏展示(关闭硬件加速) 

五、WebView 缓存

1、缓存的是url、css、图片、Js等,有两种方式,一种是网页数据缓存,一种是H5AppCache缓存 ,但都会缓存到应有所在的数据库表中。

2、五种模式:

  • Load_Cache_only 只读取缓存;
  • Load_Default 根据Cache_Control决定是否网络获取;
  • Load_Cache_Noraml   API 17后 与 2相同;
  • Load_No_Cache 不使用缓存只从网络获取;
  • Load_Cache_Else_Netwrok 只要本地存在缓存 无论是否过期或no_cache,都用缓存。

3、正确设置缓存模式:

if (NetWorkUtils.isNetworkConnected(this)){
    webView.getSettings().setCacheMode(WebSettings.LOAD_DEFAULT);
}else{
    webView.getSettings().setCacheMode(WebSettings.LOAD_CACHE_ELSE_NETWORK);
}

4、清除WebView缓存

/**
     * 清除WebView缓存
     */
    public void clearWebViewCache() {
        /**清理Webview缓存数据库,缓存文件由程序自动生成
         * /data/data/package_name/database/webview.db
         * /data/data/package_name/database/webviewCache.db
         **/
        try {
            //因为他们都是文件,所以可以用io方式删除,具体方法可以自己写
            deleteDatabase("webview.db");
            deleteDatabase("webviewCache.db");
        } catch (Exception e) {
            e.printStackTrace();
        }
        //WebView 缓存文件
        File webviewCacheDir = new File(APP_CACAHE_DIRNAME);
        //删除webview 缓存目录
        if (webviewCacheDir.exists()) {
            //具体的方法自己写
            deleteFile(webviewCacheDir);
        }
    }

欢迎入qq学习群号:569614530

发布了153 篇原创文章 · 获赞 755 · 访问量 100万+

猜你喜欢

转载自blog.csdn.net/csdn_aiyang/article/details/72765103