3.3 五种数据存储方式
Android操作系统提供的是一种公共文件系统,任何应用软件都可以使用它来存储和读取文件,该文件也可以被其他的应用软件所读取(会有一些权限控制设定)。在Android中,所有的应用软件数据(包括文件)为该应用软件私有。然而,Android同样也提供了一种标准方式供应用软件将私有数据开放给其他应用软件。具体来说,在Android中提供了如下5种数据存储方式。
· 文件存储
· SQLite数据库方式
· 内容提供器(ContentProvider)
· SharedPreferences
· 网络
3.3.1 最容易掌握的SharedPreferences存储
SharedPreferences是Android提供的用来存储一些简单的配置信息的一种机制。SharedPre-ferences以键值对方式存储,这样开发人员可以很方便地实现读取和存入。SharedPreferences经常用于存储常见的欢迎语、登录用户名和密码等信息。
1. SharedPreferences简介
SharedPreferences是Android平台上一个轻量级的存储类,主要是保存一些常用的配置信息。通过使用SharedPreferences,可以保存Android平台中的Long长整型、Int整型、String字符串型数据。在SharedPreferences中可以将数据设置为多种权限,最常用的是设置为全局共享访问。最终会以XML方式来保存数据,在处理这些XML数据时,Dalvik会通过自带底层的本地XML Parser进行解析,如XMLpull方式,这种方式会节约内存资源。
在两个Activity之间,除了可以通过Intent来传递数据,还可以用SharedPreferences共享数据的方式实现数据传递。使用SharedPreferences的方法很简单,例如,可以先在A中设置如下代码:
Editor sharedata = getSharedPreferences("data", 0).edit(); sharedata.putString("item","getSharedPreferences"); sharedata.commit();
然后可以在B中编写如下获取代码:
SharedPreferences sharedata = getSharedPreferences("data", 0); String data = sharedata.getString("item", null); Log.v("cola","data="+data);
最后,可以通过以下Java代码将数据显示出来:
<SPAN class=hilite1>SharedPreferences </SPAN> sharedata = getSharedPreferences("data", 0); String data = sharedata.getString("item", null); Log.v("cola","data="+data);
SharedPreferences的用法基本上和J2SE(java.util.prefs.Preferences)中的用法一样,最终目的是用一种简单的、透明的方式,来保存一些用户个性化设置的字体、颜色等参数信息。一般的应用程序都会提供“设置”或者“首选项”之类的界面,那么这些设置就可以通过SharedPre-ferences来保存。程序员不需要知道信息到底以什么形式保存的,保存在什么地方。
2. 练习SharedPreferences
为了使读者掌握SharedPreferences存储的知识,接下来将通过一个具体实例讲解使用SharedPreferences存储数据的基本流程。
实例3-1 练习使用SharedPreferences来存储数据(daima\3\SharedPreferences)。
step 1 编写文件SharedPreferencesHelper.java,主要代码如下所示。
public class SharedPreferencesHelper { SharedPreferences sp; SharedPreferences.Editor editor; Context context; public SharedPreferencesHelper(Context c,String name){ context = c; sp = context.getSharedPreferences(name, 0); editor = sp.edit(); } public void putValue(String key, String value){ editor = sp.edit(); editor.putString(key, value); editor.commit(); } public String getValue(String key){ return sp.getString(key, null); } }
step 2 编写文件SharedPreferencesUsage.java,主要代码如下所示。
public class SharedPreferencesUsage extends Activity { public final static String COLUMN_NAME ="name"; public final static String COLUMN_MOBILE ="mobile"; SharedPreferencesHelper sp; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); //setContentView(R.layout.main); sp = new SharedPreferencesHelper(this, "contacts"); //1. to store some value sp.putValue(COLUMN_NAME, "那一剑的风情"); sp.putValue(COLUMN_MOBILE, "00000000000"); String name = sp.getValue(COLUMN_NAME); String mobile = sp.getValue(COLUMN_MOBILE); TextView tv = new TextView(this); tv.setText("NAME:"+ name + "\n" + "MOBILE:" + mobile); setContentView(tv); } }
执行后的效果如图3-2所示。
图3-2 执行效果
在上述实例代码中,“NAME”和“MOBILE”两者的数据是在SharedPreferences中存储的。因为上面例子中的pack_name为:
package com.android.SharedPreferences;
所以存放数据的路径为:
data/data/com.android.SharedPreferences/share_prefs/contacts.xml
其中文件contacts.xml中的内容如下所示。
<?xml version='1.0' encoding='utf-8' standalone='yes' ?> <map> <string name="mobile">11111111111</string> <string name="name">那一剑的风情</string> </map>
3.3.2 文件存储
前面介绍的SharedPreferences存储方式虽然非常方便,但是有一个致命的缺点——只适用于存储比较简单的数据。在Android中如果要存储更多的数据,可供选择的解决方案有几种,如接下来将要讲解的文件存储方式就是一种很好的选择。和传统的在Java中实现I/O的程序类似,在Android中,可以使用方法openFileInput()和openFileOuput()来读取设备上的文件,如下面的代码所示。
String FILE_NAME = "tempfile.tmp"; //确定要操作文件的文件名 //初始化 FileOutputStream fos = openFileOutput(FILE_NAME, Context.MODE_PRIVATE); FileInputStream fis = openFileInput(FILE_NAME); //创建写入流代码解释
在上述代码中,方法openFileInput()和openFileOuput()只能读取该应用目录下的文件。如果要读取非自身目录下的文件,则会很不幸地抛出异常。如果在调用FileOutputStream时指定的文件不存在,Android会自动创建它,并且在默认情况下,写入的时候会覆盖原文件内容。如果想把新写入的内容附加到原文件内容后,则可以指定其模式为Context.MODE_APPEND。在默认情况下,使用openFileOutput()方法创建的文件只能被调用其的应用程序使用,其他应用程序无法读取这个文件。如果需要在不同的应用中共享数据,可以使用ContentProvider实现。
如果应用程序需要使用一些额外的资源文件,例如,用来测试音乐播放器是否可以正常工作的MP3文件,则可以将这些测试文件放在应用程序的“/res/raw/”目录下,如文件mydatafile. mp3。此时就可以在应用程序中使用getResources获取资源,然后用openRawResource()方法(不带后缀的资源文件名)打开这个文件,具体实现代码如下所示。
Resources myResources = getResources(); InputStream myFile = myResources.openRawResource(R.raw.myfilename);
除了方法openFileInput()和openFileOuput()可以读写文件外,在Android中还提供了方法deleteFile()和fileList()等来操作文件。
3.3.3 最常用的SQLite存储
在Android中最常用的存储方式是SQLite存储,它是一个轻量级的嵌入式数据库。SQLite是Android自带的一个标准的数据库,支持统一的SQL语句。
1. SQLite基础
SQLite是一款轻型的数据库,遵守ACID关联式数据库管理系统。SQLite是为嵌入式系统所设计的产品,而且目前已经在很多嵌入式产品中使用。SQLite的突出优点是占用非常少的资源。在嵌入式设备中,可能只需几百KB的内存就够了。SQLite能够支持Windows、Linux、UNIX等主流的操作系统,同时能够与很多程序语言相结合,如C#、PHP、Java等,并且还支持ODBC接口。另外,与MySQL、PostgreSQL这两款开源数据库管理系统相比,SQLite的处理速度更快。
注意:ACID是数据库事务正确执行的4个基本要素的缩写,具体包含:原子性(Atomicity)、一致性(Consistency)、隔离性(Isolation)、持久性(Durability)。一个支持事务(Transaction)的数据库系统,必须具有这4种特性,否则在事务过程(Transaction processing)中无法保证数据的正确性,交易过程极可能达不到交易方的要求。
2. 使用SQLite
为了使读者掌握SQLite存储的基本知识,接下来将通过一个具体实例的实现过程,详细介绍在Android中使用SQLite存储的基本流程。
实例3-2 练习使用SQLite来存储数据(daima\3\SQLite)。
编写实现文件UserSQLite.java,具体实现流程如下所示。
step 1 定义类DatabaseHelper继承于SQLiteOpenHelper,具体代码如下所示。
private static class DatabaseHelper extends SQLiteOpenHelper { DatabaseHelper(Context context) { super(context, DATABASE_NAME, null, DATABASE_VERSION); } @Override public void onCreate(SQLiteDatabase db) { String sql = "CREATE TABLE " + TABLE_NAME + " (" + TITLE + " text not null, " + BODY + " text not null " + ");"; Log.i("haiyang:createDB=", sql); db.execSQL(sql); } @Override public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { } }
在上述代码中,首先分别重写了方法onCreate()和onUpgrade(),然后在onCreate()方法中构造了一条SQL语句,并且通过db.execSQL(sql)执行了这条SQL语句。这条SQL语句的功能是生成了一张数据库表。
类SQLiteOpenHelper是一个辅助类,目的是生成一个数据库,并对数据库的版本进行管理。当在程序当中调用这个类的方法getWritableDatabase()或者getReadableDatabase()时,若当时没有数据,那么Android系统就会自动生成一个数据库。
类SQLiteOpenHelper是一个抽象类,在Android项目中通常需要继承这个类。在类SQLiteOpenHelper的实现中包含了3个方法,各方法的具体说明如下所示。
· 方法onCreate(SQLiteDatabase):在数据库第一次生成的时候会调用这个方法,一般在这个方法中生成数据库表。
· 方法onUpgrade(SQLiteDatabase, int, int):当数据库需要升级的时候,Android系统会主动调用这个方法。一般在这个方法里删除数据表,并建立新的数据表,当然是否还需要做其他的操作,完全取决于应用的需求。
· 方法onOpen(SQLiteDatabase):当打开数据库时的回调方法,一般不会用到。
step 2 编写按钮处理事件,如果单击“添加两条数据”按钮,若数据成功插入到数据库中的diary表中,那么在界面的title区域就会显示成功提示,如图3-3所示。
图3-3 插入成功
当单击“添加两条数据”按钮后,执行监听器里的onClick方法,并最终执行了程序中的insertItem()方法,具体代码如下所示。
/*插入两条数据*/ private void insertItem() { /*得到一个可写的SQLite数据库,如果这个数据库还没有建立*/ /*那么mOpenHelper辅助类负责建立这个数据库。*/ /*如果数据库已经建立,那么直接返回一个可写的数据库。*/ SQLiteDatabase db = mOpenHelper.getWritableDatabase(); String sql1 = "insert into " + TABLE_NAME + " (" + TITLE + ", " + BODY + ") values('AA', 'android好');"; String sql2 = "insert into " + TABLE_NAME + " (" + TITLE + ", " + BODY + ") values('BB', 'android好');"; try { Log.i("haiyang:sql1=", sql1); Log.i("haiyang:sql2=", sql2); db.execSQL(sql1); db.execSQL(sql2); setTitle("插入成功"); } catch (SQLException e) { setTitle("插入失败"); } }
对上述代码有如下几点说明。
· sql1和sql2:是构造的标准的插入SQL语句,如果对SQL语句不是很熟悉,可以参考相关的书籍。鉴于本书的重点是Android,所以对SQL语句的知识不进行详细介绍。
· Log.i():能够将参数内容打印到日志当中,并且打印级别是Info级别。
· db.execSQL(sql1):执行SQL语句。
Android支持5种打印输出级别,分别是Verbose、Debug、Info、Warning、Error,在程序中最常用的是Info级别,即将一些自己需要知道的信息打印出来,如图3-4所示。
图3-4 打印输出级别
step 3 单击“查询数据库”按钮,在屏幕界面的title区域会显示当前数据表中数据的条数。刚才插入了两条,那么现在单击后应显示为两条,如图3-5所示。
图3-5 查询数据
单击“查询数据库”按钮后,程序会执行监听器里的onClick方法,并最终执行了程序中的方法showItems(),具体代码如下所示。
/*在屏幕的title区域显示当前数据表当中的数据的条数*/ private void showItems() { /*得到一个可写的数据库*/ SQLiteDatabase db = mOpenHelper.getReadableDatabase(); String col[] = { TITLE, BODY }; Cursor cur = db.query(TABLE_NAME, col, null, null, null, null, null); /*通过getCount()方法,可以得到Cursor当中数据的个数。*/ Integer num = cur.getCount(); setTitle(Integer.toString(num) + " 条记录"); } }
在上述代码中,语句“Cursor cur = db.query(TABLE_NAME, col, null, null, null, null, null)”比较难以理解,此语句的功能是将查询到的数据放到一个Cursor当中。在这个Cursor里边封装了数据表TABLE_NAME中的所有数据列。
方法query()的功能是查询数据,包含如下7个参数。
· 第1个参数是数据库中表的名字,比如在这个例子中,表的名字就是TABLE_NAME,也就是“diary”。
· 第2个字段是我们想要返回数据包含的列的信息。在这个例子中想要得到的列有title、body,把这两个列的名字放到字符串数组里边来。
· 第3个参数为selection,相当于SQL语句的where部分,如果想返回所有的数据,那么就直接置为null。
· 第4个参数为selectionArgs。在selection部分,你有可能用到“?”,那么selectionArgs定义的字符串会代替selection中的“?”。
· 第5个参数为groupBy。定义查询出的数据是否分组,如果为null则说明不用分组。
· 第6个参数为having,相当于SQL语句当中的having部分。
· 第7个参数为orderBy,描述期望的返回值是否需要排序,如果设置为null则说明不需要排序。
注意:Cursor在Android中是一个非常有用的接口,通过Cursor可以对从数据库查询出来的结果集进行随机的读写访问。
step 4 单击“删除一条数据”按钮后,如果成功删除会在屏幕的标题(title)区域看到文字提示,如图3-6所示。
图3-6 删除一条数据
现在再次单击“查询数据库”按钮,会发现数据库中的记录少了一条,如图3-7所示。
图3-7 查询数据
当单击“删除一条数据”按钮,程序会执行监听器中的onClick方法,并最终执行程序中的deleteItem()方法,其实现代码如下所示。
/*删除其中的一条数据*/ private void deleteItem() { try { SQLiteDatabase db = mOpenHelper.getWritableDatabase(); db.delete(TABLE_NAME, " title = 'AA'", null); setTitle("删除了一条title为AA的记录"); } catch (SQLException e) { } }
在上述代码中,通过“db.delete(TABLE_NAME, " title = 'haiyang'", null)”语句删除了一条title为“AA”的数据。如果有很多条title为“AA”的数据,则会全部删除。方法delete()中各参数的具体说明如下。
· 第1个参数:表示数据库表名,在这里是TABLE_NAME,也就是diary。
· 第2个参数:相当于SQL语句当中的where部分,描述了删除的条件。
如果在第二个参数中有“?”,那么第三个参数中的字符串会依次替换在第二个参数中出现的“?”。
step 5 单击“删除数据表”按钮后,可以删除表diary,如图3-8所示。
图3-8 删除表
单击“删除数据表”按钮后会执行方法dropTable(),具体代码如下所示。
/*删除数据表*/ private void dropTable() { SQLiteDatabase db = mOpenHelper.getWritableDatabase(); String sql = "drop table " + TABLE_NAME; try { db.execSQL(sql); setTitle("删除成功:" + sql); } catch (SQLException e) { setTitle("删除错误"); } }
在上述代码中,构造了一个标准的删除数据表的SQL语句,然后执行语句db.execSQL(sql)。
step 6 此时如果单击其他按钮,可能会出现运行异常,如果单击“新建数据表”按钮,执行效果如图3-9所示。
图3-9 新建表
此时再单击“查询数据库”按钮,可以查看里边是否有数据,如图3-10所示。
图3-10 显示0条记录
单击“新建数据表”按钮后会执行方法CreateTable(),具体实现代码如下所示。
/*重新建立数据表*/ private void CreateTable() { SQLiteDatabase db = mOpenHelper.getWritableDatabase(); String sql = "CREATE TABLE " + TABLE_NAME + " (" + TITLE + " text not null, " + BODY + " text not null " + ");"; Log.i("haiyang:createDB=", sql); try { db.execSQL("DROP TABLE IF EXISTS diary"); db.execSQL(sql); setTitle("重建数据表成功"); } catch (SQLException e) { setTitle("重建数据表错误"); } }
在上述代码中,sql变量表示的语句为标准的SQL语句,负责按要求建立一张新表;“db.execSQL("DROP TABLE IF EXISTS diary")”表示如果存在表diary则先将其删除,因为在同一个数据库中不能出现两张同样名字的表;“db.execSQL(sql)”语句用于执行SQL语句,这条SQL语句的功能是建立一个新表。
3.3.4 重要的ContentProvider存储
在Android系统中,数据是私有的,当然这些数据包括文件数据和数据库数据以及一些其他类型的数据。在Android中的两个程序之间可以进行数据交换,此功能是通过ContentProvider实现的。
1. ContentProvider基础
类ContentProvider实现了一组标准的方法接口,从而能够让其他的应用保存或读取此ContentProvider的各种数据类型。在程序中可以通过实现ContentProvider抽象接口的方式将自己的数据显示出来。从外界不会看到这个显示数据在应用中是如何存储的,无须关心是用数据库存储还是用文件存储的,需要关注的是,外界可以通过这套标准的、统一的接口在程序中实现数据交互,既可以读取程序里的数据,也可以删除程序里的数据。
(1)ContentResolver接口
在类ContentProvider中,外部程序可以通过其ContentResolver接口访问ContentProvider提供的数据。在Activity中,可以通过方法getContentResolver()获取当前应用的ContentResolver实例。ContentResolver提供的接口需要和ContentProvider中需要实现的接口相对应。ContentResolver接口中的常用方法如下所示。
· query(Uri uri, String[] projection, String selection, String[] selectionArgs,String sortOrder):通过Uri进行查询,返回一个Cursor;
· insert(Uri url, ContentValues values):将一组数据插入Uri指定的地方;
· update(Uri uri, ContentValues values, String where, String[] selectionArgs):更新Uri指定位置的数据;
· delete(Uri url, String where, String[] selectionArgs):删除指定Uri并且符合一定条件的数据。
(2)ContentProvider和ContentResolver中的URI
在ContentProvider和ContentResolver中,通常有两种使用URI的形式,一种是指定所有的数据,另一种是只指定某个ID的数据。例如:
content://contacts/people/ //此Uri指定的就是全部的联系人数据 content://contacts/people/1 //此Uri指定的是ID为1的联系人的数据
上面用到的URI一般由如下3部分组成。
· 第1部分是“content://”;
· 第2部分是要获得数据的一个字符串片段;
· 第3部分是ID(如果没有指定ID,那么表示返回全部)。
因为URI通常比较长,而且有时候容易出错,并且难以理解。所以在Android中定义了一些辅助类和常量来代替这些长字符串的使用,如下所示。
Contacts.People.CONTENT_URI (联系人的URI)
2. 使用ContentProvider
为了使读者掌握ContentProvider存储的用法,接下来将通过一个具体的实例,详细讲解在Android中使用ContentProvider存储数据的基本流程。
实例3-3 练习使用ContentProvider来存储数据(daima\3\ContentProvider)。
编写主程序文件ActivityMain.java,具体代码如下所示。
protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); Cursor c = getContentResolver().query(Phones.CONTENT_URI, null, null, null, null); startManagingCursor(c); ListAdapter adapter = new SimpleCursorAdapter(this, android.R.layout.simple_list_item_2, c, new String[] { Phones.NAME, Phones.NUMBER }, new int[] { android.R.id.text1, android.R.id.text2 }); setListAdapter(adapter); }
对上述代码的具体说明如下所示。
(1)方法getContentResolver():得到应用的ContentResolver实例。
(2)query(Phones.CONTENT_URI, null, null, null, null):ContentResolver中的方法,用于查询所有联系人,并返回一个Cursor。此方法中各参数的具体含义如下。
· 第1个参数为Uri,在此例中的Uri是联系人的Uri;
· 第2个参数是一个字符串的数组,数组里的每一个字符串都是数据表中某一列的名字,它指定返回数据表中哪些列的值;
· 第3个参数相当于SQL语句的where部分,描述哪些值是需要的;
· 第4个参数是一个字符串数组,里边的值依次代替在第3个参数中出现的“?”;
· 第5个参数指定了排序的方式。
(3)startManagingCursor(c)语句:让系统来管理生成的Cursor。
(4)ListAdapter adapter = new SimpleCursorAdapter(this,Android.R.layout.simple_list_item_2, c, new String[] { Phones.NAME, Phones.NUMBER }, new int[] { Android.R.id.text1, Android.R.id.text2 }):用于生成一个Simple- CursorAdapter。
(5)setListAdapter(adapter):将ListView和SimpleCu-rsorAdapter进行绑定。
运行后的效果如图3-11所示。
图3-11 初始效果
另外,还可以添加几条数据到联系人列表中,具体流程如下所示。
step 1 单击模拟器的键,在弹出的界面中单击“Contacts”图标,如图3-12所示。
图3-12 出现的界面
step 2 单击“Create New contact”按钮,如图3-13所示。
图3-13 单击“Create New contact”按钮
step 3 添加联系人姓名和电话号码信息,如图3-14所示。
图3-14 添加联系人姓名和电话号码
step 4 单击“Done”按钮完成添加联系人信息,如图3-15所示。
图3-15 单击“Save”按钮保存
通过上述操作步骤后,即可添加一条联系人的信息,如图3-16所示。
图3-16 添加后的数据
3.3.5 网络存储
除了本章前面介绍的存储方法外,在Android中还可以通过网络实现数据存储工作。在Android的早期版本中,曾经支持用XMPP Service和Web Service进行远程访问。Android SDK 1.0以后不再支持XMPP Service,而且访问Web Service的API全部变更。
网络存储在及时更新项目中比较常用,例如,可以在网络中通过邮政编码来查询该地区的天气预报。实现原理是以POST发送的方式发送请求到webservicex.net站点,访问WebService.webservicex.net站点上提供查询天气预报的服务,具体信息请参考其WSDL文档,其网址如下:
http://www.webservicex.net/WeatherForecast.asmx?WSDL
· 输入:美国某个城市的邮政编码。
· 输出:该邮政编码对应城市的天气预报。
要想实现上述通过邮政编码来查询地区天气预报的功能,可通过如下过程进行。
step 1 如果需要访问外部网络,则需要在文件AndroidManifest.xml中加入如下代码,申请权限许可。
<!--权限 --> <uses-permission Android:name="Android.permission.INTERNET" />
step 2 以HttpPost的方式发送,SERVER_URL并不是指WSDL的URL,而是服务本身的URL,具体实现的代码如下所示。
private static final String SERVER_URL = "http://www.webservicex.net/WeatherForecast. asmx/GetWeatherByZipCode"; //定义需要获取的内容来源地址 HttpPost request = new HttpPost(SERVER_URL); //根据内容来源地址创建一个Http请求 //添加一个变量 List <NameValuePair> params = new ArrayList <NameValuePair>(); //设置一个华盛顿区号 params.add(new BasicNameValuePair("ZipCode", "200120")); //添加必须的参数 request.setEntity(new UrlEncodedFormEntity(params, HTTP.UTF_8)); //设置参数的编码 try { HttpResponse httpResponse = new DefaultHttpClient().execute(request); //发送请求并获取反馈 // 解析返回的内容 if(httpResponse.getStatusLine().getStatusCode() != 404) { String result = EntityUtils.toString(httpResponse.getEntity()); Log.d(LOG_TAG, result); } } catch (Exception e) { Log.e(LOG_TAG, e.getMessage()); }
通过上述代码,使用Http从webservicex获取ZipCode为“200120”的内容,其返回的内容如下所示。
<WeatherForecasts xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http: //www.w3.org/2001/XMLSchema-instance" xmlns="http://www.webservicex.net"> <Latitude>38.97571</Latitude> <Longitude>77.02825</Longitude> <AllocationFactor>0.024849</AllocationFactor> <FipsCode>11</FipsCode> <PlaceName>WASHINGTON</PlaceName> <StateCode>DC</StateCode> <Details> <WeatherData> <Day>Saturday, April 25, 2009</Day> <WeatherImage>http://forecast.weather.gov/images/wtf/sct.jpg</WeatherImage> <MaxTemperatureF>88</MaxTemperatureF> <MinTemperatureF>57</MinTemperatureF> <MaxTemperatureC>31</MaxTemperatureC> <MinTemperatureC>14</MinTemperatureC> </WeatherData> <WeatherData> <Day>Sunday, April 26, 2009</Day> <WeatherImage>http://forecast.weather.gov/images/wtf/few.jpg</WeatherImage> <MaxTemperatureF>89</MaxTemperatureF> <MinTemperatureF>60</MinTemperatureF> <MaxTemperatureC>32</MaxTemperatureC> <MinTemperatureC>16</MinTemperatureC> </WeatherData> … </Details> </WeatherForecasts>
上述实现过程演示了如何在Android中通过网络获取数据。要掌握这方面的内容,开发者需要熟悉java.net.*和Android.net.*这两个包的内容,具体信息请读者参阅相关文档。