目前很多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 onClick="window.demo.clickOnAndroid()"><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 onload="javascript:demo.clickOnAndroid()">
...
</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*

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

  1. Android WebView 302斗争之旅

    一.背景 越来越多的业务接入,项目内多多少少会出现几个H5页面,只是单纯的提供WebView容器接入H5页面根本满足不了需求,他们需要登录态,需要制定协议控制Native的导航栏,或者需要JsBrid ...

  2. Android WebView useragent

    今天介绍一下Android WebView UserAgent, User-Agent(简称UA)是HTTP请求头部用来标识客户端信息的字符串, 包括操作系统, 浏览器等信息.为了建立手机客户端的信息 ...

  3. android webview开发问题及优化汇总

    我们在native与网页相结合开发的过程中,难免会遇到关于WebView一些共通的问题.就我目前开发过程中遇到的问题以及最后得到的优化方案都将在这里列举出来.有些是老生常谈,有些则是个人摸索得出解决方 ...

  4. Android WebView 开发教程

    声明在先:必须在AndroidMainfest.xml 里面声明权限,否则在Java里面编写的所有WebView浏览网页的代码都无法正常使用 <uses-permission android:n ...

  5. [Android] WebView内的本地网页,使用XMLHttpRequest读取本地档案

    [Android] WebView内的本地网页,使用XMLHttpRequest读取本地档案 问题情景 在Android里,可以使用WebView来呈现本地或是远程的网页内容.但是在显示本地网页时,如 ...

  6. Android webview通过http get下载文件下载两次的问题及解决方法

    一.现象 一般通过Android webview进行下载文件的方法是 1.重写DownloadListener的onDownloadStart方法,在onDownloadStart方法中弹出对话框提示 ...

  7. Android WebView常见问题及解决方案汇总

    Android WebView常见问题解决方案汇总: 就目前而言,如何应对版本的频繁更新呢,又如何灵活多变地展示我们的界面呢,这又涉及到了web app与native app之间孰优孰劣的争论. 于是 ...

  8. android webview 底层实现的逻辑

    其实在不同版本上,webview底层是有所不同的. 先提供个地址给大家查:http://grepcode.com/file/repository.grepcode.com/java/ext/com.g ...

  9. Android WebView访问SSL证书网页(onReceivedSslError)

    Android WebView访问https SSL证书网页,如淘宝,需要在onReceivedSslError添加SSL支持 webview.setWebViewClient(new WebView ...

随机推荐

  1. eclipse插件本地扩展安装

    (1)在Eclipse 安装路径下新建links 路径. (2) 在links 文件夹内,建立X X X .link 文件,该文件的文件名可随意,但后缀必须是link ,通常推荐该文件的文件名与插件名 ...

  2. 用shell脚本写一个for循环

    一.输出十遍北京 for((i=1;i<10;i++))> do> echo '北京';> done 二.死循环 for((;;))do#java -jar producer. ...

  3. (四) openwrt单个ipk编译过程

    Tags : Makefile 本周是成胖子每周一博的第五周. 更好的阅读体验,请点击这里 [TOC] 前言 前一篇博客中,我们已经知道整个openwrt的编译顺序,本文我们来探讨与开发者息息相关的单 ...

  4. Ubuntu 12.04 DNS服务器的配置方法

    Bind是一款开放源码的DNS服务器软件,由美国加州大学Berkeley分校开发和维护的,全名为Berkeley Internet Name Domain它是目前世界上使用最为广泛的DNS服务器软件, ...

  5. 什么办法可以替代distinct

    今天在论坛上看到一个面试题,是说有什么办法可以替代distinct,得到同样的结果.答案都被大家说的差不多了,发现挺有意思的,就记录一下: SQL> select num from t1;    ...

  6. JVM相关问答

    大部分内容来源网络,整理一下,留个底. 问:堆和栈有什么区别? 答:堆是存放对象的,但是对象内的临时变量是存在栈内存中,如例子中的methodVar是在运行期存放到栈中的. 栈是跟随线程的,有线程就有 ...

  7. C++控制台应用程序之贪吃蛇(改进版)

    #include<iostream> #include<stdio.h> #include<stdlib.h> #include<time.h> #in ...

  8. 【jQuery Demo】jQuery打造动态下滑菜单

    作者:漫凯维奇      来源:[教程]jQuery打造动态下滑菜单 Tip:这只是一个转载,源代码可以在上面的来源博文中下载 此教程将分步讲解如何使用JQuery和CSS打造一个炫酷动感菜单.效果如 ...

  9. hihoCoder #1388 : Periodic Signal ( 2016 acm 北京网络赛 F题)

    时间限制:5000ms 单点时限:5000ms 内存限制:256MB 描述 Profess X is an expert in signal processing. He has a device w ...

  10. 分析递归式 Solving Recurrences------GeeksforGeeks 翻译

    在上一章中我们讨论了如何分析循环语句.在现实中,有很多算法是递归的,当我们分析这些算法的时候我们要找到他们的的递归关系.例如归并排序,为了排序一个数组,我们把它平均分为两份然后再重复平分的步骤.最后我 ...