应用程序服务器需要能够做两件事。第一,它必须获取股票代码列表并检索它们的数据。然后,它需要接受一个格式参数并基于该格式编码数据。对于 XML 和 JSON 格式而言,该服务器将返回作为文本的串行化的股票数据。对于 protocol buffers 而言,它必须发送二进制数据。
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()
getStocks()
Stock
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
Stock
XML 和 JSON 用例非常类似和直接。对于 protocol buffers,您必须生成 protocol buffers 格式的代码读写对象。为此,您需要使用 protocol buffers 规范格式定义数据结构。清单 5
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
)将
在 toProtoBuf()
Stock
Portfolio
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 Quote
Portfolio
Stock
Quote
,然后将其添加到 Portfolio
现在,您了解了服务器如何创建要发送到 Android 应用程序的数据。接下来将学习应用程序如何解析此数据。
清单 2 Activity
ListView
。因此,无论数据格式是什么,大部分功能都是通用的。
首先,创建一个抽象的基类,封装此通用功能,如
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 解析器开始。
Android SDK 提供了几种使用 XML 的方式,包括标准 DOM 和 SAX。 对于一些对内存密集型情况,可以使用 SDK 的 pull-parser。大部分时候,SAX 是最快的方式。Android 包括一些便捷的 API 使得使用 SAX 更轻松。清单 8
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").setEndTextElementListene |
清单 8 newHandler()
ContentHandler
。如果熟悉 SAX 解析, 会知道 ContentHandler
newHandler()
ContentHandler
。代码只是侦听在解析器遇到各种标记时触发的事件,然后选取数据,放到 Stock
ContentHandler
Xml.parse()
InputStream
Stock