您的位置:首页 > 移动开发

Android开发使用WebView实战技巧

2015-04-02 11:23 447 查看
转载Lee_Allen的文章,感谢其无私分享。

前段时间做项目的时候,在项目中用了WebView组件,遇到了一些问题,所以特地找来了一些资料,学习怎么解决,现在将学习的内容整理成一篇博客记录在这里,方便以后再次遇到时可以快速查看并且解决问题。我们知道,Android中WebView是一个大型的组件,其实WebView是集成了著名的浏览器引擎webkit的一个框架,主要是用来在Android应用中加载渲染网页的。好了,这篇学习笔记之前,我也学习了Google的官方文档,介绍WebView的基本用法,并且翻译好了,地址是:/article/1534100.html,请还没用过WebView的同学,先查看一下这篇文章后,再看我下面的内容。

获取网页的Title信息

我们在打开某些应用的时候,例如今日头条新闻客户端等,有些页面是使用WebView加载的,然而经常在界面的顶端会有标题栏,而且标题栏的内容不是我们自己在程序中写死的,而是动态从网页中获取的,那么我们怎么获取呢?大家都知道,在Html中,一个完整的网页中必然会包含<title></title>标签,标签之间就是标题的内容,so我们只需要将Html中的title标签中的内容获取到就可以了。要获取Title内容就必须设置WebView的setWebChromeClient(WebChromeClient client)方法,传递一个WebChromeClient
对象并且重写其下的shouldOverrideUrlLoading(WebView view, String url)方法,该方法中回调了title就是<title></title>标签间内容。例如:

[java] view
plaincopyprint?





...

mWebView = (WebView) findViewById(R.id.webview);

//开启JavaScript支持

mWebView.getSettings().setJavaScriptEnabled(true);

//加载网页

mWebView.loadUrl("http://www.baidu.com");

//强制在webview打开网页,防止使用系统默认的浏览器打开网页

mWebView.setWebViewClient(new WebViewClient() {



@Override

public boolean shouldOverrideUrlLoading(WebView view, String url) {

view.loadUrl(url);

return super.shouldOverrideUrlLoading(view, url);

}



});

mWebView.setWebChromeClient(new WebChromeClient() {



@Override

public void onReceivedTitle(WebView view, String title) {

//onReceivedTitle可以回调网页的title

tv_title.setText(title);

super.onReceivedTitle(view, title);

}



});

...



如上图所示,webview中打开了网页,并且将title现在在了TextView标题上。

用WebView下载文件

同样的,当我们在使用浏览器的时候,很多时候会需要下载文件,浏览器是个功能十分强大的应用,可以帮助我们下载网上的文件,那么如果我们的应用中集成了WebView组件,其中渲染的网页也有文件可供下载时,我们该怎么去做下载功能呢?同样,既然浏览器功能那么强大,我们的WebView组件也不差。下面我们看看WebView是怎样下载文件的。

[java] view
plaincopyprint?





public class DownloadActivity extends Activity {



private WebView mWebView;



@Override

protected void onCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState);

requestWindowFeature(Window.FEATURE_NO_TITLE);

setContentView(R.layout.activity_download);

mWebView = (WebView) findViewById(R.id.webview);

// 开启JavaScript支持

mWebView.getSettings().setJavaScriptEnabled(true);

mWebView.loadUrl("http://img.mukewang.com/down/54eec5f10001b17600000000.zip");

mWebView.setWebViewClient(new WebViewClient() {

@Override

public boolean shouldOverrideUrlLoading(WebView view, String url) {

view.loadUrl(url);

return super.shouldOverrideUrlLoading(view, url);

}

});

// 监听下载

mWebView.setDownloadListener(new DownloadListener() {



@Override

public void onDownloadStart(String url, String userAgent,

String contentDisposition, String mimetype, long contentLength) {

System.out.println("url:" + url);

if (url.endsWith(".zip")) {

// 如果传进来url包含.zip文件,那么就开启下载线程

System.out.println("download start...");

new DownloadThread(url).start();

}

}

});

}



/**

* 执行下载的线程

*/

