Android——网络编程 WebView简单体验——可内嵌在应用程序的浏览器 http网络请求 XML解析 JSON解析 Http请求的最佳写法:封装为工具类+利用Java回调监听机制 最佳写法

WebView是一个封装了网络请求,数据解析的这么一个控件,可作为程序的内置浏览器使用 

注意声明网络访问权限:android.permission.INTERNET

1     <WebView
2         android:
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解析

<apps>
  <app>
    <id>1</id>
    <name>Google Maps</name>
    <version>1.0</version>
  </app>
  <app>
    <id>2</id>
    <name>Chrome</name>
    <version>2.1</version>
  </app>
  <app>
    <id>3</id>
    <name>Google Play</name>
    <version>2.3</version>
  </app>
</apps>

方式一: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+"
");
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 }
ContentHandler

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+"
");
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":"* 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 }
App.java
 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+"
");
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             });