Android WebView使用深入浅出

目前很多android app都内置了可以显示web页面的界面,会发现这个界面一般都是由一个叫做WebView的组件渲染出来的,学习该组件可以为你的app开发提升扩展性。

先说下WebView的一些优点:

  • 可以直接显示和渲染web页面,直接显示网页
  • webview可以直接用html文件(网络上或本地assets中)作布局
  • 和JavaScript交互调用

一、基本使用

首先layout中即为一个基本的简单控件:

<WebView
        android:id="@+id/webView1"
        android:layout_width="fill_parent"
        android:layout_height="fill_parent"
        android:layout_marginTop="10dp" />

同时,因为要房访问网络,所以manifest中必须要加uses-permission:

<uses-permission android:name="android.permission.INTERNET"/>

在activity中即可获得webview的引用,同时load一个网址:

webview = (WebView) findViewById(R.id.webView1);
webview.loadUrl("http://www.baidu.com/");
//webview.reload();// reload page

这个时候发现一个问题,启动应用后,自动的打开了系统内置的浏览器,解决这个问题需要为webview设置 WebViewClient,并重写方法:

webview.setWebViewClient(new WebViewClient(){
            @Override
            public boolean shouldOverrideUrlLoading(WebView view, String url) {
                view.loadUrl(url);
                return true;
            }
        });

若自己定义了一个页面加载进度的progressbar,需要展示给用户的时候,可以通过如下方式获取webview内页面的加载进度:

webview.setWebChromeClient(new WebChromeClient(){
            @Override
            public void onProgressChanged(WebView view, int newProgress) {
                //get the newProgress and refresh progress bar
            }
        });

每个页面,都有一个标题,比如www.baidu.com这个页面的title即“百度一下,你就知道”,那么如何知道当前webview正在加载的页面的title呢:

webview.setWebChromeClient(new WebChromeClient(){
            @Override
            public void onReceivedTitle(WebView view, String title) {
                titleview.setText(title);//a textview
            }
        });

二、通过webview控件下载文件

通常webview渲染的界面中含有可以下载文件的链接,点击该链接后,应该开始执行下载的操作并保存文件到本地中。webview来下载页面中的文件通常有两种方式:

1. 自己通过一个线程写java io的代码来下载和保存文件(可控性好)

2. 调用系统download的模块(代码简单)

方法一:

首先要写一个下载并保存文件的线程类

public class HttpThread extends Thread {
  private String mUrl;
  public HttpThread(String mUrl) {
    this.mUrl = mUrl;
  }
  @Override
  public void run() {
    URL url;
    try {
      url = new URL(mUrl);
      HttpURLConnection conn = (HttpURLConnection) url.openConnection();
      conn.setDoInput(true);
      conn.setDoOutput(true);
      InputStream in = conn.getInputStream();
      File downloadFile;
      File sdFile;
      FileOutputStream out = null;
      if(Environment.getExternalStorageState().equals(Environment.MEDIA_UNMOUNTED)){
        downloadFile = Environment.getExternalStorageDirectory();
        sdFile = new File(downloadFile, "test.file");
        out = new FileOutputStream(sdFile);
      }
      //buffer 4k
      byte[] buffer = new byte[1024 * 4];
      int len = 0;
      while((len = in.read(buffer)) != -1){
        if(out != null)
          out.write(buffer, 0, len);
      }
      //close resource
      if(out != null)
        out.close();
      if(in != null){
        in.close();
      }
    } catch (IOException e) {
      // TODO Auto-generated catch block
      e.printStackTrace();
    }
  }
}

随后要实现一个DownloadListener接口,这个接口实现方法OnDownloadStart(),当用户点击一个可以下载的链接时,该回调方法被调用同时传进来该链接的URL,随后即可以对该URL塞入HttpThread进行下载操作:

//创建DownloadListener (webkit包)
class MyDownloadListenter implements DownloadListener{
  @Override
  public void onDownloadStart(String url, String userAgent,
    String contentDisposition, String mimetype, long contentLength) {
      System.out.println("url ==== >" + url);
      new HttpThread(url).start();
  }
    }
