zoukankan      html  css  js  c++  java
  • Android使用开发WebView战斗技能

      转载请注明出处:http://blog.csdn.net/allen315410/article/details/44619181      

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


    获取网页的Title信息

    我们在打开某些应用的时候。例现在日头条新闻客户端等,有些页面是使用WebView载入的。然而常常在界面的顶端会有标题栏,并且标题栏的内容不是我们自己在程序中写死的。而是动态从网页中获取的。那么我们怎么获取呢?大家都知道,在Html中,一个完整的网页中必定会包括<title></title>标签,标签之间就是标题的内容。so我们仅仅须要将Html中的title标签中的内容获取到就能够了。

    要获取Title内容就必须设置WebView的setWebChromeClient(WebChromeClient client)方法。传递一个WebChromeClient 对象而且重写其下的shouldOverrideUrlLoading(WebView view, String url)方法。该方法中回调了title就是<title></title>标签间内容。比如:

    ...
    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是如何下载文件的。

    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方法,在这种方法中开启下载功能的线程。让线程帮助我们去server下载文件。


    WebView错误码的处理

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


    好了,这就是Android系统中给我们默认的页面了。是不是值得我们吐槽一下呢?相同将这个页面呈现给用户也会被骂死的,那么我们该怎么处理这种情况呢?方式也非常easy。主要有2种方式实现,一是载入一个本地的Html页面,告知网络异常。二是通过布局的方式自己定义一个错误提示界面。载入到主界面。

    好了。上面的两种实现方式都不是重点。我们不讨论怎么制作一个好看的Html Error页面或者一个好看的XML布局。

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

    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是什么呢?这里须要一些Javaserver上面的知识了,简单来说Cookie算是server上一种缓存机制了,比如我们在登录的时候第一次输入了登录的username和password。而且保存了password,那么下次我们再次打开这个网页的时候就会自己主动读取cookie完毕自己主动登录了。假设这个含有cookie的页面使用WebView载入的话,该如何也能实现同样的功能呢?我们仅仅有设置WebView与Cookie同步就可以。

    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();
    	}
    
    	/**
    	 * 訪问server。读取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向server发送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) {
    					//訪问server成功。从server读取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调用的相关代码做一些保护,保护措施也很easy。仅仅须要在混淆文件里将JS调用的Java层代码忽略掉就能够了。

    这是Activity:

    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类:

    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>
      <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里面的方法

    -keep public class com.example.webview_02.WebHost{
    	public <methods>
    }

    版权声明:本文博客原创文章。博客,未经同意,不得转载。

  • 相关阅读:
    多线程的互斥(下)——信号量
    Linux下几种另类创建文件之方法
    Linux下VsFTP和ProFTP用户管理高级技巧 之一
    全面了解Linux下Proc文件系统
    许可证大阅兵
    SSH远程快速登录Linux
    为root账户更名
    Linux集群的I/O性能测试
    Think Pad笔记本分区解决思路及方法
    图形界面备份Linux系统介绍
  • 原文地址:https://www.cnblogs.com/blfshiye/p/4756955.html
Copyright © 2011-2022 走看看