Android教程網
  1. 首頁
  2. Android 技術
  3. Android 手機
  4. Android 系統教程
  5. Android 游戲
 Android教程網 >> Android技術 >> Android開發 >> 開發入門 >> 在 Android 應用程序中使用 Internet 數據(二)

在 Android 應用程序中使用 Internet 數據(二)

編輯:開發入門

提供股票數據

應用程序服務器需要能夠做兩件事。第一,它必須獲取股票代碼列表並檢索它們的數據。然後,它需要接受一個格式參數並基於該格式編碼數據。對於 XML 和 JSON 格式而言,該服務器將返回作為文本的串行化的股票數據。對於 protocol buffers 而言,它必須發送二進制數據。 清單 3 顯示了處理這些步驟的 servlet:


清單 3. Stock Broker servlet

				
public class StockBrokerServlet extends HttpServlet {
    public void doGet(HttpServletRequest request, 
          HttpServletResponse response) throws IOException {
        String[] symbols = request.getParameterValues("stock");
        List<Stock> stocks = getStocks(symbols);
        String format = request.getParameter("format");
        String data = "";
        if (format == null || format.equalsIgnoreCase("xml")){
            data = Stock.toXml(stocks);
            response.setContentType("text/XML");    
        } else if (format.equalsIgnoreCase("json")){
            data = Stock.toJson(stocks);
            response.setContentType("application/JSon");
        } else if (format.equalsIgnoreCase("protobuf")){
            Portfolio p = Stock.toProtoBuf(stocks);
            response.setContentType("application/octet-stream");
            response.setContentLength(p.getSerializedSize());
            p.writeTo(response.getOutputStream());
            response.flushBuffer();
            return;
        }
        response.setContentLength(data.length());
        response.getWriter().print(data);
        response.flushBuffer();
        response.getWriter().close();
    }

    public List<Stock> getStocks(String... symbols) throws IOException{
        StringBuilder sb = new StringBuilder();
        for (String symbol : symbols){
            sb.append(symbol);
            sb.append('+');
        }
        sb.deleteCharAt(sb.length() - 1);
        String urlStr = 
                "http://finance.yahoo.com/d/quotes.csv?f=sb2n&s=" + 
                    sb.toString();
        URL url = new URL(urlStr);
        HttpURLConnection conn = 
                (HttpURLConnection) url.openConnection();
        BufferedReader reader = new BufferedReader(
                new InputStreamReader(conn.getInputStream()));
        String quote = reader.readLine();
        List<Stock> stocks = new ArrayList<Stock>(symbols.length);
        while (quote != null){
            String[] values = quote.split(",");
            Stock s = 
                      new Stock(values[0], values[2],
                           Double.parseDouble(values[1]));
            stocks.add(s);
            quote = reader.readLine();
        }
        return stocks;
    }
}

 

這是一個簡單的 Java servlet,只支持 HTTP GET 請求。它讀入股票的值和格式請求參數。然後調用 getStocks() 方法。該方法調用 Yahoo! Finance 獲取股票數據。Yahoo! 只支持 CSV 格式的數據,因此 getStocks() 方法將其解析到一個 Stock 對象列表。清單 4展示了這個簡單的數據結構:


清單 4. 股票數據結構

				
public class Stock {    
    private final String symbol;
    private final String name;
    private final double price;
      //getters and setters omitted

    public String toXml(){
        return "<stock><symbol>" + symbol + 
"</symbol><name><![CDATA[" +
            name + "]]></name><price>" + price + 
"</price></stock>";
    }

    public String toJSon(){
        return "{ 'stock' : { 'symbol' : " +symbol +", 'name':" + name + 
            ", 'price': '" + price + "'}}";
    }

    public static String toXml(List<Stock> stocks){
        StringBuilder xml = new StringBuilder("<stocks>");
        for (Stock s : stocks){
            xml.append(s.toXml());
        }
        xml.append("</stocks>");
        return XML.toString();
    }

