Android開發&Http協議及數據解析

1.通過http發送或讀取數據(Post,Get)

HttpClient方式已被廢棄。
1.1 從指定url獲取返回信息(GET/POST)

private void requestHttpView(final String urlRequest){
    new Thread(new Runnable() {
        @Override
        public void run() {
            HttpURLConnection connection=null;
            try {
                URL url=new URL(urlRequest);
                connection=(HttpURLConnection)url.openConnection();
                //從指定url建立鏈接。
                connection.setRequestMethod("GET");
                //設置連接方式。

/*
connection.setRequestMethod("POST");
//需要發送數據時,將鏈接方式設置爲“POST”
DataOutputStream out = new DataOutputStream(connection.getOutputStream());
//獲取連接的輸出流(輸出流輸出流是相對本機而言的)
out.writeBytes("username=admin&password=123456");
//將需要發送的數據送入輸出流
*/

                connection.setConnectTimeout(5000);
                //設置連接超時。
                connection.setReadTimeout(5000);
                //設置讀取超時。
                InputStream inputStream=connection.getInputStream();
                //獲取鏈接的輸入流。
                BufferedReader reader=new BufferedReader(new InputStreamReader(inputStream));
                //新建BufferedReader對象,讀取數據方便
                StringBuilder builder=new StringBuilder();
                String line;
                while((line=reader.readline())!=null){
                    builder.append(line);
                }//輸入流讀取完畢。
                Message message=new Message();
                message.what=REQUEST_OK;
                message.obj=builder;
                handler.sendMessage(message);
                //將獲取到的數據交給主線程處理。
            } catch (IOException e) {
                Message message=new Message();
                message.what=REQUEST_ERROR;
                handler.sendMessage(message);
            } finally {
                if (connection!=null) {
                    connection.disconnect();
                    //關閉鏈接
                }
            }
        }
    }).start();
}

1.2 被廢棄的HttpClient方式:

private void POSTUrl(String url){
    new Thread(new Runnable() {
        @Override

        public void run() {
            Message message=null;
            try {
                HttpClient client=new DefaultHttpClient();
                //新建“客戶”對象
                HttpPost httpPost=new HttpPost(url);
                //新建post請求
                List<NameValuePair> pairs=new ArrayList<NameValuePair>();
                pairs.add(new BasicNameValuePair("title", "hello"));
                UrlEncodedFormEntity entity=new UrlEncodedFormEntity(pairs,"utf-8");
                httpPost.setEntity(entity);
                //將要發送內容添加到list中,並提交給post對象;
                HttpResponse response=client.execute(httpPost);
                if(response.getStatusLine().getStatusCode()==200){
                //返回值狀態爲200代表成功
                    message.what=REQUEST_OK;
                    HttpEntity httpEntity=response.getEntity();
                    message.obj= EntityUtils.toString(httpEntity,"utf-8");
                    //取出訪問url返回的內容。
                }else{
                    message.what=REQUEST_ERROR;
                    message.obj="statusCode not equals 200";
                }
            } catch (IOException e) {
                message.what=REQUEST_ERROR;
                message.obj=e.toString();
            }finally {
                handler.sendMessage(message);
                //交給主線程處理
            }
        }
    }).start();
}
//此時方法只供參考,說明http請求的另一種模式。

2. 數據解析——xml文檔;

訪問某個url返回的數據,大都是html語言,例如WebView控件就是將http請求以及獲取的數據封裝,用瀏覽器的方式進行查看。
還有些返回的內容只提供數據,不能形成可視化網頁,例如xml和json。這些內容大都不要進行解析,從中提取想要的information。
2.1 解析xml文檔——pull
Pull解析器提供的事件類型總共有5種,分別如下:

  • (1)START_DOCUMENT 文檔開始
  • (2)START_TAG     開始節點
  • (3)TEXT        文本節點
  • (4)END_TAG     節點結束
  • (5)END_DOCUMENT 文檔結束

pull解析原理:從文檔頭開始,整個文檔由節點組成,有些節點中包含屬性和文本;

<qq mm="a" nn="b">text</qq>
//<qq ……>表示qq節點的開始
//<qq mm="a" nn="b">text</qq>就是節點。
//qq是節點名字
//mm和nn都是屬性名
//a和b是節點對應的值
//text是文本
//遇到</qq>代表節點qq結束

pull方式解析,就是新建一個xmlPullParser對象,然後從頭開始“推”,開始解析時,將事件設置爲START_DOCUMENT,當碰到節點開始標誌時,會將事件設置爲START_TAG,如此解析下去,直到文檔結束,然後將事件爲END_DOCUMENT 文檔結束;下面介紹解析時用到的方法:

