Android与H5交互

前言

native和H5混合开发越来越普遍,于是了解native和H5的通信方式就很有必要。

一、webview使用

private void settingProperty(){
        myJavascriptInterface = new MyJavascriptInterface();
        WebSettings webSettings = mWebView.getSettings();
        //支持js
        webSettings.setJavaScriptEnabled(true);
        //支持通过JS打开新窗口
        webSettings.setJavaScriptCanOpenWindowsAutomatically(true);
        //缩放至屏幕的大小
        webSettings.setLoadWithOverviewMode(true);
        //将图片调整到适合webview的大小
        webSettings.setUseWideViewPort(false);
        //设置缓存方式
        webSettings.setCacheMode(WebSettings.LOAD_DEFAULT);
        //开启DOM storage API功能
        webSettings.setDomStorageEnabled(true);
        //开启 database storage API 功能
        webSettings.setDatabaseEnabled(true);

        String cacheDirPath = getCacheDir().getAbsolutePath()+ "/webViewCache";
        //设置数据库缓存路径
        webSettings.setDatabasePath(cacheDirPath);
        //开启Application H5 Caches 功能
        webSettings.setAppCacheEnabled(true);
        //设置Application Caches 缓存目录
        webSettings.setAppCachePath(cacheDirPath);
		/**对系统API在19以上的版本作了兼容。因为4.4以上系统onPageFinished在恢复图片加载时, 如果存在多张图片引用的是相同的src时,会只有一个image标签得到加载,因而对于这样的系统我们就先直接加载 */
        if (Build.VERSION.SDK_INT >= 19){
            webSettings.setLoadsImagesAutomatically(true);
        } else {
            webSettings.setLoadsImagesAutomatically(false);
        }
 }

WebViewClient

WebViewClient:处理webview的各种通知与请求事件

public class MyWebViewClient extends WebViewClient {
    /**
     * @return true:打开新的url(重定向)时,webview不会加载该url,需要自己处理;false:系统继续加载该url;
     */
    @Override
    public boolean shouldOverrideUrlLoading(WebView view, String url) {
        //TODO  重写此方法,实现对网页中超链接的拦截
        view.loadUrl(url);
        return true;
    }
    // 网页开始加载
    @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. 若没有设置 WebViewClient ,url重定向时则由系统(Activity Manager)处理该 url,通常是使用浏览器打开或弹出浏览器选择对话框。
  2. 若设置 WebViewClient 且shouldOverrideUrlLoading返回 true ,url重定向时应由应用的代码处理该 url,WebView 不处理,也就是程序员自己做处理。
  3. 若设置 WebViewClient 且shouldOverrideUrlLoading返回 false,url重定向时则说明由 WebView 处理该 url,即用 WebView 加载该 url。
    :url不需要重定向时,没有设置 WebViewClient也可以在WebView中打开,且与shouldOverrideUrlLoading返回值无关。

WebChromeClient

WebChromeClient:辅助webview处理javascript对话框,网页的icon和title,以及加载进度条等。

/**
 1. 重写webview的浏览器客户端
 2. 主要功能是辅助webview处理javascript对话框,网页的icon和title,以及加载进度条等
 */
public class MyWebChromeClient extends WebChromeClient {
    // 监听页面加载进度
    @Override
    public void onProgressChanged(WebView view, int newProgress) {
        super.onProgressChanged(view, newProgress);
    }
    // 监听网页标题
    @Override
    public void onReceivedTitle(WebView view, String title) {
        super.onReceivedTitle(view, title);
        //TODO 接收html的title标签,可根据html的title改变而改变
    }
    // 监听网页图标
    @Override
    public void onReceivedIcon(WebView view, Bitmap icon) {
        super.onReceivedIcon(view, icon);
    }
    @Override
    public boolean onJsAlert(WebView view, String url, String message, JsResult result) {
        return super.onJsAlert(view, url, message, result);
    }
    @Override
    public boolean onJsConfirm(WebView view, String url, String message, JsResult result) {
        return super.onJsConfirm(view, url, message, result);
    }
    @Override
    public boolean onJsPrompt(WebView view, String url, String message, String defaultValue, JsPromptResult result) {
        return s
    }
}

二、H5调用Android方法

1)@JavascriptInterface注解 (同步调用)

提供接口供js调用(@JavascriptInterface注解标注该方法是供js调用)

// “android”是为js接口取的名字
WebView.addJavascriptInterface(new MyJavascriptInterface(), “android”); 
public class MyJavascriptInterface{
	@JavascriptInterface
	public String h5NativtCallback(){
	    LogUtils.i(TAG, "h5NativeCallback");
	    return "js 调用 android 的回调 "+(num_android++);
	}
}

:H5调用android native方法:window.android.h5NativtCallback();即window.js接口名.加方法名

2)通过WebviewClient拦截url协议 (异步调用)

  1. Android native和h5定好协议;
  2. 通过WebviewClient的shouldOverrideUrlLoading拦截指定的url协议头。

3)通过WebChromeClient拦截弹窗 (同步调用)