//给webview加入监听
webview.setDownloadListener(new MyDownloadListenter());

方法二:

直接发送一个action_view的intent即可:

class MyDownloadListenter implements DownloadListener{
        @Override
        public void onDownloadStart(String url, String userAgent,
      String contentDisposition, String mimetype, long contentLength) {
  System.out.println("url ==== >" + url);
  //new HttpThread(url).start();

  Uri uri = Uri.parse(url);
  Intent intent = new Intent(Intent.ACTION_VIEW, uri);
  startActivity(intent);
        }
    }

三、错误处理

当我们使用浏览器的时候,通常因为加载的页面的服务器的各种原因导致各种出错的情况,最平常的比如404错误,通常情况下浏览器会提示一个错误提示页面。事实上这个错误提示页面是浏览器在加载了本地的一个页面,用来提示用户目前已经出错了。

但是当我们的app里面使用webview控件的时候遇到了诸如404这类的错误的时候,若也显示浏览器里面的那种错误提示页面就显得很丑陋了,那么这个时候我们的app就需要加载一个本地的错误提示页面,即webview如何加载一个本地的页面。

1. 首先我们需要些一个html文件,比如error_handle.html,这个文件里面就是当出错的时候需要展示给用户看的一个错误提示页面,尽量做的精美一些。然后将该文件放置到代码根目录的assets文件夹下。

2. 随后我们需要复写WebViewClient的onRecievedError方法,该方法传回了错误码,根据错误类型可以进行不同的错误分类处理

webview.setWebViewClient(new WebViewClient(){

      @Override
      public void onReceivedError(WebView view, int errorCode,
        String description, String failingUrl) {
    switch(errorCode)
    {
    case HttpStatus.SC_NOT_FOUND:
        view.loadUrl("file:///android_assets/error_handle.html");
        break;
    }
      }
  });

其实,当出错的时候,我们可以选择隐藏掉webview,而显示native的错误处理控件,这个时候只需要在onReceivedError里面显示出错误处理的native控件同时隐藏掉webview即可。

四、webview同步cookies

cookies是服务器用来保存每个客户的常用信息的,下次客户进入一个诸如登陆的页面时服务器会检测cookie信息,如果通过则直接进入登陆后的页面。

在webview中,如果之前已经登陆过了,那么下次再进入同样的登陆界面时,若需要再次登陆的话,一定会很恼人,所以这里提供一个webview同步cookies的方法。

1.首先,我们假设某个网站的登陆界面需要提供两个参数,一个是name,一个是pwd,那么要是对这个页面进行登陆,那么必须给与这两个信息。我们假设服务器已经注册了name为jason,pwd为123456这个账号。

2.下面,写一个Thread用来将name和pwd自动的登入,在服务器返回的response中获得cookie信息,稍后对这个cookie进行保存,这里先给出这个Thread的代码:

public class HttpCookie extends Thread {
  private Handler mHandler;
  public HttpCookie(Handler mHandler) {
    this.mHandler = mHandler;
  }
  @Override
  public void run() {
    HttpClient client = new DefaultHttpClient();
    HttpPost post = new HttpPost("");//this place should add the login address

    List<NameValuePair> list = new ArrayList<NameValuePair>();
    list.add(new BasicNameValuePair("name", "jason"));
    list.add(new BasicNameValuePair("pwd", "123456"));
    try {
      post.setEntity(new UrlEncodedFormEntity(list));
      HttpResponse reponse = client.execute(post);
      if(reponse.getStatusLine().getStatusCode() == HttpStatus.SC_OK){
        AbstractHttpClient absClient = (AbstractHttpClient) client;
        List<Cookie> cookies = absClient.getCookieStore().getCookies();
        for(Cookie cookie:cookies){
          if(cookie != null){
            //TODO
            //this place would get the cookies
          }
        }
      }
    } catch (UnsupportedEncodingException e) {
      // TODO Auto-generated catch block
      e.printStackTrace();
    } catch (ClientProtocolException e) {
      // TODO Auto-generated catch block
      e.printStackTrace();
    } catch (IOException e) {
      // TODO Auto-generated catch block
      e.printStackTrace();
    }
  }
}