    public static String toJson(List<Stock> stocks){
        StringBuilder json = new StringBuilder("{'stocks' : [");
        for (Stock s : stocks){
            json.append(s.toJson());
            json.append(',');
        }
        json.deleteCharAt(json.length() - 1);
        json.append("]}");
        return JSon.toString();
    }
}

 

每個 Stock 都有三個屬性— symbolname 和 price — 和幾個便捷的方法,以便將其自己轉換成 XML 字符串或 JSON 字符串。它提供了一個工具方法,用於將 Stock 對象列表轉換成 XML 或 JSON。回到 清單 3,根據格式請求參數,Stock 對象列表被轉換成 XML 或 JSON 字符串並被發送回客戶端。

XML 和 JSON 用例非常類似和直接。對於 protocol buffers,您必須生成 protocol buffers 格式的代碼讀寫對象。為此,您需要使用 protocol buffers 規范格式定義數據結構。清單 5 展示了一個示例:


清單 5. 股票的 Protocol buffers 消息

				
package stocks;

option Java_package = "org.developerworks.stocks";

message Quote{
    required string symbol = 1;
    required string name = 2;
    required double price = 3;
}

message Portfolio{
    repeated Quote quote = 1;
}

 

protocol buffers 消息格式類似於接口描述語言 (IDL),它與語言無關,因此可以將其與各種語言一起使用。在本例中,運行 protocol buffers 編譯器(protoc)將 清單 5 中的代碼編譯成要用於客戶端和服務器的 Java 類。有關將 protocol buffers 消息編譯成 Java 類的詳細信息,請參閱 Protocol Buffers Developer Guide(參見 參考資料)。

在 清單 3 中,一個名為 toProtoBuf() 的方法將 Stock 對象列表轉換成一個 Portfolio 消息。清單 6 展示了該方法的實現:


清單 6. 創建組合消息

				
public static Stocks.Portfolio toProtoBuf(List<Stock> stocks){
    List<Stocks.Quote> quotes = new ArrayList<Stocks.Quote>(stocks.size());
    for (Stock s : stocks){
        Quote q = 
            Quote.newBuilder()
                .setName(s.name)
                .setSymbol(s.symbol)
                .setPrice(s.price)
                .build();
        quotes.add(q);
    }
    return Portfolio.newBuilder().addAllQuote(quotes).build();
}

 

清單 6 中的代碼使用了從 清單 5 中的消息生成的代碼 — Quote和 Portfolio 類。只需構建來自每個 Stock 對象的 Quote,然後將其添加到 清單 3 中返回到 servlet 的 Portfolio 對象即可。在清單 3 中,servlet 直接打開到客戶端的流並使用生成的代碼編寫到流的二進制協議 buffers 數據。

現在,您了解了服務器如何創建要發送到 android 應用程序的數據。接下來將學習應用程序如何解析此數據。

使用數據格式

清單 2 中的主 Activity 需要使用服務器可以發送的各種格式的數據。它還需要請求適當格式的數據並且數據一旦解析,就用它來填充其 ListVIEw。因此,無論數據格式是什麼,大部分功能都是通用的。

首先,創建一個抽象的基類,封裝此通用功能,如 清單 7 所示:


清單 7. 數據解析器基類

				
abstract class BaseStockParser extends AsyncTask<String, Integer, Stock[]>{
    String urlStr = "http://protostocks.aPPSpot.com/stockbroker?format=";

    protected BaseStockParser(String format){
        urlStr += format;
    }

    private String makeUrlString(String... symbols) {
        StringBuilder sb = new StringBuilder(urlStr);
        for (int i=0;i<symbols.length;i++){
            sb.append("&stock=");
            sb.append(symbols[i]);
        }
        return sb.toString();
    }

    protected InputStream getData(String[] symbols) throws Exception{
        HttpClient client = new DefaultHttpClient();
        HttpGet request = new HttpGet(new URI(makeUrlString(symbols)));

        HttpResponse response = clIEnt.execute(request);
        return response.getEntity().getContent();
    }