class DownloadThread extends Thread {

private String mUrl;



public DownloadThread(String url) {

this.mUrl = url;

}



@Override

public void run() {

try {

URL httpUrl = new URL(mUrl);

HttpURLConnection conn = (HttpURLConnection) httpUrl.openConnection();

conn.setDoInput(true);

conn.setDoOutput(true);

conn.setConnectTimeout(5000);

conn.setReadTimeout(5000);



InputStream in = conn.getInputStream();

FileOutputStream out = null;

// 获取下载路径

File downloadFile;

File sdFile;

if (Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) {

downloadFile = Environment.getExternalStorageDirectory();

sdFile = new File(downloadFile, "download.zip");

out = new FileOutputStream(sdFile);

}

byte[] b = new byte[8 * 1024];

int len;

while ((len = in.read(b)) != -1) {

if (out != null) {

out.write(b, 0, len);

}

}

if (out != null) {

out.close();

}

if (in != null) {

in.close();

}

System.out.println("download success...");

} catch (MalformedURLException e) {

e.printStackTrace();

} catch (IOException e) {

e.printStackTrace();

}

}

}



}

其实特别简单,WebView中已经给我们提供好一个实现下载的接口以及回调方法,这个接口就是android.webkit.DownloadListener,我们首先调用WebView的setDownloadListener(DownloadListener listener)方法,在方法参数中传入DownloadListener对象,重写其中的onDownloadStart方法,在这个方法中开启下载功能的线程,让线程帮助我们去服务器下载文件。

WebView错误码的处理

WebView错误码处理是什么意思呢?我们在使用Android设备的时候,可能某些时候网络不好或者没有网络,这时候设备就访问不到服务器了,加载不了Html页面。一般情况下,当我们的设备无网络情况下加载一个Html时,会自行弹出Android默认的错误页面,如下图:



好了,这就是Android系统中给我们默认的页面了,是不是值得我们吐槽一下呢?同样将这个页面呈现给用户也会被骂死的,那么我们该怎么处理这样的情况呢?方式也很简单,主要有2种方式实现,一是加载一个本地的Html页面,告知网络异常,二是通过布局的方式自定义一个错误提示界面,加载到主界面。好了,上面的两种实现方式都不是重点,我们不讨论怎么***一个好看的Html Error页面或者一个好看的XML布局。

监听WebView加载出错是在setWebViewClient()的传递WebViewClient对象下的onReceivedError()方法中完成处理的,下面是一个简单的处理方式:

[java] view
plaincopyprint?





mWebView.setWebViewClient(new WebViewClient() {

...

@Override

public void onReceivedError(WebView view, int errorCode, String description, String failingUrl) {

//网络异常,WebView加载我们自定义的页面

super.onReceivedError(view, errorCode, description, failingUrl);

view.loadUrl("file:///android_asset/error.html");

}



});

好了,上面代码中做了简单处理:重新加载本地的assets目录的html静态页面。

WebView同步cookie

cookie是什么呢?这里需要一些Java服务器上面的知识了,简单来说Cookie算是服务器上一种缓存机制了,例如我们在登录的时候第一次输入了登录的用户名和密码,并且保存了密码,那么下次我们再次打开这个网页的时候就会自动读取cookie完成自动登录了。如果这个含有cookie的页面使用WebView加载的话,该怎样也能实现相同的功能呢?我们只有设置WebView与Cookie同步即可。

[java] view
plaincopyprint?





public class CookieTestActivity extends Activity {



private WebView mWebView;



private Handler mHandler = new Handler() {

public void handleMessage(Message msg) {

// 同步Cookie

CookieSyncManager.createInstance(CookieTestActivity.this);

CookieManager cookieManager = CookieManager.getInstance();

cookieManager.setAcceptCookie(true);

cookieManager.setCookie("http://192.168.1.105:8080/webs", msg.obj.toString());

CookieSyncManager.getInstance().sync();

mWebView.loadUrl("http://192.168.1.105:8080/webs/index.jsp");

};

};



@Override

protected void onCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState);

requestWindowFeature(Window.FEATURE_NO_TITLE);

setContentView(R.layout.activity_download);

mWebView = (WebView) findViewById(R.id.webview);

// 开启JavaScript支持

mWebView.getSettings().setJavaScriptEnabled(true);

String url = "http://192.168.1.105:8080/webs/login.jsp";

new HttpCookie(mHandler, url).start();

}



/**

* 访问服务器,读取Cookie信息

*/