由于这是一个子线程,所以需要在主线程中创建并执行。

同时,因为其实子线程,那么里面必须含有一个handler的元素,用来当成功获取cookie后通知主线程进行同步和保存。初始化这个子线程的时候需要将主线程上的handler给传过来,随后在以上代码的TODO中发送消息,让主线程记录cookie,发送的这个消息需要将cookie信息包含进去:

if(cookie != null){
  //TODO
  //this place would get the cookies
  Message msg = new Message();
  msg.obj = cookie;
  if(mHandler != null){
    mHandler.sendMessage(msg);
    return;
  }
}

随后在主线程中(webview加载登陆界面前),在handler中将会获取到cookie信息,下面将对该cookie进行保存和同步:

private Handler mHandler = new Handler(){
    public void handleMessage(android.os.Message msg)
    {
      CookieSyncManager.createInstance(MainActivity.this);
      CookieManager cookieMgr = CookieManager.getInstance();
      cookieMgr.setAcceptCookie(true);
      cookieMgr.setCookie("", msg.obj.toString());// this place should add the login host address(not the login index address)
      CookieSyncManager.getInstance().sync();
      webview.loadUrl("");// login index address
    };
  };

这个时候发现webview加载的login index页面中可以自动的登陆了并显示登陆后的界面。

五、 WebView与JavaScript的交互

1. webview调用js

mWebView.loadUrl("javascript:do()");

以上是webview在调用js中的一个叫做do的方法,该js所在的html文件大致如下:

<html>
  <script language="javascript">
    /* This function is invoked by the webview*/
    function do() {
      alert("1");
    }
  </script>
  <body>
    <a><div style="width:80px;
      margin:0px auto;
      padding:10px;
      text-align:center;
      border:2px solid #111111;" >
        <img id="droid" src="xx.png"/><br>
        Click me!
    </div></a>
  </body>
</html>

2. js调用webview

我们假设下列的本地类是要给js调用的:

package com.test.webview;
class DemoJavaScriptInterface {
  DemoJavaScriptInterface() {
  }
  /**
   * This is not called on the UI thread. Post a runnable to invoke
   * loadUrl on the UI thread.
   */
  public void clickOnAndroid() {
      mHandler.post(new Runnable() {
    public void run() {
        //TODO
    }
      });
  }
    }

首先给webview设置:

mWebview.setJavaScriptEnabled(true);

随后将本地的类(被js调用的)映射出去:

mWebView.addJavascriptInterface(new DemoJavaScriptInterface(), "demo");

“demo”这个名字就是公布出去给JS调用的,那么js久可以直接用下列代码调用本地的DemoJavaScriptInterface类中的方法了:

<body>
    ...
</body>

六、WebView与JavaScript相互调用混淆问题

若webview中的js调用了本地的方法,正常情况下发布的debug包js调用的时候是没有问题的,但是通常发布商业版本的apk都是要经过混淆的步骤,这个时候会发现之前调用正常的js却无法正常调用本地方法了。

这是因为混淆的时候已经把本地的代码的引用给打乱了,导致js中的代码找不到本地的方法的地址。

解决这个问题很简单,即在proguard.cfg文件中加上一些代码,声明本地中被js调用的代码不被混淆。下面举例说明:

第五节中被js调用的那个类DemoJavaScriptInterface的包名为com.test.webview,那么就要在proguard.cfg文件中加入:

-keep public class com.test.webview.DemoJavaScriptInterface{
    public <methods>;
}

若是内部类,则大致写成如下形式:

-keep public class com.test.webview.DemoJavaScriptInterface$InnerClass{
    public <methods>;
}

若android版本比较新,可能还需要添加上下列代码:

-keepattributes *Annotation*
-keepattributes *JavascriptInterface*

来自:http://www.cnblogs.com/soaringEveryday/p/4495221.html

