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