webView--总结


Anaroid WebView API详解--http://blog.csdn.net/zhangcanyan/article/details/51344090;
Android5.1系统WebView内存泄漏场景--http://blog.csdn.net/zhangcanyan/article/details/51344118;
Android WebView的缓存方式分析--http://blog.csdn.net/zhangcanyan/article/details/51347744;
Android安全开发之WebView中的大坑--http://blog.csdn.net/zhangcanyan/article/details/51930775;

--4,Android中java与 javaScript交互;

--3,Android Webview清除缓存和Cookie;

--2,WebView自适应屏幕宽度代码;

--1,Android WebView编程的那些坑(一);

 ===========

 --4,Android中java与 javaScript交互;

 

Android提供了一个很强大的WebView控件用来处理Web网页,而在网页中,JavaScript又是一个很举足轻重的脚本。本文将介绍如何实现Java代码和Javascript代码的相互调用。

如何实现

实现Java和js交互十分便捷。通常只需要以下几步。

  • WebView开启JavaScript脚本执行
  • WebView设置供JavaScript调用的交互接口。
  • 客户端和网页端编写调用对方的代码。

本例代码

为了便于讲解,先贴出全部代码

package com.example.javajsinteractiondemo;
import android.annotation.SuppressLint;
import android.app.Activity;
import android.os.Bundle;
import android.util.Log;
import android.view.Menu;
import android.webkit.JavascriptInterface;
import android.webkit.WebChromeClient;
import android.webkit.WebSettings;
import android.webkit.WebView;
import android.webkit.WebViewClient;
import android.widget.Toast;

public class MainActivity extends Activity {
  private static final String LOGTAG = "MainActivity";
  @SuppressLint("JavascriptInterface")
  @Override
  protected void onCreate(Bundle savedInstanceState) {
      super.onCreate(savedInstanceState);
      setContentView(R.layout.activity_main);
      final WebView myWebView = (WebView) findViewById(R.id.myWebView);
      WebSettings settings = myWebView.getSettings();
      settings.setJavaScriptEnabled(true);
      myWebView.addJavascriptInterface(new JsInteration(), "control");
      myWebView.setWebChromeClient(new WebChromeClient() {});
      myWebView.setWebViewClient(new WebViewClient() {
          @Override
          public void onPageFinished(WebView view, String url) {
              super.onPageFinished(view, url);
              testMethod(myWebView);
          }

      });
      myWebView.loadUrl("file:///android_asset/js_java_interaction.html");
  }

  private void testMethod(WebView webView) {
      String call = "javascript:sayHello()";
      call = "javascript:alertMessage("" + "content" + "")";
      call = "javascript:toastMessage("" + "content" + "")";
      call = "javascript:sumToJava(1,2)";
      webView.loadUrl(call);

  }

  public class JsInteration {
      @JavascriptInterface
      public void toastMessage(String message) {
          Toast.makeText(getApplicationContext(), message, Toast.LENGTH_LONG).show();
      }

      @JavascriptInterface
      public void onSumResult(int result) {
          Log.i(LOGTAG, "onSumResult result=" + result);
      }
  }

}

 

前端网页代码;

<html>
<script type="text/javascript">
    function sayHello() {
        alert("Hello")
    }

    function alertMessage(message) {
        alert(message)
    }

    function toastMessage(message) {
        window.control.toastMessage(message)
    }

    function sumToJava(number1, number2){
       window.control.onSumResult(number1 + number2)
    }
</script>
Java-Javascript Interaction In Android
</html>

 

调用示例

js调用Java

调用格式为window.jsInterfaceName.methodName(parameterValues) 此例中我们使用的是control作为注入接口名称。

function toastMessage(message) {
  window.control.toastMessage(message)
}

function sumToJava(number1, number2){
   window.control.onSumResult(number1 + number2)
}

 

Java调用JS

webView调用js的基本格式为webView.loadUrl(“javascript:methodName(parameterValues)”)

调用js无参无返回值函数

String call = "javascript:sayHello()";
webView.loadUrl(call);

调用js有参无返回值函数

注意对于字符串作为参数值需要进行转义双引号。

String call = "javascript:alertMessage("" + "content" + "")";
webView.loadUrl(call);

调用js有参数有返回值的函数

Android在4.4之前并没有提供直接调用js函数并获取值的方法,所以在此之前,常用的思路是 java调用js方法,js方法执行完毕,再次调用java代码将值返回。

1.Java调用js代码

String call = "javascript:sumToJava(1,2)";
webView.loadUrl(call);

2.js函数处理,并将结果通过调用java方法返回

function sumToJava(number1, number2){
       window.control.onSumResult(number1 + number2)
}

 

3.Java在回调方法中获取js函数返回值

@JavascriptInterface
public void onSumResult(int result) {
  Log.i(LOGTAG, "onSumResult result=" + result);
}

 

4.4处理

Android 4.4之后使用evaluateJavascript即可。这里展示一个简单的交互示例 具有返回值的js方法

function getGreetings() {
      return 1;
}

java代码时用evaluateJavascript方法调用

private void testEvaluateJavascript(WebView webView) {
  webView.evaluateJavascript("getGreetings()", new ValueCallback<String>() {

  @Override
  public void onReceiveValue(String value) {
      Log.i(LOGTAG, "onReceiveValue value=" + value);
  }});
}

输出结果

I/MainActivity( 1432): onReceiveValue value=1

注意

  • 上面限定了结果返回结果为String,对于简单的类型会尝试转换成字符串返回,对于复杂的数据类型,建议以字符串形式的json返回。
  • evaluateJavascript方法必须在UI线程(主线程)调用,因此onReceiveValue也执行在主线程。

疑问解答

Alert无法弹出

你应该是没有设置WebChromeClient,按照以下代码设置;

myWebView.setWebChromeClient(new WebChromeClient() {});

Uncaught ReferenceError: functionName is not defined

问题出现原因,网页的js代码没有加载完成,就调用了js方法。解决方法是在网页加载完成之后调用js方法

myWebView.setWebViewClient(new WebViewClient() {
  @Override
  public void onPageFinished(WebView view, String url) {
      super.onPageFinished(view, url);
      //在这里执行你想调用的js函数
  }

});

Uncaught TypeError: Object [object Object] has no method

安全限制问题

如果只在4.2版本以上的机器出问题,那么就是系统处于安全限制的问题了。Android文档这样说的

Caution: If you’ve set your targetSdkVersion to 17 or higher, you must add the @JavascriptInterface annotation to any method that you want available your web page code (the method must also be public). If you do not provide the annotation, then the method will not accessible by your web page when running on Android 4.2 or higher.

中文大意为

警告:如果你的程序目标平台是17或者是更高,你必须要在暴露给网页可调用的方法(这个方法必须是公开的)加上@JavascriptInterface注释。如果你不这样做的话,在4.2以以后的平台上,网页无法访问到你的方法。

两种解决方法
  • 将targetSdkVersion设置成17或更高,引入@JavascriptInterface注释
  • 自己创建一个注释接口名字为@JavascriptInterface,然后将其引入。注意这个接口不能混淆。

注,创建@JavascriptInterface代码

public @interface JavascriptInterface {

}

代码混淆问题

如果在没有混淆的版本运行正常,在混淆后的版本的代码运行错误,并提示Uncaught TypeError: Object [object Object] has no method,那就是你没有做混淆例外处理。 在混淆文件加入类似这样的代码

-keep class com.example.javajsinteractiondemo$JsInteration {
    *;
}

All WebView methods must be called on the same thread

过滤日志曾发现过这个问题。

E/StrictMode( 1546): java.lang.Throwable: A WebView method was called on thread 'JavaBridge'. All WebView methods must be called on the same thread. (Expected Looper Looper (main, tid 1) {528712d4} called on Looper (JavaBridge, tid 121) {52b6678c}, FYI main Looper is Looper (main, tid 1) {528712d4})
E/StrictMode( 1546):   at android.webkit.WebView.checkThread(WebView.java:2063)
E/StrictMode( 1546):   at android.webkit.WebView.loadUrl(WebView.java:794)
E/StrictMode( 1546):   at com.xxx.xxxx.xxxx.xxxx.xxxxxxx$JavaScriptInterface.onCanGoBackResult(xxxx.java:96)
E/StrictMode( 1546):   at com.android.org.chromium.base.SystemMessageHandler.nativeDoRunLoopOnce(Native Method)
E/StrictMode( 1546):   at com.android.org.chromium.base.SystemMessageHandler.handleMessage(SystemMessageHandler.java:27)
E/StrictMode( 1546):   at android.os.Handler.dispatchMessage(Handler.java:102)
E/StrictMode( 1546):   at android.os.Looper.loop(Looper.java:136)
E/StrictMode( 1546):   at android.os.HandlerThread.run(HandlerThread.java:61)

在js调用后的Java回调线程并不是主线程。如打印日志可验证

ThreadInfo=Thread[WebViewCoreThread,5,main]

解决上述的异常,将webview操作放在主线程中即可。

webView.post(new Runnable() {
    @Override
    public void run() {
        webView.loadUrl(YOUR_URL).
    }
});
 
 

--3,Android Webview清除缓存和Cookie;

