BlackBerry 应用程序开发者指南 第一卷:基础--第6章 连接网络
作者:Confach 发表于2006-04-28 21:40 pm
版权信息:可以任意转载, 转载时请务必以超链接形式标明文章原始出处和作者信息.
http://www.cnblogs.com/confach/articles/387906.html
6
第6章 连接网络
HTTP和socket连接 使用HTTP连接 使用HTTPS连接 使用socket连接 使用端口(port)连接 使用蓝牙序列端口连接 |
HTTP和Socket连接
尽管你可以通过socket连接实现HTTP,但是最好使用HTTP连接,因为socket连接不支持BlackBerry MDS服务特性,例如push。也最好使用HTTP连接,因为比起那些使用HTTP连接的应用程序,使用socket连接的应用程序明显需要更多的带宽。
注:如果你使用socket连接,将你的应用程序设计为适应断断续续的无线网络连接。例如,如果你的程序发生错误时,它会重新打开连接。
使用HTTP连接
注:使用BlackBerry Internet Service Browser的java程序不会启动HTTP,HTTPS和TCP连接。
打开一个HTTP连接
为了打开一个HTTP连接,调用Connector.open(),指定http为协议。将返回的对象转化为一个HTTPConnection或者StreamConnection对象。HttpConnection是一个StreamConnection,它提供访问指定HTTP功能,包括HTTP头和其他HTTP资源。
HttpConnection conn = null; String URL = "http://www.myServer.com/myContent"; conn = (HttpConnection)Connector.open(URL); |
设置HTTP请求方式
为设置HTTP请求方式(GET或POST),调用HttpConnection.setRequestMethod().
conn.setRequestMethod(HttpConnection.POST); |
设置或获取头字段
为HTTP请求或HTTP响应消息设置或获取头字段,调用HttpConnection 上的getRequestProperty() 或setRequestProperty()。
conn.setRequestProperty("User-Agent", "BlackBerry/3.2.1"); String lang = conn.getRequestProperty("Content-Language"); |
发送和接受数据
为发送和接受数据,调用HTTPConnection的openInputStream()和openOutputStream()获得输入和输出流。
InputStream in = conn.openInputStream(); OutputStream out = conn.openOutputStream(); |
代码实例
HttpFetch.java 实例使用了一个HTTP 连接来获取数据。它遵循下列步骤:
-
创建一个连接线程。
-
定义一个方法获取数据。
-
定义一个方法将数据显示给用户。
-
定义一个方法退出应用程序。
-
定义应用程序构造子。
注:HTTPFetch.java实例需要你在应用程序工程里创建资源文件,并且定义需要的资源键值。参看125页“本地化应用程序”获得更多信息。
例:HTTPFetch.java
/**
* HTTPFetch.java
* Copyright (C) 2001-2005 Research In Motion Limited. All rights reserved.
*/
package com.rim.samples.docs.httpfetch;
import net.rim.device.api.ui.*;
import net.rim.device.api.ui.component.*;
import net.rim.device.api.ui.container.*;
import net.rim.device.api.i18n.*;
import net.rim.device.api.system.*;
import javax.microedition.io.*;
import java.io.*;
import com.rim.samples.docs.baseapp.*;
import com.rim.samples.docs.resource.*;
public class HTTPFetch extends BaseApp implements HTTPFetchResource
{
// Constants.
private static final String SAMPLE_PAGE = "http://localhost/testpage/sample.txt”;
private static final String[] HTTP_PROTOCOL = {"http://", “http:\\”};
private MainScreen _mainScreen;
private RichTextField _content;
/**
* Send and receive data over the network on a
* separate thread from the main thread of your application.
*/
ConnectionThread _connectionThread = new ConnectionThread();
//statics
private static ResourceBundle _resources = ResourceBundle.getBundle(
HTTPFetchResource.BUNDLE_ID, HTTPFetchResource.BUNDLE_NAME);
public static void main(String[] args)
{
HTTPFetch theApp = new HTTPFetch();
theApp.enterEventDispatcher();
}
/**
* The ConnectionThread class manages the HTTP connection.
* Fetch operations are not queued, but if a second fetch request
* is made while a previous request is still active,
* the second request stalls until the previous request completes.
*/
private class ConnectionThread extends Thread
{
private static final int TIMEOUT = 500; //ms
private String _theUrl;
/* The volatile keyword indicates that because the data is shared,
* the value of each variable must always be read and written from memory,
* instead of cached by the VM. This technique is equivalent to wrapping
* the shared data in a synchronized block, but produces less overhead.
*/
private volatile boolean _start = false;
private volatile boolean _stop = false;
/**
* Retrieve the URL. The synchronized keyword makes sure that only one
* thread at a time can call this method on a ConnectionThread object.
*/
public synchronized String getUrl()
{
return _theUrl;
}
/**
* Fetch a page. This method is invoked on the connection thread by
* fetchPage(), which is invoked in the application constructor when
* the user selects the Fetch menu item.
*/
public void fetch(String url)
{
_start = true;
_theUrl = url;
}
/**
* Close the thread. Invoked when the application exits.
*/
public void stop()
{
_stop = true;
}
/**
* Open an input stream and extract data. Invoked when the thread
* is started.
*/
public void run()
{
for(;;)
{
// Thread control.
while( !_start && !_stop)
{
// No connections are open for fetch requests,
// but the thread has not been stopped.
try
{
sleep(TIMEOUT);
}
catch (InterruptedException e)
{
System.err.println(e.toString());
}
}
// Exit condition.
if ( _stop )
{
return;
}
/* Ensure that fetch requests are not missed
* while received data is processed.
*/
synchronized(this)
{
// Open the connection and extract the data.
StreamConnection s = null;
try
{
s = (StreamConnection)Connector.open(getUrl());
InputStream input = s.openInputStream();
// Extract data in 256 byte chunks.
byte[] data = new byte[256];
int len = 0;
StringBuffer raw = new StringBuffer();
while ( -1 != (len = input.read(data)) )
{
raw.append(new String(data, 0, len));
}
String text = raw.toString();
updateContent(text);
input.close();
s.close();
}
catch (IOException e)
{
System.err.println(e.toString());
// Display the text on the screen.
updateContent(e.toString());
}
// Reset the start state.
_start = false;
}
}
}
}
// Constructor.
public HTTPFetch()
{
_mainScreen = new MainScreen();
_mainScreen.setTitle(new LabelField(
_resources.getString(APPLICATION_TITLE), LabelField.ELLIPSIS
| LabelField.USE_ALL_WIDTH));
_mainScreen.add(new SeparatorField());
_content = new RichTextField(
_resources.getString(HTTPDEMO_CONTENT_DEFAULT));
_mainScreen.add(_content);
_mainScreen.addKeyListener(this);
_mainScreen.addTrackwheelListener(this);
// Start the helper thread.
_connectionThread.start();
pushScreen(_mainScreen);
fetchPage(SAMPLE_PAGE);
}
// Retrieve web content.
private void fetchPage(String url)
{
// Perform basic validation (set characters to lowercase and add http:// or https://).
String lcase = url.toLowerCase();
boolean validHeader = false;
int i = 0;
for (i = HTTP_PROTOCOL.length - 1; i >= 0; --i)
{
if ( -1 != lcase.indexOf(HTTP_PROTOCOL[i]) )
{
validHeader = true;
break;
}
}
if ( !validHeader )
{
// Prepend the protocol specifier if it is missing.
url = HTTP_PROTOCOL[0] + url;
}
// Create a new thread for connection operations.
_connectionThread.fetch(url);
}
// Display the content.
private void updateContent(final String text)
{
/* This technique creates several short-lived objects but avoids
* the threading issues involved in creating a static Runnable and
* setting the text.
*/
UiApplication.getUiApplication().invokeLater(new Runnable()
{
public void run()
{
_content.setText(text);
}
});
}
// Close the connection thread when the user closes the application.
protected void onExit()
{
_connectionThread.stop();
}
}
使用HTTPS连接
注:BlackBerry Internet Service Browser不允许Java应用程序启动HTTP,HTTPS和TCP连接。
打开一个HTTPS连接
为打开一个HTTPS连接,调用Connector.open(),指定https作为协议。将返回的对象转化为一个HttpsConnection;
HttpsConnection stream = (HttpsConnection)Connector.open("https://host:443/"); |
指定代理或终端到终端(end_to_end)模型
缺省的,连接在代理模型中使用HTTPS。用户也可以设置一个BlackBerry设备选项来使用缺省的end_to_end模型。为获得更多信息,参看189页的“HTTPS支持“。
为了在end_to_end模型里打开一个HTTPS连接,将下列参数中的一个增加到传递给Connector.open()的连接字符串中:
参数 |
描述 |
EndToEndRequired |
这个参数指定了一个end_to_end HTTPS连接必须从BlackBerry设备到目标服务器中使用。如果一个end_to_end的HTTPS连接不能建立,这个连接将会关闭。 |
EndToEndDesired |
这个参数指定一个end_to_end HTTPS连接应该从BlackBerry设备到目标服务器中被使用,如果BlackBerry设备支持它的话。如果BlackBerry设备不支持end_to_end TLS,并且用户许可代理TLS连接,那么一个代理连接将被使用。 |
HttpsConnection stream = (HttpsConnection)Connector.open("https://host:443/;EndToEndDesired"); |
注:BlackBerry设备缺省没有安装end_to_end模块。尽管这样,BlackBerry 桌面版软件3.6.0及后续版本中包含了它。当应用程序加载到BlackBerry设备时,为了加载模块,为你的应用程序在.alx文件中加入下面的标记:
<requires id="net.rim.blackberry.crypto1"/> <requires id="net.rim.blackberry.crypto2"/> |
为获得更多信息,参看183页的”.alx文件”。
使用socket连接
注:BlackBerry Internet Service Browser不允许Java应用程序启动HTTP,HTTPS和TCP连接。
指定TCP的设置
应用程序可以在下面的一种模式下建立一个TCP socket连接或一个在TCP上的HTTP连接。
模式 |
描述 |
代理模式 |
BES的MDS服务特征为BlackBerry设备建立与Web 服务器的TCP连接 |
直接模式 |
BlackBerry设备建立一个直接与Web服务器的TCP连接。 |
注:使用直接TCP模式需要你和服务提供商一起紧密工作。联系你的服务提供商确保支持直接TCP socket连接。
为了通过编程指定TCP设置,增加可选的deviceside参数到传递给Connector.open()的连接字符串。
在GSM网络里,为指定BlackBerry设备商的TCP设置,用户点击BlackBerry设备选项的TCP。
注:如果IT策略设置允许TCP连接,TCP才在BlackBerry设备选项里显示。
如果TCP设置没有指定,下将使用下面缺省的。
网络 |
缺省的TCP设置 |
可选的TCP设置 |
GSM |
代理模式 |
直接模式 |
iDEN |
直接模式 |
代理模式 |
参看API参考的Connector获得更多信息。
打开一个socket连接
为打开一个socket连接,调用Connector.open(),指定socket为其协议。
注:应用程序必须显式的输入他们的本地机器IP,因为localhost不被支持。
private static String URL = "socket://<local machine IP>:4444"; StreamConnection conn = null; conn = (StreamConnection)Connector.open(URL); |
在socket连接上发送和接收数据
使用openInputStream() 和openOutputStream()获得输入和输出流。
OutputStreamWriter _out = new OutputStreamWriter(conn.openOutputStream()); String data = "This is a test"; int length = data.length(); _out.write(data, 0, length); InputStreamReader _in = new InputStreamReader(conn.openInputStream()); char[] input = new char[length]; for ( int i = 0; i < length; ++i ) { input[i] = (char)_in.read(); } |
关闭连接
调用输入和输出流,以及socket连接上的close()方法。
注:每个close()方法抛出一个IOException。应用程序必须实现这个异常的处理。
_in.close(); _out.close(); conn.close(); |
使用端口连接
注:当你的应用程序首先访问net.rim.device.api.system.SerialPort类或net.rim.device.api.system.USBPort类时,检查NoClassDefFoundError。如果系统管理员使用应用程序管理限制访问序列端口和USB接口,这个错误就会抛出。参看16页的“应用程序管理”获得更多信息。
当它们使用一个序列端口或USB接口连上一台计算机式,应用程序可以使用一个序列端口或USB接口和桌面的应用程序进行通信。连接类型也可以使用来和一个插到序列端口或USB接口的外围设备通信。
注:如果你正在使用端口连接和桌面应用程序通信,你不必让所有其他正在使用序列端口或USB接口的应用程序运行。
打开一个USB接口或序列端口连接
调用Connector.open(),指定comm作为协议,COM1或USB作为端口。
private StreamConnection _conn = (StreamConnection)Connector.open( "comm:COM1;baudrate=9600;bitsperchar=8;parity=none;stopbits=1"); |
在端口连接上发送数据
调用openDataOnputStream() 和openOutputStream()获得输出流。
DataOutputStream _dout = _conn.openDataOutputStream(); |
使用输出流上的写方法来写数据。
private String data = "This is a test"; _dout.writeChars(test); |
在端口连接上接收数据
调用openDataInputStream() 和openIutputStream()获得输入流。
DataInputStream _din = _conn.openInputStream(); |
使用输入流上的读方法来写数据。
String contents = _din.readUTF(); |
注:你不能从在主事件线程上的输入流读取,因为这个操作会阻塞直至数据接收完成。创建一个独立的线程,在此线程上接收数据。
关闭端口连接
调用输入和输出流,以及端口连接上的close()方法。
注:每个close()方法可能抛出一个IOException异常。应用程序必须实现异常的处理。
_din.close(); _dout.close(); conn.close(); |
使用蓝牙序列端口连接
蓝牙API(net.rim.device.api.bluetooth)允许应用程序访问蓝牙序列端口配置(Profile)以及允许启动一个服务器或者客户端蓝牙序列端口连接到一台计算机或其他蓝牙无线技术支持的设备。
注:当你的应用程序首先访问蓝牙API时,会检查NoClassDefFoundError。如果系统管理员使用应用程序管理限制访问序列端口和USB接口,这个错误就会抛出。参看16页的“应用程序管理”获得更多信息。
BlackBerry模拟器不支持蓝牙。
打开一个蓝牙序列端口连接
为了打开一个蓝牙序列端口连接,调用Connector.open(),它提供由BluetoothSerialPort.getSerialPortInfo()返回的序列端口信息作为参数。
由这个方法返回的连接字符串指定了作为协议的btspp:// 以及下面条目之一:
- 如果你正在打开一个连接作为客户端,由getSerialPortInfo().toString()返回的连接字符串包含了设备号(device ID)以及Server设备正在监听的端口。
- 如果你正在打开一个连接作为服务器,由getSerialPortInfo().toString()返回的连接字符串包含了你的BlackBerry设备正在监听的端口。
BluetoothSerialPortInfo[] info = BluetoothSerialPort.getSerialPortInfo(); StreamConnection _conn = (StreamConnection)Connector.open( info.toString(), Connector.READ_WRITE ); |
在蓝牙序列端口连接上发送数据
调用openDataOutputStream()或openOutputStream()获得一个输出流。
注:直到连接建立,这个调用会阻塞。
DataOutputStream _dout = _conn.openDataOutputStream(); |
在输出流上使用写方法来写数据
private String data = "This is a test"; _dout.writeChars(test); |
在蓝牙序列端口连接上接收数据
调用openDataInputStream()或openInputStream()获得一个输入流。
DataInputStream _din = _conn.openInputStream(); |
在输入流上使用读方法来读数据
String contents = _din.readUTF(); |
注:你不能在主事件线程上读取输入流数据,因为这个操作会阻塞直到数据接收完毕。创建一个独立的线程来接收数据。
关闭一个端口连接
在输入和输出流以及蓝牙序列端口连接上调用close()方法。
if (_bluetoothConnection != null) { try { _bluetoothConnection.close(); } catch(IOException ioe) { } } if (_din != null) { try { _din.close(); } catch(IOException ioe) { } } if (_dout != null) { try { _dout.close(); } catch(IOException ioe) { } } _bluetoothConnection = null; _din = null; _dout = null; |
代码实例
BluetoothSerialPortDemo.java实例使一个简单蓝牙序列端口应用程序的客户端。这个应用程序监听在序列端口上的数据,并且当数据到达时提交数据。
例:BluetoothSerialPortDemo.java
/**
* BluetoothSerialPortDemo.java
* Copyright (C) 2004-2005 Research In Motion Limited.
*/
/* The client side of a simple serial port demonstration application.
* This application listens for text on the serial port and
* renders the data when it arrives.
*/
package com.rim.samples.docs.bluetoothserialportdemo;
import java.io.*;
import javax.microedition.io.*;
import net.rim.device.api.bluetooth.*;
import net.rim.device.api.ui.*;
import net.rim.device.api.ui.component.*;
import net.rim.device.api.ui.container.*;
import net.rim.device.api.i18n.*;
import net.rim.device.api.system.*;
import net.rim.device.api.util.*;
import com.rim.samples.docs.baseapp.*;
import com.rim.samples.docs.resource.*;
public class BluetoothSerialPortDemo extends BaseApp implements BluetoothSerialPortDemoResResource {
//statics ------------------------------------------------------------------
private static ResourceBundle _resources;
private static final int INSERT = 1;
private static final int REMOVE = 2;
private static final int CHANGE = 3;
private static final int JUST_OPEN = 4;
private static final int CONTENTS = 5;
private static final int NO_CONTENTS = 6;
static {
_resources = ResourceBundle.getBundle(BluetoothSerialPortDemoResResource.BUNDLE_ID,
BluetoothSerialPortDemoResResource.BUNDLE_NAME);
}
private EditField _infoField;
private StreamConnection _bluetoothConnection;
private DataInputStream _din;
rivate DataOutputStream _dout;
public static void main(String[] args)
{
BluetoothSerialPortDemo theApp = new BluetoothSerialPortDemo();
theApp.enterEventDispatcher();
}
//constructor --------------------------------------------------------------
public BluetoothSerialPortDemo()
{
MainScreen mainScreen = new MainScreen();
mainScreen.setTitle(new LabelField(_resources.getString(TITLE),
LabelField.USE_ALL_WIDTH));
_infoField = new EditField(Field.READONLY);
mainScreen.add(_infoField);
mainScreen.addKeyListener(this);
mainScreen.addTrackwheelListener(this);
pushScreen(mainScreen);
invokeLater(new Runnable()
{
public void run() {
openPort();
}
});
}
protected void onExit() {
closePort();
}
// Close the serial port.
private void closePort() {
if (_bluetoothConnection != null)
{
try {
_bluetoothConnection.close();
}
catch(IOException ioe) {
}
if (_din != null)
{
try {
_din.close();
}
catch(IOException ioe) {
}
}
if (_dout != null) {
try {
_dout.close();
}
catch(IOException ioe)
{
}
}
_bluetoothConnection = null;
_din = null;
_dout = null;
}
}
// Open the serial port.
private void openPort() {
if (_bluetoothConnection != null) {
closePort();
}
new InputThread().start();
}
private class InputThread extends Thread {
public void run() {
try {
BluetoothSerialPortInfo[] info =
BluetoothSerialPort.getSerialPortInfo();
if( info == null || info.length == 0 ) {
invokeAndWait( new Runnable() {
public void run() {
Dialog.alert( "No bluetooth serial ports available for connection.");
onExit();
System.exit(1);
}
});
_bluetoothConnection = (StreamConnection)Connector.open( info[0].toString(),
Connector.READ_WRITE);
_din = _bluetoothConnection.openDataInputStream();
_dout = _bluetoothConnection.openDataOutputStream();
}
catch(IOException e) {
invokeAndWait( new Runnable() {
public void run() {
Dialog.alert(“Unable to open serial port”);
onExit();
System.exit(1);
}
});
} catch( UnsupportedOperationException e ) {
invokeAndWait( new Runnable() {
public void run() {
Dialog.alert(“This handheld or simulator does not support bluetooth.”);
onExit();
System.exit(1);
}
});
}
try {
int type, offset, count;
String value;
_dout.writeInt(JUST_OPEN);
_dout.flush();
for (;;) {
type = _din.readInt();
if (type == INSERT) {
offset = _din.readInt();
value = _din.readUTF();
insert(value, offset);
}
else if (type == REMOVE) {
offset = _din.readInt();
count = _din.readInt();
remove(offset, count);
}
else if (type == JUST_OPEN) {
// Send contents to desktop.
value = _infoField.getText();
if (value == null || value.equals(““)) {
_dout.writeInt(NO_CONTENTS);
_dout.flush();
}
else {
_dout.writeInt(CONTENTS);
_dout.writeUTF(_infoField.getText());
_dout.flush();
}
}
else if (type == CONTENTS) {
String contents = _din.readUTF();
synchronized(Application.getEventLock()) {
_infoField.setText(contents);
}
}
else if (type == NO_CONTENTS) {
}
else {
throw new RuntimeException();
}
}
}
catch(IOException ioe) {
invokeLater(new Runnable() {
public void run() {
Dialog.alert(“Problems reading from or writing to serial port.”);
onExit();
System.exit(1);
}
});
}
}
}
private void insert(final String msg, final int offset) {
invokeLater(new Runnable() {
public void run() {
_infoField.setCursorPosition(offset);
_infoField.insert(msg);
}
});
}
private void remove(final int offset, final int count) {
invokeLater(new Runnable() {
public void run() {
_infoField.setCursorPosition(offset+count);
_infoField.backspace(count);
}
});
}
/**
* Override makeMenu to add custom menu items.
*/
protected void makeMenu(Menu menu, int instance)
{
if (_infoField.getTextLength() > 0) {
menu.add(new MenuItem(_resources, MENUITEM_COPY, 100000, 10) {
public void run() {
Clipboard.getClipboard().put(_infoField.getText());
}
});
}
super.makeMenu(menu, instance);
}
}
- Last Updated:2007年1月10日
- Last Updated:2006年04月28日 created