WebView:拦截url、js交互、使用本地图片资源

转载请标明出处:http://blog.csdn.net/jj583500596/article/details/69250664

上一篇WebView:基本设置与使用,了解了WebView的常用API、基本设置以及加载HTML网页。这一篇道长深入说一些常用的WebView使用方法。比如标题上写的拦截URL、JS交互、使用本地图片资源等。好了,废话就到这里,走起……

一、Android代码与JS交互

现在我们已经使用WebView加载好了HTML网页,我们可以点击网页上的控件进行操作,但是如果HTML中的一些控件需要与Android客户端相互传递参数或者调用一些Android客户端控件时我们要怎么做?答案就是使用JS交互来实现。

Android代码与JS交互的大前提:

// 启用WebView对JavaScript的支持
mWebView.getSettings().setJavaScriptEnabled(true);

1.Android代码调用JS

WebView调用JS代码非常简单,直接调用loadUrl,加载JS方法即可:

// 无参数调用
mWebView.loadUrl("javascript:javacalljs()");

// Java调用JS并传递参数
String content = "hello js, form Android code!";
mWebView.loadUrl("javascript:javacalljswithargs('" + content + "')");
// mWebView.loadUrl("javascript:setPhoto('" + filePath + "','" + filename + "')");


HTML页面中的JS代码:

   <head> 
      <meta http-equiv="Content-Type"  content="text/html;charset=utf-8">

      <script type="text/javascript"> 
            function javacalljs(){ 
                document.getElementById("content").innerHTML +=
                    "<br\>java调用了js函数";
            }

            function javacalljswithargs(arg){ 
                document.getElementById("content").innerHTML +=    
                    ("<br\>"+arg); 
            }
      </script> 
   </head>

2.JS调用Android代码

实现一个class,并在其中添加对应的方法,供JS调用:

   /**
     * Js调用的JavascriptInterface
     */
    public class TestInterface {

        /**
         * 因为安全问题,在Android 4.2以后(如果应用的android:targetSdkVersion数值为17+)
         * JS只能访问带有 @JavascriptInterface注解的Java函数。
         */
        @JavascriptInterface
        public void startCall() {
           Intent intent = new Intent();
           intent.setAction(Intent.ACTION_DIAL);
           intent.setData(Uri.parse("tel:" + 10086));
           startActivity(intent);
        }

        @JavascriptInterface
        public void showToast(String content) {
            Toast.makeText(MainActivity.this, "js调用了java函数并传递了参数:" + content, Toast.LENGTH_SHORT).show();
        }
    }


调用WebView的addJavascriptInterface方法,设置JS回调的对象

  // 设置JavascriptInterface
  // javainterface实际就是一个普通的java类,里面是我们本地实现的java代码
  // 将object 传递给webview,并指定别名,这样js脚本就可以通过我们给的这个别名来调用我们的方法
  // 在代码中,TestInterface是实例化的对象,testInterface是这个对象在js中的别名
  mWebView.addJavascriptInterface(new TestInterface(), "testInterface");


HTML页面中的JS代码,回调Android代码的方式为:window.别名.方法名

<a onClick="window.testInterface.startCall()">点击拨打10086</a><br/>

<a onClick="window.testInterface.showToast('我弹了一个Toast')" >点击弹出Toast,内容为“我弹了一个Toast”</a>


前面有一个安全问题:与JS交互时,JS中夹如果杂有恶意代码(比如获取手机内存信息),Android 4.2以后做了优化,JS只能访问带有 @JavascriptInterface注解的Java函数,但是Android 4.2以前怎么解决?现在发现了两种解决方法,以后发现了新的解决方法道长会更新博客。
方法一:其实很简单,就是在你的jscalljava回调函数内另外开个线程去load JS代码即可:

mWebView.post(new Runnable() {
        @Override                 
        public void run() {
            wb.loadUrl("javascript:display_alert()");
        }
    });