最近项目中遇到用webView显示内容的需求,接到任务后代码如下
// 更新加载进度条
wv_setmeal_detail.setWebChromeClient(new WebChromeClient() {
public void onProgressChanged(WebView view, int progress) {
pb_webview.setProgress(progress);
if (progress == 100) {
pb_webview.setVisibility(View.GONE);
}
}
});
wv_setmeal_detail.setWebViewClient(new WebViewClient() {
public void onReceivedError(WebView view, int errorCode, String description, String failingUrl) {
}
});
wv_setmeal_detail.loadUrl(url);

嗯,没问题,显示的很好,但是测试时发现一个坑爹的问题,
就是退出当前账号换其他账号登录时webview显示的内容还是之前账号的信息,额...怪了.想想肯定是缓存搞的鬼,好,接下来就是清除缓存.

网上各种google baidu,发现很多方法都不管用.做法一般如下
1.websettings设置不适用缓存
mCurrentWebView.clearCache(true);
mCurrentWebView.getSettings().setCacheMode(WebSettings.LOAD_NO_CACHE);
其实不管用...
2.webview显示时会自动生成如下文件 : data/data/<包名>/app_webview , 然后就是循环遍历删除该文件下的内容
/**
* 清除WebView缓存
*/
public void clearWebViewCache() {
// WebView 缓存文件
File appCacheDir = new File(DATA_BASE_PATH + getPackageName() + APP_WEBVIEW_PATH);
if (appCacheDir.exists()) {
deleteFile(appCacheDir);
}
}
/**
* 递归删除 文件/文件夹
*/
public void deleteFile(File file) {
if (file.exists()) {
if (file.isFile()) {
file.delete();
} else if (file.isDirectory()) {
File files[] = file.listFiles();
for (int i = 0; i < files.length; i++) {
deleteFile(files[i]);
}
}
file.delete();
}
}
发现推出后马上切换行,过一分钟左右还是不行. 就郁闷了...到底缓存数据存在哪了呢...
最后google在stackoverflow上看到一篇文章,问题得以解决.原来是要清除webview的cookie才能彻底把缓存清除
public void clearWebViewCache() {
// 清除cookie即可彻底清除缓存
CookieSyncManager.createInstance(self);
CookieManager.getInstance().removeAllCookie();
}

附上两篇文章地址
http://stackoverflow.com/questions/2465432/android-webview-completely-clear-the-cache
http://www.devdiv.com/forum.php?mod=viewthread&tid=116641

吐槽一下 百度真的搜出来是一大堆没用的东东,google是王道啊,stackoverflow这个网站真心不错,好多问题都在上边得以解决.

 

 --2,WebView自适应屏幕宽度代码;

一个超简单的方法可快速实现WebView自适应屏幕宽度,希望此方法对各位同学有所帮助。

// 让网页自适应屏幕宽度

WebSettings webSettings= webView.getSettings();
webSettings.setLayoutAlgorithm(LayoutAlgorithm.SINGLE_COLUMN);

LayoutAlgorithm是一个枚举,用来控制html的布局,总共有三种类型: NORMAL:正常显示,没有渲染变化。 SINGLE_COLUMN:把所有内容放到WebView组件等宽的一列中。 NARROW_COLUMNS:可能的话,使所有列的宽度不超过屏幕宽度。

 

Android中WebView图片实现自适应的方法:

加上这个属性后,html的图片就会以单列显示,就不会变形占了别的位置
WebSettings ws = tv.getSettings();
ws.setLayoutAlgorithm(LayoutAlgorithm.SINGLE_COLUMN);
webView.setInitialScale(5);//让缩放显示的最小值为起始
webSettings.setSupportZoom(true);// 设置支持缩放
webSettings.setBuiltInZoomControls(true);// 设置缩放工具的显示

 

--1,Android WebView编程的那些坑(一);

最大的坑是ROM不同,webkit不同,差异性很大。再加上google的坑,真是坑上加坑。
比如js注入问题,比如client回调接口时序问题,比如内存回收问题,etc

1、内存泄漏问题,尤其注意Android 5.0系统的WebView移除不了的问题;
2、WebKit线程数不能控制,低效,例如,如果js效率出现问题或者大量js执行,直接就导致连HTTP请求都延迟,
自己抓包就知道(你会傻乎乎地去指责服务器同事怎么你们服务器这么烂,网页加载好慢);
3、webcorethread的wait问题,不知道什么时候就会发生,完全束手无策。后果是什么?退出页面没有卵用,只能杀进程。
4、Android4.2以下手机的JavaScript interface的注入漏洞问题,完全不要太危险;
5、弱鸡的一系列ui问题,滚动,滑动,兼容,把网页前端哥们苦不堪言,找Android前端同事投诉,Android同学很无奈,
完全不知道该怎么办。
解决方案:1.跨进程。(无法解决兼容问题)2.放弃它。