(1)int getAttributeCount(); //獲取當前節點的屬性個數
(2)String getAttributeValue(int index); //獲取屬性值,參數表示第index個屬性。
(3)int getEventType();//獲取事件類型
(4)String getName();//用於START_TAG和END_TAG事件中,獲取當前節點的名字
(5)int next();//處理下一個節點
(6)String nextText();//用於START_TAG事件中,獲得下一個TEXT類型的節點(就是文本)

pull解析代碼

private String[] analyzeXML(String xml,String[] names){
    String[] values=new String[names.length];
    try {
        XmlPullParserFactory factory=XmlPullParserFactory.newInstance();
        XmlPullParser parser=factory.newPullParser();
        //利用“工廠”生產一個進行pull解析的對象。
        parser.setInput(new StringReader(xml));
        //設置進行pull解析的內容或輸入流
        int type=parser.getEventType();
        //獲取當前的事件類型。
        while(type!=XmlPullParser.END_DOCUMENT){
        //不斷的讀取下一個節點,直到文檔結束。
            String nodeName=parser.getName();
            switch (type){
                case XmlPullParser.START_TAG:{
                    for(int i=0;i<names.length;i++){
                        if(names[i].equals(nodeName)){
                            values[i]=parser.nextText();
                            //nodeName爲當前節點名
                            break;
                        }
                    }
                    break;
                }
                case XmlPullParser.END_TAG:{
                    break;
                }
                default:{
                    break;
                }
            }
            type=parser.next();
        }
    } catch (Exception e) {
    }
    return values;
    //返回獲取到的對應節點的文本
}

2.2 解析xml文檔——sax
sax解析原理:sax解析方式與pull有些類似,都是一邊讀取一邊解析,不過sax將解析以類的形式封裝起來,只需要重寫方法就可以了。
sax屬於事件驅動,每當遇到一定的標誌就執行特定的方法;pull像是”手動“驅動,需要代碼顯示控制讀取,判斷,取值,再讀取……
sax解析代碼:

SAXParserFactory factory=SAXParserFactory.newInstance();
XMLReader xmlReader=factory.newSAXParser().getXMLReader();
MyHandler handler = new MyHandler();
xmlReader.setContentHandler(handler);
// 將ContentHandler的實例設置到XMLReader
//(setContentHandler方法需要接收一個處理解析事件的類參數)
xmlReader.parse(new InputSource(new StringReader(xmlData)));
// 開始執行解析

myHandler類一般繼承DefaultHandler類,然後重寫其中的事件方法。
MyHandler代碼:

public class MyHandler extends DefaultHandler {
    @Override
    public void startDocument() throws SAXException {
        //開始解析文檔時調用,進行初始化的工作。
    }
    @Override
    public void startElement(String uri, String localName, String qName,Attributes attributes) throws SAXException {
        //開始某節點時候調用
        /*uri - 名稱空間 URI,如果節點沒有任何名稱空間 URI,或者沒有正在執行名稱空間處理,則爲空字符串。
         *localName - 本地名稱(不帶前綴),如果沒有正在執行名稱空間處理,則爲空字符串。
         *qName - 限定的名稱(帶有前綴),如果限定的名稱不可用,則爲空字符串。(節點名,一般讀取數據時比較qName是否是自己想要的內容名稱,然後提取數據)
         *attributes - 附加到節點的屬性。如果沒有屬性,則它將是空的 Attributes 對象。
         */
    }
    @Override
    public void characters(char[] ch, int start, int length) throws SAXException {
        //獲取文本時候調用,準確的說應該是在讀取某節點中內容時候調用。
        /*ch - 字符。
         *length - 從字符數組中使用的字符數。
         *start - 字符數組中的開始位置
         */     
    }
    @Override
    public void endElement(String uri, String localName, String qName) throws SAXException {
        //節點結束時調用
        //與startElement參數基本相同
    }
    @Override
    public void endDocument() throws SAXException {
        //文檔結束時調用
    }
}
//從解析方法中也可以看出,xml格式非常重要,不規範的書寫方式會導致解析失敗。

這樣看起來是不是發現sax除了比較”自動化“之外,跟pull解析沒有多大的區別。

2.3 解析xml文檔——DOM
js中,dom指的是文檔對象模型,也是操作頁面最基本最常用的方法。

DOM原理: pull與sax解析都是無”記憶”的,即對整個文檔而言,只能在觸發事件時進行內容的讀取,不可能依靠前兩種解析方式理清文檔結構。DOM則是先將文檔 ”通讀“,以節點建立”樹“的模型,在想要獲取內容時只要知道在”樹“中的位置,就可以隨時讀取,不過因爲要建立模型,因此會佔用較大的內存空間。