方法二:JS注入本地文件
为HTML注入JS文件,通过在webview的onPageFinished方法中执行JS代码注入:
第一种注入方式:
当webview加载完之后,读取整个JS文件中的内容,然后将整个文件内容以字符串的形式,通过webview.loadUrl(“javascript:fileContentString”)注入

URL url = new URL("http://www.rayray.ray/ray.js");
in = url.openStream();
byte buff[] = new byte[1024];
ByteArrayOutputStream fromFile = new ByteArrayOutputStream();
FileOutputStream out = null;
do {
    int numread = in.read(buff);
    if (numread <= 0) {
        break;
    }
    fromFile.write(buff, 0, numread);
} while (true);
String wholeJS = fromFile.toString();

@Override
public void onPageFinished(WebView view, String url) {
    super.onPageFinished(view, url);
    webview.loadUrl("javascript:" + wholeJS);
}

第二种注入方式:
页面加载完之后,直接向webview对应的html中加入”script”便签,并包含要注入的js的Url地址,如下:

String js = "var newscript = document.createElement(\"script\");";
js += "newscript.src=\"http://www.123.456/789.js\";";
js += "document.body.appendChild(newscript);";

@Override
public void onPageFinished(WebView view, String url) {
    super.onPageFinished(view, url);
    webview.loadUrl("javascript:" + js);
}

二、拦截URL

其实拦截url也可以实现与Android客户端交互,但是拦截url不能实现与Android客户端相互传参,只是在一些跳转时用的比较多

    mWebView.setWebViewClient(new WebViewClient() {
        @Override
        public boolean shouldOverrideUrlLoading(WebView view, String url) {
            // 当url里面包含article字段的时候,则跳转到TwoStairsActivity页面,否则还是继续显示网页
            // 返回值:true 不会显示网页资源,需要等待你的处理,false 就认为系统没有做处理,会显示网页资源
            if (!TextUtils.isEmpty(url) && url.contains("article")) {
                Intent intent = new Intent(ClubActivityDetailActivity.this, TwoStairsActivity.class);
                intent.putExtra("url", url);
                startActivity(intent);
                return true;
            }
            return false;
        }
    });

三、加载Html文件时使用本地图片资源

加载HTML页面的时候,有的App客户端会选择把图片资源放在本地,以减少加载资源比较多的情况下导致的该页面加载缓慢,耗费大量的流量。这对流量限制的客户端来说,用户体验非常不好。所以将这些图片资源存放在app中,加载HTML时直接从本地加载这些图片资源。(只针对HTML文件从网络上获取)

方法一:转换成Base64编码

  • 将图片文件转换成Base64编码
import com.parse.codec.binary.Base64;

import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;

/**
 * 将图片转换为Base64<br>
 * 将base64编码字符串解码成img图片
 *
 * @创建时间 2015-06-01 15:50
 */
public class Img2Base64Util {

    public static void main(String[] args) {
        String imgFile = "d:\\3.jpg";//待处理的图片
        String imgbese = getImgStr(imgFile);
        System.out.println(imgbese.length());
        System.out.println(imgbese);
        String imgFilePath = "d:\\332.jpg";//新生成的图片
        generateImage(imgbese, imgFilePath);
    }

    /**
     * 将图片转换成Base64编码
     *
     * @param imgFile 待处理图片
     * @return
     */
    public static String getImgStr(String imgFile) {
        //将图片文件转化为字节数组字符串,并对其进行Base64编码处理

        InputStream in = null;
        byte[] data = null;
        //读取图片字节数组
        try {
            in = new FileInputStream(imgFile);
            data = new byte[in.available()];
            in.read(data);
            in.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
        return new String(Base64.encodeBase64(data));
    }

    /**
     * 对字节数组字符串进行Base64解码并生成图片
     *
     * @param imgStr      图片数据
     * @param imgFilePath 保存图片全路径地址
     * @return
     */
    public static boolean generateImage(String imgStr, String imgFilePath) {
        //
        if (imgStr == null) //图像数据为空
            return false;

        try {
            //Base64解码
            byte[] b = Base64.decodeBase64(imgStr);
            for (int i = 0; i < b.length; ++i) {
                if (b[i] < 0) {//调整异常数据
                    b[i] += 256;
                }
            }
            //生成jpeg图片
            OutputStream out = new FileOutputStream(imgFilePath);
            out.write(b);
            out.flush();
            out.close();
            return true;
        } catch (Exception e) {
            return false;
        }
    }
}