一级bug如下:
应用需求,一个页面里面有多个fragment,然后fragment里面都是webview加载内容的,然后你会发现不知道
什么时候(对,就是不知道什么时候),webview加载不出内容了,白屏一片。OK,没问题,加载不出来无所谓啦,大不了重进,
跟下去,发现底层有一条叫webcore(印象中是这个名字)的线程一直处于wait的状态,再也恢复不了了,唯一拯
救就是杀进程。
在某些手机上,Webview承载视频时,activity销毁后,视频资源没有被销毁。
解决办法:在onDestory之前修改url为空地址。

WebView居然提供了一个超奇葩的Destroy方法,需要自己手动去调一下才能释放资源。否则就算依赖的Activit 或者Fragment不在了,资源还是不会被释放干净。


1、 onPageFinished这个也是把我坑好久,进度条该结束的时候不结束,不该结束的时候提前结束,
我总结根本原因还是不同版本浏览器内核的实现差异导致的,也深入过内核代码发现确实结束的回调时机有差异,
除非自己做内核,否则除了尽可能的兼容处理外, 尽量保证它提前结束,因为迟迟不结束比提前结束体验要糟糕得多。

2、webView耗电的问题,我们之前发现的一个情况是,webView切换到后台时,如果当前页面有JS代码仍在不时的run,
就会导致比较严重的耗电,所以必须确保切换到后台后暂停JS执行,同时切回来的时候恢复它。

3. webView闪屏的问题,也是确实存在的,试验过,确实跟硬件渲染有关。

4. 数据积累也是头疼的问题,经常有用户抱怨它的空间被占满了,其实是webkit本身没有管理好缓存,不得不让浏览器开发人员涉法处理。

5.默认的webview滚动条确实很粗,但还是可以修改的。

想起来的的后面再补充:
webview原生支持js与native代码交互,
可惜在4.2以下版本上有安全漏洞,当时被乌云报出来,事情还挺大,各大浏览器厂商都紧急应对,我们也还是想了其他办法,
解决了这个问题。
其实所谓的WebView的各种坑,大部分是Webkit等内核的坑,其实只是它正常发展成熟过程中的一些遗留问题,
随着版本的迭代演化,也在不断改进。 遗憾的是Android版本的严重碎片化,使得这些问题我们不得不面对。
作为Ninja浏览器(mthli/Ninja · GitHub)的开发者,我想我遇到的问题应该具有一些代表性吧。
下面说说我比较困惑的几个地方:
11,WebViewClient.onPageFinished()。
你永远无法确定当WebView调用这个方法的时候,网页内容是否真的加载完毕了。
当前正在加载的网页产生跳转的时候这个方法可能会被多次调用,StackOverflow上有比较具体的解释
(How to listen for a Webview finishing loading a URL in Android?), 但其中列举的解决方法并不完美。
所以当你的WebView需要加载各种各样的网页并且需要在页面加载完成时采取一些操作的话,可能WebChromeClient.onProgressChanged()
比WebViewClient.onPageFinished()都要靠谱一些。
12,WebView后台耗电问题:
当你的程序调用了WebView加载网页,WebView会自己开启一些线程(?),如果你没有正确地将WebView销毁的话,
这些残余的线程(?)会一直在后台运行,由此导致你的应用程序耗电量居高不下。
对此我采用的处理方式比较偷懒,简单又粗暴(不建议),即在Activity.onDestroy()中直接调用System.exit(0),,使得应用程序完全被移出虚拟机,这样就不会有任何问题了。
13,切换WebView闪屏问题。
如果你需要在同一个ViewGroup中来回切换不同的WebView(包含了不同的网页内容)的话,你就会发现闪屏是不可避免的。
这应该是Android硬件加速的Bug,如果关闭硬件加速这种情况会好很多,但无法获得很好的浏览体验,你会感觉网页滑动的时候一卡一卡的,不跟手。
14,数据积累问题。
开启缓存什么的有利于网页的浏览体验,但你会发现即使是清除了必要的内容,比如Cache、Cookie、Form Data、History、Password等等东西,
你的应用程序所占用的存储空间还是会越来越大,到最后只好手动到系统设置的应用信息界面里清除数据了 :

15,滚动条问题。
Android System WebView的横向滚动条真是好粗的有木有...
以上是我能想到的啦,没想到的大概是不重要所以自动忽略啦~

另外针对Android System WebView的相关开发,推荐看看Google官方的示例教程 GoogleChrome/chromium-webview-samples ·GitHub

 

 

原文地址:https://www.cnblogs.com/awkflf11/p/4369297.html