时间: 2024-08-01 05:45:57

Android WebView使用深入浅出的相关文章

Android WebView 开发教程

1.WebView的使用 (a). 创建WebView的实例加入到Activity中 WebView webview = new WebView(this); setContentView(webview); 或者在xml中配置WebView <Webview android:layout_width="match_parent" android:layout_height="match_parent" > </Webview> (b). 访

屏蔽电信流氓广告造成的诡异的问题--Android WebView 长时间不能加载页面

发现在家里的时候用Android App里的WebView打开网站很慢,会有十几秒甚至更长时间的卡住. 但是在电脑上打开同样的网页却很快. 查找这个问题的过程比较曲折,记录下来. 抓取Android网络数据 为了调试这个问题,首先要抓取Android的网络包数据.开始时,是想用Wireshark来抓包的,但是很麻烦,tcpdump在手机要root权限. 于是转换思路,能不能在Android上设置代理,来抓包? 但是fiddler没有linux版本,于是转用BurpSuite了. 设置Androi

Android WebView 输入框键盘不弹出

问题 在Android中使用内嵌的WebView加载HTML网页时,如果html页面中存在输入框.那么在有些手机设备中,当输入框获取焦点时,系统输入法键盘无法正确弹出,从而无法完成正常的输入要求 在做APP时,自己也遇到了这个问题,以下是自己解决的方法,有可能不适合大家所遇到的情况,但值得借鉴~ WebView设置问题 有些时候我们设计的html页面并不能够很好的适应WebView,尤其我们的html页面是为PC浏览器设计的时候,当使用WebView来加载时,界面很可能会发生错乱,当input输

android webview 通过html5播放在线视频 切换大屏

1.添加网络访问权限 <uses-permission android:name="android.permission.INTERNET" /> 2.webview添加全屏支持 developer官方文档关于html5支持视频播放描述如下:In order to support inline HTML5 video in your application, you need to have hardware acceleration turned on, and set

android webview点击返回键返回上一级activity

android webview点击返回键返回上一个activity 1 @Override 2 public boolean onKeyDown(int keyCode, KeyEvent event) { 3 // TODO Auto-generated method stub 4 if (keyCode == KeyEvent.KEYCODE_BACK && event.getRepeatCount() == 0) { 5 return true; 6 } 7 return super

Android:WebView中对图片注册上下文菜单

前言 今天一朋友问我一个问题,就是如何在WebView控件中的图片增加上下文菜单,以便增加保存图片等功能.今天就给他简单做了一个演示Demo,现写下来,给有相同问题的朋友提供些许思路吧. 概要实现 其实这个功能很简单,没有太复杂的东西,就是对WebView的控件的使用,一是给WebView注册了上下文菜单事件,二是在响应事件中去判断事件源的类型,如果是图片类型,则把url取出来 注册上下文菜单事件 这个就比较简单了通过下面的代码即可完成. WebView vw = (WebView) findV

android webview &quot;Uncaught SecurityError: Failed to read the &#39;localStorage&#39; property from &#39;Window&#39;: Access is denied for this document.&quot;, source: (1)

这种错误,可以在loadurl之后再调用 localstorage.setitem()即可解决. "Uncaught SecurityError: Failed to read the 'localStorage' property from 'Window': Access is denied for this document.", source:  (1) android webview "Uncaught SecurityError: Failed to read t

android WebView详细使用方法(转)

1.最全面的Android Webview详解 2.最全面总结 Android WebView与 JS 的交互方式 3.你不知道的 Android WebView 使用漏洞

Android WebView 开发详解(一)

转载请注明出处  http://blog.csdn.net/typename/article/details/39030091 powered by meichal zhao 概览: Android WebView在Android平台上是一个特殊的View, 他能用来显示网页,这个类可以被用来在你的app中仅仅显示一张在线的网页,还可以用来开发浏览器.WebView内部实现是采用渲染引擎来展示view的内容,提供网页前进后退,网页放大,缩小,搜索,前端开发者可以使用web inspector(A