編輯:開發入門
提供股票數據
應用程序服務器需要能夠做兩件事。第一,它必須獲取股票代碼列表並檢索它們的數據。然後,它需要接受一個格式參數並基於該格式編碼數據。對於 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
都有三個屬性— symbol
、name
和 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— 它也是非常冗長的。
開始之前本教程介紹了如何在 android 平台之上處理 XML。要按照本教程構建樣例應用程序,必須在開發計算機中安裝和運行 Android SDK。推薦使用 Ecli
下面是開始android編程的好方法: 找一些與你想做事情類似的代碼 調整它,嘗試讓它做你像做的事情 經歷問題 使用
使用 JSONXML 是 android 上的一等公民,鑒於依賴於 XML 的 Web 服務的數量,這是個好事。很多服務還支持另一個流行格式 JSON。它通常比 XML
環境監控系統在這個場景中,我們假設您是企業所在的若干辦公場所的資產管理員。管理資產與管理數據中心沒有太大的差別 — 一般情況下都很枯燥,只有出現緊急的情況下工作才會比較