    @Override
    protected void onPostExecute(Stock[] stocks){
        ArrayAdapter<Stock> adapter = 
            new ArrayAdapter<Stock>(Main.this, R.layout.stock, 
                      stocks );
        setListAdapter(adapter);
    }
}

 

清單 7 中的基類擴展了 android.os.AsyncTask。這是一個常用的用於異步操作的類。它抽象出線程和處理程序的創建,用於請求主 UI 線程。它是基於其輸入和輸出數據類型參數化的。對於所有解析器而言,輸入總是一樣的:股票代碼字符串。 輸出也是一樣的:Stock對象數組。基類獲取 format,這是一個指定了要使用的數據格式的字符串。然後提供一個方法,發出適當的 HTTP 請求並返回一個流響應。最後,它覆蓋 AsyncTask 的 onPostExecute() 方法並使用從解析器返回的數據為 Activity 的 ListVIEw 創建一個 Adapter

現在看到三個解析器的功能是通用的。我將為您展示更具體的解析代碼,從 XML 解析器開始。

用 SAX 解析 XML

android SDK 提供了幾種使用 XML 的方式,包括標准 DOM 和 SAX。 對於一些對內存密集型情況,可以使用 SDK 的 pull-parser。大部分時候,SAX 是最快的方式。android 包括一些便捷的 API 使得使用 SAX 更輕松。清單 8 顯示了 Day Trader 應用程序的 XML 解析器:


清單 8. XML 解析器實現

				
private class StockXmlParser extends BaseStockParser{
    public StockXmlParser(){
        super("xml");
    }

    @Override
    protected Stock[] doInBackground(String... symbols) {
        ArrayList<Stock> stocks = new ArrayList<Stock>(symbols.length);
        try{
            ContentHandler handler = newHandler(stocks);
            Xml.parse(getData(symbols), Xml.Encoding.UTF_8, handler);
        } catch (Exception e){
            Log.e("DayTrader", "Exception getting XML data", e);
        }
        Stock[] array = new Stock[symbols.length];
        return stocks.toArray(array);
    }

    private ContentHandler newHandler(final ArrayList<Stock> stocks){
        RootElement root = new RootElement("stocks");
        Element stock = root.getChild("stock");
        final Stock currentStock = new Stock();
        stock.setEndElementListener(
            new EndElementListener(){
                public void end() {
                    stocks.add((Stock) currentStock.clone());
                }
            }
        );
        stock.getChild("name").setEndTextElementListener(
            new EndTextElementListener(){
                public void end(String body) {
                    currentStock.setName(body);
                }
            }
        );
        stock.getChild("symbol").setEndTextElementListener(
            new EndTextElementListener(){
                public void end(String body) {
                    currentStock.setSymbol(body);
                }
            }
        );
        stock.getChild("price").setEndTextElementListener(
            new EndTextElementListener(){
                public void end(String body) {
                            currentStock.setPrice(Double.parseDouble(body));
                }
            }
        );
        return root.getContentHandler();
    }
}

 

清單 8 中的大部分代碼都在 newHandler() 方法中,該方法創建一個 ContentHandler。如果熟悉 SAX 解析, 會知道 ContentHandler通過響應 SAX 解析器觸發的各種事件創建解析數據。newHandler()方法使用 android 便捷 API 指定使用事件處理程序的ContentHandler。代碼只是偵聽在解析器遇到各種標記時觸發的事件,然後選取數據,放到 Stock 對象列表中。 創建ContentHandler 後,調用 XML.parse() 方法來解析基類提供的InputStream 並返回 Stock 對象數組。這是快速解析 XML 的方法,但是 —即使使用 android 提供的便捷 API— 它也是非常冗長的。

  1. 上一頁:
  2. 下一頁:
熱門文章
閱讀排行版
Copyright © Android教程網 All Rights Reserved