通过WebChromeClient的onJsAlert()、onJsConfirm()和onJsPrompt()分别拦截js的alert()、confirm()和prompt()方法

三、Android调用H5方法

  1. WebView#loadUrl()
mWebView.loadUrl("javascript:callbackFromNative('fromAndroid!')"

:loadUrl方法中的字符串参数构成:”javascript:”+js方法,其中js方法中的参数是传递给js方法的参数
2. List item

mWebview.evaluateJavascript(
	"javascript:callbackFromNative('data from android!‘)“,
	 new ValueCallback<String>() {
             @Override
             public void onReceiveValue(String value) {  }
});

区别:loadUrl需要刷新页面,且需要回调时要通过@JavaScriptInterface注解的方式实现;而evaluateJavascript不需要刷新页面,自带回调。

四、webview常见的问题

1)判断WebView是否已经滚动到页面底端

getScrollY():当前可见区域的顶端距整个页面顶端的距离,也就是当前内容滚动的距离.
getHeight()或getBottom():当前WebView 这个容器的高度
getContentHeight :整个html 的原始高度,但并不等同于当前整个页面的高度,因为WebView 有缩放功能, 所以当前整个页面的实际高度为原始html 的高度再乘上缩放比例.
因此,准确判断WebView是否已经滚动到页面底端:

if(WebView.getContentHeight*WebView.getScale() == (webview.getHeight()+WebView.getScrollY())){ //已经处于底端 }   

2)处理WebView中的非超链接请求

有时候需要加上请求头,但是非超链接的请求,没有办法再shouldOverrinding中拦截并用webView.loadUrl(String url,HashMap headers)方法添加请求头。
首先需要在url中加特殊标记/协议, 如在shouldInterceptRequest()方法中,可以拦截到所有的网页中资源请求(比如加载JS,图片以及Ajax请求等等),然后将要添加的请求头,以get形式拼接到url末尾。

@Override  
public WebResourceResponse shouldInterceptRequest(WebView view,String url) {  
    // 非超链接(如Ajax)请求无法直接添加请求头,现拼接到url末尾,这里拼接一个imei作为示例  
  
    String ajaxUrl = url;  
    // 如标识:req=ajax  
    if (url.contains("req=ajax")) {  
       ajaxUrl += "&imei=" + imei;  
    }  
  
    return super.shouldInterceptRequest(view, ajaxUrl);  
  
}  

3)屏蔽webview长按事件

webview长按时将会调用系统的复制控件,可以通过

mWebView.setOnLongClickListener(new OnLongClickListener() {  
	@Override  
	public boolean onLongClick(View v) {  
		return true;  
	}  
});  

4)WebView #onPageFinished()多次调用

WebView 在Android4.4的手机上onPageFinished()回调会多调用一次(具体原因待追查)。因此,需要尽量避免在onPageFinished()中做业务操作,否则会导致重复调用,还有可能会引起逻辑上的错误。

5)WebView#addJavaScriptInterface()引起的安全问题.

注入恶意的js代码,尤其是在已经获取root权限的手机上,通过恶意程序可能会利用该漏洞安装或者卸载应用。

  1. JS中可以遍历window对象,找到存在“getClass”方法的对象的对象,然后再通过反射的机制,得到Runtime对象;
  2. 若当前应用具有读写SDCard的权限,即android.permission.WRITE_EXTERNAL_STORAGE,便可以调用静态方法来访问文件;
    参考:http://blog.csdn.net/leehong2005/article/details/11808557

6)如何避免WebView内存泄露

如:WebView页面中播放了音频,退出Activity后音频仍然在播放

  1. 初始化webview
    不在xml中定义 Webview ,而是在需要的时候在Activity中创建,并且Context使用 getApplicationgContext(),webview就不会持有activity的引用
      LinearLayout.LayoutParams lp = new LayoutParams(ViewGroup.LayoutParams.MatchParent,ViewGroup.LayoutParams.MatchParent);
      WebView wv = new WebView(getApplicationContext()); 
      wv.setLayoutParams(lp);
      linearyLayout.addView(wv);
    
  2. 销毁webview
    在销毁 webview 的时候,先让 webview 加载空内容,清空历史,然后将webview 从父容器移除 ,然后再销毁webview。
    @Override
    protected void onDestroy(){
        //先加载空内容
        if(wv!=null){
          //先加载空内容
         wv.loadDataWithBaseUrl(null,"","text/html","utf-8",null);
          //清空历史
         wv.clearHistory()
          //从布局中移除
          ((ViewGroup)wv.getParent()).removeView(wv);
          //然后销毁
          wv.destroy();
          //然后置为空
          wv=null;
        }
        super.onDestroy();
    }
    

参考

[1] : https://www.jb51.net/article/100758.htm
[2] : https://blog.csdn.net/qq_24530405/article/details/52067474
[3] : https://blog.csdn.net/carson_ho/article/details/52693322
[4] : https://www.cnblogs.com/lee0oo0/p/4026774.html
[5] : https://www.jianshu.com/p/d3e033c48323

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值