class HttpCookie extends Thread {

private Handler handler;

private String url;



public HttpCookie(Handler handler, String url) {

this.handler = handler;

this.url = url;

}



@Override

public void run() {

//使用httpClient向服务器发送POST请求

HttpClient client = new DefaultHttpClient();

HttpPost post = new HttpPost(url);

List<NameValuePair> list = new ArrayList<NameValuePair>();

list.add(new BasicNameValuePair("name", "zhangsan"));

list.add(new BasicNameValuePair("age", "22"));

try {

post.setEntity(new UrlEncodedFormEntity(list));

HttpResponse response = client.execute(post);

if (response.getStatusLine().getStatusCode() == 200) {

//访问服务器成功,从服务器读取Cookie信息

AbstractHttpClient absClient = (AbstractHttpClient) client;

List<Cookie> cookies = absClient.getCookieStore().getCookies();

for (Cookie cookie : cookies) {

// 将Cookie发送到UI线程

Log.d("TAG", "name=" + cookie.getName() + ",age=" + cookie.getValue());

Message message = Message.obtain();

message.obj = cookie;

handler.sendMessage(message);

return;

}

}

} catch (UnsupportedEncodingException e) {

e.printStackTrace();

} catch (ClientProtocolException e) {

e.printStackTrace();

} catch (IOException e) {

e.printStackTrace();

}

}

}

}

好,通过上面的示例代码,我们很快就知道怎么设置Cookie同步了,主要代码是在Handler中的。

WebView与JS调用混淆问题

这个问题会经常遇到,当我们在Java中写好了调用JS的代码后,测试的时候完全正常的,然而我们在发布程序的时候,需要混淆打包,混淆打包之后的生存了我们的.apk文件,将apk安装到设备上后,再次调用JS的时候,会发现调用方法失效了,这是一件让人恼火的时候。这时候为了解决这个问题,我们需要对JS调用的相关代码做一些保护,保护措施也非常简单,只需要在混淆文件中将JS调用的Java层代码忽略掉就可以了。

这是Activity:

[java] view
plaincopyprint?





public class MainActivity extends Activity {



private WebView mWebView;



@Override

protected void onCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState);

setContentView(R.layout.activity_main);

mWebView = (WebView) findViewById(R.id.webview);

mWebView.loadUrl("file:///android_asset/index.html");

mWebView.setWebViewClient(new WebViewClient() {

@Override

public boolean shouldOverrideUrlLoading(WebView view, String url) {

view.loadUrl("file:///android_asset/index.html");

return true;

}

});

mWebView.getSettings().setJavaScriptEnabled(true);

mWebView.addJavascriptInterface(new WebHost(this), "js");

}

}

然后我们还要写一个调用JS类:

[java] view
plaincopyprint?





public class WebHost {

private Context mContext;



public WebHost(Context context) {

this.mContext = context;

}



public void callJS() {

Toast.makeText(mContext, "call from js", Toast.LENGTH_SHORT).show();

}

}

JS代码如下:

[html] view
plaincopyprint?





<html>

<title>

<head>Java与JS回调</head>

</title>

<body>

<p>

callJava:<input type="button" value="call from js" onclick="call()"/>

</p>

<script type="text/javascript">

function call(){

js.callJS();

}

</script>

</body>

</html>

此时写的代码在测试阶段没有问题,然后当混淆打包之后,会出现调用JS失效的可能,解决办法:在混淆配置文件proguard.cfg中忽略WebHost里面的方法

[plain] view
plaincopyprint?





-keep public class com.example.webview_02.WebHost{

public <methods>

}

硬件加速后webview闪烁问题

android webview 在3.0+后显示flash要启用硬件加速,开启硬件加速是在manifest中加入: android:hardwareAccelerated="true"

但是开启硬件加速后webview有可能会出现闪烁的问题,解决方法是在webview中设置:

setLayerType(View.LAYER_TYPE_SOFTWARE, null);

这是把webview 中的硬件加速关闭。设置LAYER_TYPE_SOFTWARE后会把当前view转为bitmap保存。这样就不能开多个webview,否则会报out of memory。

解决方法是在webview中加入:
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {

invalidate();

super.onMeasure(widthMeasureSpec, heightMeasureSpec);

}



最近发现个问题,在给webview设置了背景(黑色)以后,在htc one (4.0.3) 和 G20上会闪黑屏。结果改了下设置背景的函数就好了。

setBackgroundColor(Color.parseColor("#000000")); //ok 不会闪黑屏

setBackgroundColor(0x000000);//会闪黑屏
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: