Android——网络编程
WebView简单体验——可内嵌在应用程序的浏览器
WebView是一个封装了网络请求,数据解析的这么一个控件,可作为程序的内置浏览器使用
注意声明网络访问权限:android.permission.INTERNET
1 <WebView 2 android:id="@+id/webview" 3 android:layout_width="match_parent" 4 android:layout_height="match_parent" />
1 protected void onCreate(Bundle savedInstanceState) { 2 super.onCreate(savedInstanceState); 3 setContentView(R.layout.activity_main); 4 WebView webView = (WebView) findViewById(R.id.webview); 5 // step1 设置支持js 6 webView.getSettings().setJavaScriptEnabled(true); 7 // step2 设置客户端本页根据传入的url打开网页,不需要调用系统浏览器 8 webView.setWebViewClient(new WebViewClient() { 9 10 @Override 11 public boolean shouldOverrideUrlLoading(WebView view, String url) { 12 // 根据url访问网络 13 view.loadUrl(url); 14 // 设置成true,表示跳转网页时在本页打开 15 return true; 16 } 17 18 }); 19 // 默认访问百度,测试简写不可用 20 webView.loadUrl("http://www.baidu.com"); 21 // 注意访问网络需要权限 22 }
http网络请求
方式一:HttpURLConnection
网络请求是比较耗时的操作,如果直接在主线程中使用的话,可能会出现ANR问题,所以通常是要开启一个子线程进行操作的
下面是常见写法,但是存在着一些弊端=》通常应用程序里面网络请求不止一个,所以应当封装成一个工具类,当需要请求的时候,直接用工具类传入地址进行访问
注:用模拟器调试的话,可以开启本地apache服务器,请求 10.0.2.2/xxx.xml
1 private void sendRequestWithHttpURLConnection() { 2 // 发送网络请求,必须开启子线程,否则可能阻塞主线程,ANR 3 new Thread(new Runnable() { 4 5 @Override 6 public void run() { 7 // 注意别写成HttpsURLConnection 8 HttpURLConnection connection = null; 9 try { 10 // 1.得到HttpURLConnection实例——通过url的open..获得 11 URL url = new URL("http://www.ytslf.com/get_data.xml"); 12 connection = (HttpURLConnection) url.openConnection(); 13 14 // 2.设置请求方式以及请求延时 15 connection.setRequestMethod("GET");// GET表示需要从服务器得到数据,POST表示需要向服务器提交数据 16 connection.setConnectTimeout(8000); 17 connection.setReadTimeout(8000); 18 19 // 3.获取InputStream流,读取服务器返回的数据 20 InputStream in = connection.getInputStream(); 21 BufferedReader bufr = new BufferedReader( 22 new InputStreamReader(in)); 23 String line = null; 24 StringBuilder sb = new StringBuilder(); 25 while ((line = bufr.readLine()) != null) { 26 sb.append(line); 27 } 28 // 4.将获取到的数据交给Handler处理 29 Message msg = new Message(); 30 msg.what = SHOW_RESPONSE; 31 msg.obj = sb.toString(); // 可以直接传入StringBuilder,但为了统一,传String 32 handler.sendMessage(msg); 33 } catch (IOException e) { 34 e.printStackTrace(); 35 } finally { 36 if (connection != null) { 37 // 关闭连接 38 connection.disconnect(); 39 } 40 } 41 42 } 43 }).start(); 44 }
方式二:HttpClient
HttpClient算是HttpURLConnection的升级版把
1 private void sendRequestWithHttpClient() { 2 new Thread(new Runnable() { 3 4 @Override 5 public void run() { 6 try { 7 // 1.得到HttpClient实例 8 HttpClient httpClient = new DefaultHttpClient(); 9 // 2.发送一个GET请求 10 // HttpGet httpGet = new HttpGet("http://www.ytslf.com/get_data.xml"); 11 HttpGet httpGet = new HttpGet("http://www.ytslf.com/get_data.json"); 12 // 通过execute方法执行请求,可以得到服务器反馈的数据,都封装在HttpResponse对象中 13 HttpResponse httpResponse = httpClient.execute(httpGet); 14 15 /* 16 * 发送Post请求较GET复杂,需要提交参数 17 * 1.新建一个HttpPost对象 18 * HttpPost httpPost = new HttpPost("http://www.baidu.com"); 19 * 2.通过NameValuePair集合存放需要提交的参数 20 * List<NameValuePair> params = 21 * new ArrayList<NameValuePair>(); 22 * params.add(newBasicNameValuePair("username", "admin")); 23 * params.add(newBasicNameValuePair("password", "123456")); 24 * UrlEncodedFormEntity entity = new UrlEncodedFormEntity(params, "utf-8"); 25 * httpPost.setEntity(entity); 26 * // 之后就和GET一样的操作 27 * httpClient.execute(httpPost); 28 */ 29 // 3.接下来就是要得到服务器返回的数据了 30 if (httpResponse.getStatusLine().getStatusCode() == 200) { 31 // 如果返回的状态码为200,则表示请求和响应都成功了 32 HttpEntity httpEntity = httpResponse.getEntity(); 33 String response = EntityUtils.toString(httpEntity, 34 "utf-8"); 35 // /*请求测试 36 // 将数据发送到Message队列中 37 Message msg = new Message(); 38 msg.what = SHOW_RESPONSE; 39 msg.obj = response; 40 handler.sendMessage(msg); 41 // */ 42 // 解析测试——Pull方式 43 // Log.d("test", "response ok"); 44 // parseXMLWithPull(response); 45 // Log.d("test", "parse over"); 46 47 48 } 49 50 } catch (Exception e) { 51 e.printStackTrace(); 52 } 53 } 54 }).start(); 55 }
XML解析
方式一:Pull解析
1 private void parseXMLWithPull(String xmdData){ 2 try { 3 // 1.获取XML的PULL解析工厂实例 4 XmlPullParserFactory factory = XmlPullParserFactory.newInstance(); 5 // 2.通过工厂获取XML解析器实例 6 XmlPullParser xmlPullParser = factory.newPullParser(); 7 // 3.XML解析器设置要解析的XML数据 8 xmlPullParser.setInput(new StringReader(xmdData)); 9 // go go go! 10 int eventType = xmlPullParser.getEventType(); 11 String id = ""; 12 String name = ""; 13 String version = ""; 14 15 // 开始解析 16 while(eventType != XmlPullParser.END_DOCUMENT){ 17 String nodeName = xmlPullParser.getName(); 18 switch (eventType) { 19 // 开始解析某个节点 20 case XmlPullParser.START_TAG: 21 if("id".equals(nodeName)){ 22 id = xmlPullParser.nextText(); 23 } 24 else if("name".equals(nodeName)){ 25 name = xmlPullParser.nextText(); 26 } 27 else if ("version".equals(nodeName)) { 28 version = xmlPullParser.nextText(); 29 } 30 break; 31 32 // 完成解析某个节点 33 case XmlPullParser.END_TAG: 34 if("app".equals(nodeName)){ 35 Log.d("test", "id:"+id); 36 Log.d("test", "name:"+name); 37 Log.d("test", "version:"+version); 38 responseText.append(id+"::"+name+"::"+version+"\n"); 39 } 40 break; 41 42 default: 43 break; 44 } 45 eventType = xmlPullParser.next();// 解析下一个 46 } 47 48 49 } catch (Exception e) { 50 e.printStackTrace(); 51 } 52 }
方式二:SAX解析
1 // SAX解析XML 2 private void parseXMLWithSAX(String xmlData){ 3 try { 4 SAXParserFactory factory = SAXParserFactory.newInstance(); 5 XMLReader xmlReader = factory.newSAXParser().getXMLReader(); 6 // 创建自定义的Handler实例 7 ContentHandler handler = new ContentHandler(); 8 // 将实例设置到XMLReader中 9 xmlReader.setContentHandler(handler); 10 // 开始解析 11 xmlReader.parse(new InputSource(new StringReader(xmlData))); 12 } catch (Exception e) { 13 e.printStackTrace(); 14 } 15 }
1 package com.example.networktest; 2 3 import org.xml.sax.Attributes; 4 import org.xml.sax.SAXException; 5 import org.xml.sax.helpers.DefaultHandler; 6 7 import android.util.Log; 8 9 public class ContentHandler extends DefaultHandler { 10 11 private String nodeName; 12 private StringBuilder id; 13 private StringBuilder name; 14 private StringBuilder version; 15 16 /** 17 * 开始解析xml时调用 18 */ 19 @Override 20 public void startDocument() throws SAXException { 21 id = new StringBuilder(); 22 name = new StringBuilder(); 23 version = new StringBuilder(); 24 } 25 26 /** 27 * 开始解析某个节点的时候调用 28 */ 29 @Override 30 public void startElement(String uri, String localName, String qName, 31 Attributes attributes) throws SAXException { 32 // 记录当前节点的名字 33 nodeName = localName; 34 } 35 36 /** 37 * 具体解析内容时调用 38 */ 39 @Override 40 public void characters(char[] ch, int start, int length) 41 throws SAXException { 42 // TODO Auto-generated method stub 43 if ("id".equals(nodeName)) { 44 id.append(ch, start, length); 45 } else if ("name".equals(nodeName)) { 46 name.append(ch, start, length); 47 } else if ("version".equals(nodeName)) { 48 version.append(ch, start, length); 49 } 50 } 51 52 /** 53 * 解析完某个节点的时候调用 54 */ 55 @Override 56 public void endElement(String uri, String localName, String qName) 57 throws SAXException { 58 if("app".equals(localName)){ 59 // StringBuilder读取到的内容可能会有多余的空格或换行符等,通过String的trim()方法去掉 60 Log.d("test", "id:"+id.toString().trim()); 61 Log.d("test", "name:"+name.toString().trim()); 62 Log.d("test", "version:"+version.toString().trim()); 63 64 // 清空StringBuilder 65 id.setLength(0); 66 name.setLength(0); 67 version.setLength(0); 68 } 69 } 70 71 /** 72 * xml全部解析完成时调用 73 */ 74 @Override 75 public void endDocument() throws SAXException { 76 } 77 78 }
JSON解析
json格式如:[{"id":"5","name":"hh","version":"2.1"},{……}]
方式一:JSONObject解析
1 // JSONObject解析json 2 private void parseJSONWithJSONObject(String jsonData){ 3 try { 4 JSONArray jsonArray = new JSONArray(jsonData); 5 for(int i=0;i<jsonArray.length();i++){ 6 JSONObject jsonObject = jsonArray.getJSONObject(i); 7 String id = jsonObject.getString("id"); 8 String name = jsonObject.getString("name"); 9 String version = jsonObject.getString("version"); 10 Log.d("test", "id:"+id); 11 Log.d("test", "name:"+name); 12 Log.d("test", "version:"+version); 13 responseText.append(id+"::"+name+"::"+version+"\n"); 14 } 15 } catch (Exception e) { 16 e.printStackTrace(); 17 } 18 }
方式二:GSON解析
利用GSON解析,需要用到GSON的jar包,将jar包拷贝至项目的libs文件夹即可
利用GSON,可以将json数据直接解析成一个对象,利用该对象就能的到相应的数据,这一点是极好的
例如:要解析{"name":"Tom","age":"20"}
可以定义一个具有name和age字段的Person类,解析如下
Gson gson = new Gson();
Person person = gson.fromJson(jsonData,Person.class);
如果解析的是json数组,则需要用到TypeToken,将期望解析成的数据类型传入到fromJson的参数当中
List<Person> people = gson.fromJson(jsonData,new TypeToken<List<Person>>(){}.getType());
解析:
[{"id":"5","version":"5.5","name":"Angry Birds"}, {"id":"6","version":"7.0","name":"Clash of Clans"}, {"id":"7","version":"3.5","name":"Hey Day"}]
1 package com.example.networktest; 2 3 public class App { 4 private String id; 5 private String name; 6 private String version; 7 8 public String getId() { 9 return id; 10 } 11 12 public void setId(String id) { 13 this.id = id; 14 } 15 16 public String getName() { 17 return name; 18 } 19 20 public void setName(String name) { 21 this.name = name; 22 } 23 24 public String getVersion() { 25 return version; 26 } 27 28 public void setVersion(String version) { 29 this.version = version; 30 } 31 32 }
1 // GSON第三方jar包解析json 2 private void parseJSONWithGSON(String jsonData){ 3 try { 4 Gson gson = new Gson(); 5 List<App> appList = gson.fromJson(jsonData, new TypeToken<List<App>>() {}.getType()); 6 for(App app : appList){ 7 String id = app.getId(); 8 String name = app.getName(); 9 String version = app.getVersion(); 10 Log.d("test", "id:"+id); 11 Log.d("test", "name:"+name); 12 Log.d("test", "version:"+version); 13 // responseText.append(id+"::"+name+"::"+version+"\n"); 14 } 15 } catch (Exception e) { 16 e.printStackTrace(); 17 } 18 }
Http请求的最佳写法:封装为工具类+利用Java回调监听机制
1.封装,定义一个HttpUtil工具类,将请求的代码封装成静态方法,且需要传入网络地址作为参数
1 public class HttpUtil { 2 public static String sendHttpRequest(String address) { 3 HttpURLConnection connection = null; 4 try { 5 return null; 6 } catch (Exception e) { 7 // TODO: handle exception 8 return null; 9 } finally { 10 if (connection != null) { 11 connection.disconnect(); 12 } 13 } 14 } 15 }
细节暂时不写……
这样封装了之后,就可以愉快的调用了
String response = HttpUtil.sendHttpRequest("http://www.baidu.com");
但是仍然存在问题:
1.没有开启线程,则仍然默认是在主线程中执行网络请求,可能导致主线程阻塞
2.如果开启线程,如何能够让子线程执行完之后返回数据?这确实是一个问题!!!
解决方法:利用Java的回调机制
回调机制概述
最佳写法
1.根据需求,定义公用接口HttpCallbackListener
1 public interface HttpCallbackListener { 2 public abstract void onFinish(String response); 3 public abstract void onError(Exception e); 4 }
2.在工具类中转入这个接口参数,在得到需要异步处理的结果后,执行接口中的方法,从而实现数据返回
1 import java.io.BufferedReader; 2 import java.io.InputStream; 3 import java.io.InputStreamReader; 4 import java.net.HttpURLConnection; 5 import java.net.URL; 6 7 8 public class HttpUtil { 9 public static void sendHttpRequest(final String address , final HttpCallbackListener listener) { 10 new Thread(new Runnable() { 11 12 @Override 13 public void run() { 14 HttpURLConnection connection = null; 15 try { 16 // 获取实例 17 URL url = new URL(address); 18 connection = (HttpURLConnection) url.openConnection(); 19 20 // 设置访问方式和网络延迟 21 connection.setRequestMethod("GET"); 22 connection.setConnectTimeout(8000); 23 connection.setReadTimeout(8000); 24 25 // 设置是否URLConnection是否允许输入输出 26 connection.setDoInput(true); 27 connection.setDoOutput(true); 28 29 // 获取服务器输入流 30 InputStream in = connection.getInputStream(); 31 32 // 疯狂的读 33 StringBuilder response = new StringBuilder(); 34 BufferedReader bufr = new BufferedReader(new InputStreamReader(in)); 35 String line = null; 36 while((line = bufr.readLine())!=null){ 37 response.append(line); 38 } 39 // 利用回调,异步返回数据 40 if(listener!=null){ 41 // 回调onFinish 42 listener.onFinish(response.toString()); 43 } 44 45 } catch (Exception e) { 46 if(listener!=null){ 47 // 回调onError 48 listener.onError(e); 49 } 50 } finally { 51 if (connection != null) { 52 connection.disconnect(); 53 } 54 } 55 } 56 }).start(); 57 } 58 }
3.使用下这个工具类~~
1 String address = "http://www.ytslf.com/get_data.json"; 2 HttpUtil.sendHttpRequest(address, new HttpCallbackListener() { 3 4 @Override 5 public void onFinish(String response) { 6 // 根据放回的数据,执行具体的逻辑处理,这里是交给handler,然后让handler解析json 7 Message msg = new Message(); 8 msg.what = SHOW_RESPONSE; 9 msg.obj = response; 10 handler.sendMessage(msg); 11 } 12 13 @Override 14 public void onError(Exception e) { 15 // 异常处理,这里就不写了 16 17 } 18 });