本文主要介绍了OPhone应用程序如何实现网络连接的管理。附件提供一段用于开发参考和示例的代码库,封装了常用的网络请求和数据接受方法,使得普通网络应用程序的开发大大单化,便于网络程序开发者可以专注于应用逻辑。附件同时提供了一段可以运行的应用程序作为网络开发的示例代码。
本文所讲述的网络开发方法通用于目前已经发布的两个版本的OPhone手机,即OPhone 1.0和OPhone 1.5。
OPhone网络连接的接入点管理
OPhone 平台实现了多个应用程序对分组数据复用的功能,不同的应用可以根据自身需要,打开不同的网络连接,通过这种方式我们可以在不同的OPhone应用中同时进行诸如浏览网页、接受彩信、发送邮件这样的网络任务。可以通过OPhone手机的操作来配置网络接入点。从首屏的图标上选择“配置”“数据连接”来进行配置,配置作为系统信息被保存,各个应用程序可以查询到这些接入点的配置详情,并选择使用一个接入点开始网络连接。
在OPhone 1.0和OPhone 1.5下这两个配置有一些不同。
OPhone 1.0的配置窗口如下。分别是网络接入点的选择窗口,某个网络接入点的详细信息编辑。
图1: OPhone 1.0接入点列表 图2: OPhone 1.0接入点编辑
OPhone 1.5的配置窗口如下。他比OPhone 1.0多了第一层次的接入点类型列表,每个类型下可以配置多个接入点,某个类型列表下的接入点可以视为一组同样类型的接入点。这也决定了OPhone 1.5在网络接入点的管理和连接使用上有一些不同。具体见下面的进一步说明。
图3: OPhone 1.5 接入点分组 图4: OPhone 1.5接入点列表 图5: OPhone 1.5接入点配置
OPhone网络连接的过程
下图描述了OPhone上进行一次网络数据请求的完整处理流程。
过程分为网络连接部分和数据请求接收部分。数据请求部分可以使用Java标准的网络请求,也可以使用系统提供的Apache HttpClient接口,这些与通用的Java网络开发并没有太大差异。详细可以参考OPhone SDN上王卫同学的一篇文章《如何在OPhone平台编写网络应用》。
网络连接部分用于建立设备上的网络数据通道,比如GPRS环境下就是建立分组数据(PDP)连接。他是区别于普通Java网络开发的一个最大差别,即在PC运行的模拟器上可以没有“网络连接部分”,但是如果要想让你的应用程序在实际的OPhone手机上进行网络数据的请求,则必须了解该步相关知识。
在一个移动终端设备上可能存在多个数据通道,比如可能同时有GPRS,WIFI,USB虚拟网络接口,蓝牙虚拟网络接口等多个数据通道,因此手机上可能有多个接入点可供网络应用程序选择。应用程序可以查询到这些接入点的配置,并且选择一个进行网络连接。
OPhone系统上,在应用程序运行的不同时间,可以连接/使用多个不同的接入点,但是在进行网络连接的某一时刻,只允许激活一个网络连接的数据通道。如果要切换到使用其他网络接入点,必须重新发起一个新的网络连接。
必须注意的是,OPhone 1.0和OPhone 1.5上发起网络连接的方法并不相同。这也一定程度上引起程序移植的问题,增加了网络应用开发的复杂性和工作量。
OPhone 1.5使用android.net.ConnectivityManager类中的startUsingNetworkFeature()方法来激活网络连接,网络状态的通知通过一个Intent消息EVENT_DATA_STATE_CHANGED来传递和接受。OPhone 1.0使用android.net.DataConnection类中的openConnection()方法来激活网络连接,通过setNetworkStatusListener()方法来设置网络状态的接收接口。
OPhone 1.5比OPhone 1.0的接入点配置上增加了接入点类型(APN type)的概念,同一种类型的接入点被归类在一起成为一个接入点的分组。OPhone 1.5发起网络连接时所传递的参数是这个接入点类型,连接过程中会尝试连接该类型接入点分组下所有的接入点,直到网络连接成功。如“图6: OPhone 1.5接入点列表”所示的网络配置,连接时如果第一个APN连接失败,在连接操作结束前会尝试使用第二个APN。而OPhone 1.0所传递的连接参数是接入点ID,一个ID仅仅对应一个接入点配置项。
目前在中国移动的GSM网络下常见的网络接入点有为GPRS的不同接入点cmwap, cmnet, cmmail等而设置的不同接入点配置项。一般情况下各个接入点都可能成功发起网络连接,但是根据移动网络的运营情况,不同的数据业务在不同的接入点上可能有计费上的差异。
当然还有一些其他的细微差别。比如最常见的cmwap, cmnet两个接入点,除了计费差异外,使用cmwap接入点的连接,如果需要让手机连接到互联网上的服务器,必须使用HTTP代理,也就是使用cmwap接入点后,应用程序将只能发起标准的HTTP请求。中国移动全网配置的代理服务器地址是10.0.0.172,端口80。另外,如果使用中国移动的cmwap接入点,你必须面对这样一个问题,一般情况下,当网络连接第一次建立后,HTTP请求发出后第一个内容并不是真实的你需要的内容,而是代替以一个WML编码的计费提醒页面。解决办法是您可能需要分析一下返回的内容,如果不正确,那么你需要重新发起一次网络请求来获取真实的网络数据(如果你开发的是一个浏览器,你也可以直接显示收到的WML页面,让用户收到计费提醒,点击页面中的链接可以跳过计费提醒)。
OPhone应用程序的网络开发
针对以上描述的OPhone 1.5,OPhone 1.0的各自特点和差异,这里尝试使用一个共同的接口封装来简化网络应用在不同版本间的移植过程。并且考虑最简单化这个接口的设计,使的网络调用变得更方便。
1. 首先拷贝需要的两个帮助库文件到开发中的测试下面下:
OPhone 1.5下是:dataconnection_helper_v15.jar和dataconnection_helper_httpget.jar;
OPhone 1.0下是dataconnection_helper_v10.jar和dataconnection_helper_httpget.jar。
2. 配置OPhone项目,添加这两个库到开发中的Eclipse项目下。如下图所示:
首先,选择左侧 “Java build Path”,然后选择右侧Tab页的“Libraries”,然后点击按钮“Add External JARs...”。选择拷贝过来的jar文件,然后确认是否添加成功。
继续配置页,选择上面的“Order and Export”,选中新添加的两个Jar文件,允许项目编译时同时导出这两个jar库的内容。
3. 在项目代码中使用Helper库提供的代码库,用于示例的DataConnectionDemo程序如下:
// 下面是对应OPhone 1.5引用的库名称, // 对应于OPhone 1.0请使用oms.dataconnection.helper.v10.* import oms.dataconnection.helper.v15.*; import oms.dataconnection.helper.httpget.DataHttp; public class DataConnectionDemo extends Activity { public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); // 初始化接入点查询帮助类的对象 mQueryApList = new QueryApList(this); // 获取各个接入点的标题 String[] apListTitle = mQueryApList.getApTitle(); // 生成网络连接帮助的对象 mConnectHelper = new ConnectByAp(this); // 获取指定序号位置接入点的ID final int apId = mQueryApList.getApId(position); // 创建网络连接的线程 Runnable runnable = new Runnable() { public void run() { // 获取接入点配置的代理服务器信息 String proxyHost = mQueryApList.getApProxy(position); int proxyPort = mQueryApList.getApProxyPort(position); // 连接并设置当前使用的网络介入点 // 函数一直阻滞,直到网络连接结束,返回值表示连接的结果是成功还是失败 boolean result = mConnectHelper.connect(apId, 10000); if (!result) return; // 至此,网络连接成功,下面可以开始Java通用的网络数据的请求和接收 // 但这里使用了一个自己封装过的HTTP数据请求类DataHttp来简化数据接收 // DataHttp会主动请求gzip或deflate压缩过的HTTP数据,并且自动解压接收到的压缩数据, // 并且尝试支持从HTTP HEADER中读取内容的字符编码信息。 // 首先,生成网络请求 DataHttp dataHttp = new DataHttp(requestURL , "*/*", "OPhone 1.5 ", proxyHost, proxyPort) { // 自定义新数据接受到的网络通知,用于数据传输状态的获取 @Override protected void onDataReceived(int len_read, int size, int total) { // show data receiving status printLog(title, String.format("Received data: %d(%d/%d)" , len_read, size, total)); super.onDataReceived(len_read, size, total); } }; // 然后,发送请求网络,并接收返回数据 printLog(title, String.format("Start getting data through: %s, %s, %s:%d" , apType, requestURL, proxyHost, proxyPort)); try { dataHttp.connect(); } catch (MalformedURLException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } // 读取返回数据 // 调用下面的方法可以实现压缩编码的解压和字符编码的自动识别 String page = dataHttp.getResponseString(); if (page == null) printLog(title, "Failed receiving data from: " + requestURL); else { String strOut = (page.length() <= 200) ? page : page.substring(0, 195) + " ...."; printLog(title, String.format("Finished receiving data(%dB) " ", page length in char:%d/n%s/n=============" , dataHttp.body.length, page.length(), strOut)); } } // END of run() }; // END defining class Runnable // 启动网络连接和数据读取的线程 new Thread(runnable).start(); } // END onCreate() } // END defining class DataConnectionDemo
附件参考库的使用方法
见附件Javadoc文档。
附件示例代码的运行效果
下图显示了示例程序运行的效果。网络连接使用了cmnet接入点,没有设置HTTP代理。获取到数据大小是9516字节,解压后内容包含字符30649个。网络数据经过6次读取和等待后完成,由此也可以计算出网络数据的传输速度。
下图显示了第一次连接cmwap接入点后得到计费提醒页的内容截图。使用了代理10.0.0.172:80,此时获取到的内容长度是488 Byte,内容为WML页面。
重新使用cmwap接入点获取数据后,内容成功获取到,网络接收的文件是31195字节。从Demo程序运行时刻的效果可以发现,Demo运行时刻cmwap接入点配置的HTTP代理服务器不支持HTTP内容的压缩编码传输 ,因此传输接收的数据量是未传输压缩编码前的原网页大小。
源代码:
bin.zip DataConnectionDemo.tgz document-DataConnectionHelper.zip