  • JS代码,将当前的fileBase64传到HTML页面中,并设置到img上
<img alt="Embedded Image" src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADIA..." />


注意:

在上面的URL中,data表示取得数据的协定名称,image/png 是数据类型名称,base64 是数据的编码方法,逗号后面就是这个image/png文件base64编码后的数据。

目前,url scheme支持的类型有:

data: - 文本数据
data:text/plain - 文本数据
data:text/html - HTML代码
data:text/html;base64 - base64编码的HTML代码
data:text/css - CSS代码
data:text/css;base64 - base64编码的CSS代码
data:text/javascript - Javascript代码
data:text/javascript;base64 - base64编码的Javascript代码
data:image/gif;base64 - base64编码的gif图片数据
data:image/png;base64 - base64编码的png图片数据
data:image/jpeg;base64 - base64编码的jpeg图片数据
data:image/x-icon;base6 - base64编码的icon图片数据

所以在转换完成以后要在Base64字符串前面拼接上相应的头,如果不拼接的话浏览器会不识别字符串。

优缺点:转换成Base64编码这种方法适合小图片,如果图片过大的话重新编码也会耗费大量时间。

方法二:使用shouldInterceptRequest拦截

shouldInterceptRequest这个回调可以通知主程序WebView处理的资源(css/js/image等)请求并允许主程序进行处理后返回数据。如果主程序返回的数据为null则WebView会自行请求网络加载资源,否则使用主程序提供的数据。注意:这个回调发生在非UI线程中,所以不可以进行UI相关的操作。

public WebResourceResponse shouldInterceptRequest (WebView view, String url) 

从API 11开始引入,API 21弃用

public WebResourceResponse shouldInterceptRequest (WebView view, WebResourceRequest request) 

从API 21开始引入,所以在使用的时候,最好两者都要进行重写。

mWebView.setWebViewClient(new WebViewClient() {

                @Override
                public WebResourceResponse shouldInterceptRequest(WebView view, String url) {
                    String key = "http://localhost/";
                    WebResourceResponse response = null;
                    if (url.contains(key)) {
                        try {
                            String imgPath = url.replace(key,"");
                            imgPath = Uri.parse(imgPath).getPath();
                            InputStream localCopy = new FileInputStream(imgPath);
                            //当前只针对图片
                            response = new WebResourceResponse("image/png", "UTF-8", localCopy);
                        } catch (IOException e) {
                            e.printStackTrace();
                        }
                    }
                    return response;
                }
            });



当从本地相册或者拍照完成后,图片的URL可能是这样的/storage/emulated/0/newCrop.jpg,如果这个URL回调给JS是显示不出来的。因为WebView根本不会识别并自动加载。
需要把图片路径拼接成如下样式:

filePath = "http://localhost/file:///storage/emulated/0/newCrop.jpg"


通过Android调用JS代码设置图片

final String filename = new File(filePath).getName();
mHandler.post(new Runnable() {
   @Override
   public void run() {
       mWebView.loadUrl("javascript:setPhoto('" + filePath + "','" + filename + "')");
   }
});

/**
* JS回调Java代码
*/
public class JsInteration {

   @JavascriptInterface
   public void setPhoto(String filePath, String fileName) {
   // Toast.makeText(ClubApplyActivity.this, filePath, Toast.LENGTH_SHORT).show();
   }
}



JS代码:

<img src="http://localhost/file:///storage/emulated/0/newCrop.jpg" alt="" class="img-responsive img-thumbnail">



好了,咱们的WebView使用暂时介绍到这里,以后如果还有补充,道长会继续更新。希望这篇博客能为你提供一些帮助。


  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值