DocumentBuilderFactory factory=DocumentBuilderFactory.newInstance();
DocumentBuilder builder = factory.newDocumentBuilder();
Document doc = builder.parse(file); 
//其實解析xml文檔方法步驟都差不多,先構建出解析器,再傳入需要解析的內容,然後對內容進行分析。

此時獲得的doc包含整個文檔中的節點。直觀來看:
來自互聯網

(隨便找了個xml的dom樹)

//可以看到,dom“樹”的節點是由節點(非TEXT類型)和文本組成的(文本總是沒有子節點的子節點,屬性相當於節點的“附加"信息。我們獲取的內容就是指文本)

//系統不可能將”樹“形象的映射到內存裏,因此dom解析是通過棧來實現的,因爲xml文檔的規範性,通過查看棧的數據,就可以”立體化“dom樹。

//需要注意的是,dom解析類似於引用,如果改變生成的doc,那麼實際的文檔將會被改變。

//dom查找數據可以通過根節點不停的查找子節點,直至定位到想要的節點,然後獲取內容。

節點——Node:節點本來是數據結構中tree的組成部分,不過在xml文檔中,所有的內容都可以稱爲節點。在Dom中,節點分爲十二總,涵蓋了文檔中所有部分,我們上面說的”文本“,其實只是TEXTNODE類型的節點,不過爲了直觀,把兩者區分開來。
元素——Element:元素和節點很相似,事實上,看源碼的話可以發現Element繼承自Node,Element相當於Node的擴展,不過使用起來較爲方便一些。
Node與Element的區別

例:

private String xmlAnalize(String xml){
/*xml字符串內容:
 *<?version="1.0" encoding="utf-8"?>
 *<html title="title">
 *     <head>head</head>
 *     <!--reliase-->
 *     <body>body</body>
 *</html>
 */
    try {
        DocumentBuilder documentBuilder=DocumentBuilderFactory.newInstance().newDocumentBuilder();
        Document document=documentBuilder.parse(new ByteArrayInputStream(xml.getBytes("UTF-8")));
        NodeList childs=document.getChildNodes();
        Node node1;
        if(childs.getLength()>0){
            for(int i=0;i<childs.getLength();i++){
                node1=childs.item(i);
                Log.i("java","……………………"+node1.getNodeType()+"&"+node1.getNodeName()+"&"+node1.getNodeValue());
            }
        }
        /*控制檯輸出:
         *……………………7&version="1.0"&encoding="utf-8"
         * ……………………1&html&null
         */
        //由此可見document相當於整個文檔,getChildNodes方法可以獲得“根目錄”下所有子節點,此處7表示文檔編碼版本等信息。

        Element element=document.getDocumentElement();//xml規範,xml文檔除了開頭的編碼等說明外,其他內容必須包含在同一個根節點下。getDocumentElement()方法便是獲取該根目錄節點(此節點也是Element)。

        Log.i("java","……………………"+element.getNodeType()+"&"+element.getNodeName()+"&"+element.getNodeValue());
       /*控制檯輸出:
         * ……………………1&html&null
         */

        return getElements(element);
    } catch (Exception e) {
        Log.i("java",e.toString());
        return null;
    }
}
private String getElements(Node node){
//獲取html節點下所有子節點的類型,名字,和值。
    StringBuilder builder=new StringBuilder();
    NodeList nodeList=node.getChildNodes();
    Node node1;
    if(nodeList.getLength()>0){
        for(int i=0;i<nodeList.getLength();i++){
            node1=nodeList.item(i);
            builder.append(node1.getNodeType()+"&"+node1.getNodeName()+"&"+node1.getNodeValue()+"\n");
            Log.i("java",node1.getNodeType()+"&"+node1.getNodeName()+"&"+node1.getNodeValue());
            //獲取結點對應的類型,name,以及value
        }
    }//判斷屬性結點會不會在節點樹中。
    /*控制檯輸出:
     * 1&head&null
     * 8&#comment&reliase
     * 1&body&null
     */

    //可以看到,dom解析還可以獲取到註釋信息,而html節點下的屬性信息不能通過getChildNodes方法獲得。
    return builder.toString();
}

3. 數據解析——json

假如json數據以“組”的形式存在,如數組,listmap除外)。
private Vector<String[]> analyzeJSON(String jsonData,String[]names) {
    Vector<String[]> vector=new Vector<String[]>();
    try {
        JSONArray jsonArray = new JSONArray(jsonData);
        for (int i = 0; i < jsonArray.length(); i++) {
            JSONObject jsonObject = jsonArray.getJSONObject(i);
            String[]values=new String[names.length];
            for(int j=0;j<values.length;j++){
                values[j]=jsonObject.getString(names[j]);
            }
            vector.add(values);
        }
    } catch (Exception e) {

    }
    return vector;
}

jsonArray可以存儲“組”,jsonObject提取組中的”單“個信息。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章