Android提供了下面4种机制来保存和获取数据:系统偏好Preferences,文件Files,数据库Databases和网络Network
一、SharedPreferences
SharedPreferences是一个简单键值对的xml格式的存储方式,用来存储一些简单的配置信息的一种机制。方便读取和存入。
a,读取
getPreferences (int mode) 返回SharedPreferences实例
mode Activity.MODE_PRIVATE, Activity.MODE_WORLD_READABLE, Activity.MODE_WORLD_WRITEABLE
getString (String key, String defValue) 获取数据
b,写数据
通过SharedPreferences(必须为MODE_PRIVATE或MODE_WORLD_WRITEABLE)实例的edit()返回Editor对象
Editor.putString (String key, String value) 设置值
Editor.commit() 提交保存
Editor.clear() 清除所有数据
Editor.remove (String key) 移除某一值
eg:
下面看一个演示的例子。
1.第一步
在Eclipse中打开ex_SharedPreferences 项目,其步骤如下所示。
(1)新建一个项目。依次单击File→New→Android Project项。
(2)在新建项目的对话框中,选择Create project from existing source项。
(3)单击浏览,找到ex_SharedPreferences项目,然后单击确定
2.第二步
单击运行项目,可以看到主界面,这个界面的布局信息都在main.xml文件当中,在一个LinearLayout当中放了3个TextView和两个EditView组件,代码如下所示:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:Android="http://schemas.Android.com/apk/res/Android"
Android:orientation="vertical"
Android:layout_width="fill_parent"
Android:layout_height="fill_parent"
>
<TextView
Android:layout_width="fill_parent"
Android:layout_height="wrap_content"
Android:text="SharedPreferences demo"
/>
<TextView Android:layout_width="fill_parent"
Android:layout_height="wrap_content"
Android:text="Name:" />
<EditText Android:id="@+id/name"
Android:layout_width="fill_parent"
Android:layout_height="wrap_content"
Android:text="" />
<TextView Android:layout_width="fill_parent"
Android:layout_height="wrap_content"
Android:text="Password:" />
<EditText Android:id="@+id/password"
Android:layout_width="fill_parent"
Android:layout_height="wrap_content"
Android:password="true"
Android:text="" /> //初始状态下,EditView都是空的,输入用户名和密码,退出-在应用列表中找到该应用,重新启动,则看到前面输入的用户名和密码
</LinearLayout>
由此可见,应用保存了我们输入的Name和Password,现在来看看其实现的代码,在DBShared Preferences.java文件中,此文件的代码如下所示:
package us.imnet.iceskysl.db;
import Android.app.Activity;
import Android.content.SharedPreferences;
import Android.os.Bundle;
import Android.widget.EditText;
public class DBSharedPreferences extends Activity {
public static final String SETTING_INFOS = "SETTING_Infos";
public static final String NAME = "NAME";
public static final String PASSWORD = "PASSWORD";
private EditText field_name; //接收用户名的组件
private EditText filed_pass; //接收密码的组件
/** Called when the activity is first created. */
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
//Find VIew
field_name = (EditText) findViewById(R.id.name); //首先获取用来输入用户名的组件
filed_pass = (EditText) findViewById(R.id.password); //同时也需要获取输入密码的组件
//在oncreate中用findViewByid得到两个EditView
// Restore preferences
SharedPreferences settings = getSharedPreferences(SETTING_INFOS, 0); //获取一个SharedPreferences对象settings
String name = settings.getString(NAME, ""); //取出保存的NAME
String password = settings.getString(PASSWORD, ""); //取出保存的PASSWORD
//Set value//使用getString取得其中保存的值
field_name.setText(name); //将取出来的用户名赋予field_name
filed_pass.setText(password); //将取出来的密码赋予filed_pass
//用setText将值设置为两个EditText的值
}
@Override
protected void onStop(){
super.onStop();
SharedPreferences settings = getSharedPreferences(SETTING_INFOS, 0); //首先获取一个SharedPreferences对象
settings.edit()//调用edit()方法使其处于可以编辑的状态
.putString(NAME, field_name.getText().toString())
.putString(PASSWORD, filed_pass.getText().toString())
//然后用putString将两个EditText中的值保存起来
.commit();
} //用commit()方法将用户名和密码提交即可保存进去
}
小知识
SharedPreferences保存到哪里去了?
SharedPreferences是以XML的格式以文件的方式自动保存的,在DDMS中的File Explorer中展开到/data/data/<package name>/shared_prefs下,以上面这个为例,可以看到一个叫做SETTING_Infos.xml的文件
将其导出到设备中,可以打开这个文件,看到其代码内容为:
<?xml version='1.0' encoding='utf-8' standalone='yes' ?>
<map>
<string name="PASSWORD">Password</string>
<string name="NAME">IceskYsl</string>
</map>
小知识
我们可以通过「getXXX」函数,从 SharedPreferences中读取不同类型的内容,例如,上面我们使用[getString]读取String类型的内容。
注意
Preferences只能在同一个包内使用,不能在不同的包之间使用。
本文来自CSDN博客,转载请标明出处:http://blog.csdn.net/to_cm/archive/2010/06/26/5695675.aspx
二、文件存储
前面介绍的Shared Preferences存储方式非常方便,但是其只适合存储比较简单的数据,如果需要存储更多的数据,可行选择的方式有好几种,这里先给读者介绍文件存储的方法。
和传统的Java中实现I/O的程序类似,在Android中,其提供了openFileInput 和 openFileOuput 方法读取设备上的文件,下面看个例子代码,具体如下所示:
String FILE_NAME = "tempfile.tmp"; //确定要操作文件的文件名
FileOutputStream fos = openFileOutput(FILE_NAME, Context.MODE_PRIVATE); //初始化
FileInputStream fis = openFileInput(FILE_NAME); //创建写入流
代码解释:
上述代码中两个方法只支持读取该应用目录下的文件,读取非其自身目录下的文件将会抛出异常。需要提醒的是,如果调用FileOutputStream 时指定的文件不存在,Android 会自动创建它。另外,在默认情况下,写入的时候会覆盖原文件内容,如果想把新写入的内容附加到原文件内容后,则可以指定其模式为Context.MODE_APPEND。
注意
默认情况下,使用openFileOutput方法创建的文件只能被其调用的应用使用,其他应用无法读取这个文件,如果需要在不同的应用中共享数据,可以使用Content Provider实现,关于Content Provider我们将在稍后的内容中介绍。
小知识 资源文件放在哪里?
如果你的应用需要一些额外的资源文件,例如,一些用来测试你写的音乐播放器是否可以正常工作的MP3文件,可以将这些文件放在应用程序的/res/raw/下,如mydatafile.mp3。那么就可以在你的应用中使用getResources获取资源后,以openRawResource方法(不带后缀的资源文件名)打开这个文件,实现代码如下所示:
Resources myResources = getResources();
InputStream myFile = myResources.openRawResource(R.raw.myfilename);
除了前面介绍的读写文件外,Android还提供了诸如deleteFile、fileList 等方法来操作文件,不再赘述。
三、SQLite存储方式
a,SQLiteDatabase类
1),打开/关闭数据库
openOrCreateDatabase(String name, int mode, CursorFactory factory)返回SQLiteDatabase实例 //数据库不存在则新建一个
mode Context.MODE_PRIVATE, Context.MODE_WORLD_READABLE, Context.MODE_WORLD_WRITEABLE, Context.MODE_PRIVATE
SQLiteDatabase类.close()关闭数据库
2),执行数据库操作
SQLiteDatabase.execSQL (String sql) //执行Sql语句
long insert (String table, String nullColumnHack, ContentValues values)
int update (String table, ContentValues values, String whereClause, String[] whereArgs)
int delete (String table, String whereClause, String[] whereArgs)
其中ContentValues是一个键值对,通过ContentValues.put方法设置列名和列值
Cursor rawQuery (String sql, String[] selectionArgs) //执行的是语句
Cursor query (String table, String[] columns, String selection, String[] selectionArgs, String groupBy, String having, String orderBy)
Cursor query (String table, String[] columns, String selection, String[] selectionArgs, String groupBy, String having, String orderBy, String limit)
Cursor query (boolean distinct, String table, String[] columns, String selection, String[] selectionArgs, String groupBy, String having,
String orderBy, String limit)
其中:
columns 显示列
selection 条件where部分
selectionArgs ?替换符号.如selection为name =? and age=?那么selectionArgs可以为 new String[]{"abc","20"}
Cursor类
int getCount() 返回记录数
boolean isClosed () 判断关闭
boolean isFirst ()
boolean isLast ()
boolean moveToFirst ()
boolean moveToLast ()
boolean moveToNext ()
boolean moveToPosition (int position)
boolean moveToPrevious ()
boolean isNull (int columnIndex) 判断是否为空
XXX getXXX(int columnIndex) 读取某一列的数据
3),事务
SQLiteDatabase.beginTransaction() //打开
SQLiteDatabase.setTransactionSuccessful() //提交
SQLiteDatabase.endTransaction() //结束
b,SQLiteOpenHelper
通过该继承该类可以获取到数据库创建/打开/升级等信息
须实现的方法
onCreate(SQLiteDatabase db) //数据库被创建时触发
onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) //升级数据库
其他重写
onOpen(SQLiteDatabase db) 书库打开是触发
方法
SQLiteDatabase getReadableDatabase ()
synchronized SQLiteDatabase getWritableDatabase ()
无论是Shared Preferences还是SQLite都是程序私有的其他程序无法直接访问.要想访问其他程序的数据须实现Content Provider.
SQLite存储的使用与步骤
在Eclipse中,打开ex08_1_SQLite 项目,具体步骤如下。
• 新建一个项目。依次单击File→New→Android Project项。
• 在新建项目的对话框中,选择Create project from existing source项。
• 单击浏览,找到ex08_1_SQLite项目,然后单击确定。
程序的目录结构如图8-6所示。
2.第二步
单击运行项目,我们可以看到主界面如图8-7所示,这个界面的布局信息都在main.xml文件中,在一个LinearLayout当中数值排列了5个Button。
3.第三步
小知识 什么是SQLiteDatabase?
一个SQLiteDatabase的实例代表了一个SQLite的数据库,通过SQLiteDatabase实例的一些方法,我们可以执行SQL语句,对数据库进行增、删、查、改的操作。需要注意的是,数据库对于一个应用来说是私有的,并且在一个应用当中,数据库的名字也是惟一的。
▲ 图8-6 程序目录结构图 ▲ 图8-7 主界面
小知识
什么是SQLiteOpenHelper ?
根据这名字,我们可以看出这个类是一个辅助类。这个类主要生成一个数据库,并对数据库的版本进行管理。当在程序当中调用这个类的方法getWritableDatabase()或者getReadableDatabase()方法的时候,如果当时没有数据,那么Android系统就会自动生成一个数据库。SQLiteOpenHelper 是一个抽象类,我们通常需要继承它,并且实现里边的3个函数,具体函数如下所示。
• onCreate(SQLiteDatabase):在数据库第一次生成的时候会调用这个方法,一般我们在这个方法里边生成数据库表。
• onUpgrade(SQLiteDatabase, int, int):当数据库需要升级的时候,Android系统会主动的调用这个方法。一般我们在这个方法里边删除数据表,并建立新的数据表,当然是否还需要做其他的操作,完全取决于应用的需求。
• onOpen(SQLiteDatabase):这是当打开数据库时的回调函数,一般也不会用到。
我们在ActivityMain文件中看下边这个内部类。DatabaseHelper 类继承SQLiteOpenHelper ,具体代码如下所示:
private static class DatabaseHelper extends SQLiteOpenHelper {
DatabaseHelper(Context context) {
super(context, DATABASE_NAME, null, DATABASE_VERSION);
}
@Override
public void onCreate(SQLiteDatabase db) {
// SQL语句
String sql = "CREATE TABLE " + TABLE_NAME + " (" + TITLE
+ " text not null, " + BODY + " text not null " + ");";
Log.i("haiyang:createDB=", sql);
//执行这条SQL语句
db.execSQL(sql);
}
@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
}
}
代码解释:
• DatabaseHelper类继承了SQLiteOpenHelper 类,并且重写了onCreate和onUpgrade方法。
• 在onCreate()方法里边首先我们构造一条SQL语句,然后调用db.execSQL(sql)执行SQL语句。这条SQL语句为我们生成了一张数据库表。
• 目前我们还不需要升级数据库,所以我们在onUpgrade()函数里边没有执行任何操作。
4.第四步
我们单击插入两条记录的按钮,如果数据成功插入到数据库当中的diary表中,那么在界面的title区域就会有成功的提示,如图8-8所示。
单击这个按钮后,程序执行了监听器里的onClick方法,并最终执行了上述程序里的insertItem方法,其具体代码如下所示:
private void insertItem() {
SQLiteDatabase db = mOpenHelper.getWritableDatabase();
//首先生成SQL语句。这条语句负责得到一个可写的SQLite数据库,如果这个数据库还没有建立,那么mOpenHelper辅助类负责建立这个数据库。如果数据库已经建立,那么直接返回一个可写的数据库。
String sql1 = "insert into " + TABLE_NAME + " (" + TITLE + ", " + BODY
+ ") values('haiyang', 'Android的发展真是迅速啊');";
String sql2 = "insert into " + TABLE_NAME + " (" + TITLE + ", " + BODY
+ ") values('icesky', 'Android的发展真是迅速啊');";
try {
Log.i("haiyang:sql1=", sql1); //会将参数内容打印到日志当中,并且打印级别是Info级别,在使用LogCat工具的时候我们会进行详细的介绍。
Log.i("haiyang:sql2=", sql2);
db.execSQL(sql1);//对SQL语句进行执行。
db.execSQL(sql2);
setTitle("插入两条数据成功");
} catch (SQLException e) {
setTitle("插入两条数据失败");
}
}
小知识 对Android的打印级别介绍
Android支持5种打印级别,分别是Verbose、Debug、Info、Warning、Error,当然我们在程序当中一般用到的是Info级别,即将一些自己需要知道的信息打印出来,如图8-9所示。
▲ 图8-9 Android中的5种打印级别
注意
虽然执行SQL语句在Android当中并没有强制放在try catch 语句当中,但是我们最好这么做。并在catch模块中将错误信息打印在日志当中。这样一方面是方便调试,另一方面可以加强程序的健壮性。
5.第五步
单击查询数据库的按钮,会在界面的title区域显示当前数据表当中数据的条数,刚才我们插入了两条,那么现在单击后应该显示为两条,如图8-10所示。
单击这个按钮后,程序执行了监听器里的onClick方法,并最终执行了上述程序里的showItems方法,具体代码如下所示:
private void showItems() {
SQLiteDatabase db = mOpenHelper.getReadableDatabase(); //得到一个可写的数据库。
String col[] = { TITLE, BODY };
Cursor cur = db.query (TABLE_NAME, col, null, null, null, null, null);//将查询到的数据放到一个Cursor当中,这个Cursor里边封装了这个数据表TABLE_NAME当中的所有条列。
Integer num = cur.getCount();
setTitle(Integer.toString(num) + " 条记录");
}
query()方法相当的有用,在这里我们简单地讲一下。
• 第一个参数是数据库里边表的名字,比如在我们这个例子,表的名字就是TABLE_NAME,也就是"diary"。
• 第二个字段是我们想要返回数据包含的列的信息。在这个例子当中我们想要得到的列有title、body。我们把这两个列的名字放到字符串数组里边来。
• 第三个参数为selection,相当于SQL语句的where部分,如果想返回所有的数据,那么就直接置为null。
• 第四个参数为selectionArgs。在selection部分,你有可能用到“?”,那么在selectionArgs定义的字符串会代替selection中的“?”。
• 第五个参数为groupBy。定义查询出来的数据是否分组,如果为null则说明不用分组。
• 第六个参数为having ,相当于SQL语句当中的having部分。
• 第七个参数为orderBy,来描述我们期望的返回值是否需要排序,如果设置为null则说明不需要排序。
• Integer num = cur.getCount()语句通过getCount()方法,可以得到Cursor当中数据的个数。
小知识 什么是Cursor ?
Cursor 在Android当中是一个非常有用的接口,通过Cursor 我们可以对从数据库查询出来的结果集进行随机的读写访问。
6.第六步
单击删除一条数据库的按钮后,如果成功删除,我们可以看到在屏幕的标题(title)区域有文字提示,如图8-11所示。
现在我们再单击查询数据库按钮,看数据库里边的记录是不是少了一条。单击查询数据库按钮后,出现如图8-12所示的界面。
▲ 图8-11 成功删除一条记录 ▲ 图8-12 还剩一条记录
下面我们来看一下如何删除数据。
单击删除一条记录的按钮后,程序执行了监听器里的onClick方法,并最终执行了上述程序里的deleteItem方法,其代码如下所示:
private void deleteItem() {
try {
SQLiteDatabase db = mOpenHelper.getWritableDatabase();
//进行删除操作
db.delete(TABLE_NAME, " title = 'haiyang'", null);
setTitle("删除title为haiyang的一条记录");
} catch (SQLException e) {
}
}
7.第七步
单击删除数据表,我们可以删除diary这张数据表,如图8-13所示。
下边我们看在代码部分,是怎么实现删除的,具体代码如下所示:
private void dropTable() {
SQLiteDatabase db = mOpenHelper.getWritableDatabase();
String sql = "drop table " + TABLE_NAME;
try {
//执行SQL语句
db.execSQL(sql);
setTitle("数据表成功删除:" + sql);
} catch (SQLException e) {
setTitle("数据表删除错误");
}
}
8.第八步
现在单击其他的按钮,程序运行时有可能会出现异常,我们单击重新建立数据表按钮,如图8-14所示。
现在我们单击查询数据库,看里边是否有数据,如图8-15所示。
下边我们看一下程序是如何建立一张新表的,具体实现代码如下所示:
private void CreateTable() {
SQLiteDatabase db = mOpenHelper.getWritableDatabase();
String sql = "CREATE TABLE " + TABLE_NAME + " (" + TITLE
+ " text not null, " + BODY + " text not null " + ");";//sql变量表示的语句为标准的SQL语句,负责按要求建立一张新表。
Log.i("haiyang:createDB=", sql);
try {
db.execSQL("DROP TABLE IF EXISTS diary"); //如果存在diary这张表,我们需要先删除,因为在同一个数据库当中不能出现两张同样名字的表。
db.execSQL(sql);// db.execSQL(sql)语句执行SQL语句,新表建立。
setTitle("数据表成功重建");
} catch (SQLException e) {
setTitle("数据表重建错误");
}
}
▲ 图8-14 重新建立数据库表 ▲ 图8-15 新建的表里边没有数据
四、ContentProvider
1.什么是ContentProvider
Android这个系统和其他的操作系统还不太一样,读者需要记住的是,数据在Android当中是私有的,当然这些数据包括文件数据和数据库数据以及一些其他类型的数据。那这个时候有读者就会提出问题,难道两个程序之间就没有办法对于数据进行交换?Android这么优秀的系统不会让这种情况发生的。解决这个问题主要靠ContentProvider。一个Content Provider类实现了一组标准的方法接口,从而能够让其他的应用保存或读取此Content Provider的各种数据类型。也就是说,一个程序可以通过实现一个Content Provider的抽象接口将自己的数据暴露出去。外界根本看不到,也不用看到这个应用暴露的数据在应用当中是如何存储的,或者是用数据库存储还是用文件存储,还是通过网上获得,这些一切都不重要,重要的是外界可以通过这一套标准及统一的接口和程序里的数据打交道,可以读取程序的数据,也可以删除程序的数据,当然,中间也会涉及一些权限的问题。下边列举一些较常见的接口,这些接口如下所示。
• 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.什么是ContentResolver
外界的程序通过ContentResolver接口可以访问ContentProvider提供的数据,在Activity当中通过getContentResolver()可以得到当前应用的ContentResolver实例。ContentResolver提供的接口和ContentProvider中需要实现的接口对应,主要有以下几个。
• 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并且符合一定条件的数据。
3.ContentProvider和ContentResolver中用到的Uri
在ContentProvider和ContentResolver当中用到了Uri的形式通常有两种,一种是指定全部数据,另一种是指定某个ID的数据。我们看下面的例子。
• content://contacts/people/ 这个Uri指定的就是全部的联系人数据。
• content://contacts/people/1 这个Uri指定的是ID为1的联系人的数据。
在上边两个类中用到的Uri一般由3部分组成。
• 第一部分是:"content://" 。
• 第二部分是要获得数据的一个字符串片段。
• 最后就是ID(如果没有指定ID,那么表示返回全部)。
由于URI通常比较长,而且有时候容易出错,且难以理解。所以,在Android当中定义了一些辅助类,并且定义了一些常量来代替这些长字符串的使用,例如下边的代码:
• Contacts.People.CONTENT_URI (联系人的URI)。
8.5.2 使用ContentProvider读取系统数据
在这个例子里边,首先在系统的联系人应用当中插入一些联系人信息,然后把这些联系人的名字和电话再显示出来,通过这个例子可以学到。
• 如何在联系人应用当中添加联系人。
• 如何使用系统提供的ContentProvider。
• 如何使用ContentResolver当中的query()方法。
具体实现步骤如下所示。
1.第一步
在Eclipse中打开ex09_1_ContentProvider项目,具体操作如下。
(1)新建一个项目,依次单击File→New→Android Project项。
(2)在新建项目的对话框中,选择Create project from existing source项。
(3)单击浏览按钮,找到ex09_1_ContentProvider项目,然后单击确定按钮。
程序的目录结构如图8-23所示。
2.第二步
首先运行这个项目,将会看到如图8-24所示的界面。
▲ 图8-23 程序的目录结构 ▲ 图8-24 未添加任何数据的主界面
图8-24所示的列表中没有任何数据,接下来的操作是为应用添加几条联系人数据。
3.第三步
按照下列图示添加几条数据到联系人列表中,具体步骤如下。
(1)单击模拟器的Home键,在转出来的界面上,单击桌面上的Contacts应用,如图8-25所示。
(2)进入应用后,单击MENU项,在出现的界面上单击New contact 按钮,如图8-26所示。
▲ 图8-25 单击Contacts应用 ▲ 图8-26 单击New contact选项
(3)添加联系人姓名和电话号码信息,如图8-27所示。
(4)单击MENU项,在返回的界面上单击Save项保存,如图8-28所示。
▲ 图8-27 添加联系人姓名和电话号码 ▲ 图8-28 保存联系人姓名和电话号码信息
(5)按照上边的操作步骤,添加了两条数据后显示如图8-29所示。
4.第四步
再次运行程序,模拟器显示如图8-30所示。
▲ 图8-29 添加后结果 ▲ 图8-30 模拟器显示
我们看一下程序ActivityMain中的onCreate()方法,具体代码如下所示:
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Cursor c = getContentResolver().query(Phones.CONTENT_URI, null, null, null, null);//getContentResolver()方法得到应用的ContentResolver实例。
// query(Phones.CONTENT_URI, null, null, null, null)。它是ContentResolver里的方法,负责查询所有联系人,并返回一个Cursor
startManagingCursor(c); //让系统来管理生成的Cursor
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 });//生成一个SimpleCursorAdapter。
setListAdapter(adapter);//将ListView和SimpleCursorAdapter进行绑定
}
query(Phones.CONTENT_URI, null, null, null, null)。它是ContentResolver里的方法,负责查询所有联系人,并返回一个Cursor
.这个方法参数比较多,每个参数的具体含义如下。
• 第一个参数为Uri,在这个例子里边这个Uri是联系人的Uri。
• 第二个参数是一个字符串的数组,数组里边的每一个字符串都是数据表中某一列的名字,它指定返回数据表中那些列的值。
• 第三个参数相当于SQL语句的where部分,描述哪些值是我们需要的。
• 第四个参数是一个字符串数组,它里边的值依次代替在第三个参数中出现的“?”符号。
• 第五个参数指定了排序的方式。
五、网络储存
前面介绍的几种存储都是将数据存储在本地设备上,除此之外,还有一种存储(获取)数据的方式,通过网络来实现数据的存储和获取,下面看一个在Android上调用WebService的例子。
注意
在Android的早期版本中,曾经支持过进行XMPP Service和Web Service的远程访问。Android SDK 1.0以后的版本对它以前的API作了许多的变更。Android 1.0以上版本不再支持XMPP Service,而且访问Web Service的API全部变更。
1.例子介绍
通过邮政编码查询该地区的天气预报,以POST发送的方式发送请求到webservicex.net站点,访问WebService.webservicex.net站点上提供查询天气预报的服务,具体信息请参考其WSDL文档,网址为:
http://www.webservicex.net/WeatherForecast.asmx?WSDL。
输入:美国某个城市的邮政编码。
输出:该邮政编码对应城市的天气预报。
2.实现步骤如下
(1)如果需要访问外部网络,则需要在AndroidManifest.xml文件中加入如下代码申请权限许可:
<!-- Permissions -->
<uses-permission Android:name="Android.permission.INTERNET" />
(2)以HTTP POST的方式发送(注意: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”(美国WASHINGTON D.C)的内容,其返回的内容如下:
<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.*这两个包的内容,在这就不赘述了,请读者参阅相关文档。
数据存储Data Storage
概览Storage quickview
系统偏好:快速,轻量级存储
文件:存储到设备内部或可移动闪存
数据库:任意的结构化存储
支持基于网络的存储
一个典型的桌面操作系统提供了一个通用文件系统使得任何应用程序能够使用它来存储文件,这些文件可以被其它应用程序读取(可能有访问权限的设置)。Android使用一个不同的系统:在Android上,所有应用程序数据(包括文件)都是该应用程序私有的。
不过,Android同样提供了一个应用程序向其它应用程序暴露其私有数据的基本方式-通过内容提供器。内容提供器是应用程序的可选组件,用来暴露该应用程序数据的读写接口,且遵循任何可能引入的约定。内容提供器实现了一个用来请求和修改数据的基本语法,一个读取返回数据的基本机制。Android为基础数据类型如图像,音频和视频文件以及个人联系人信息提供了许多内容提供器。想要了解更多如何使用内容提供器的信息,请参见一篇单独的文章:内容提供器(Content Providers)。
无论你是否想把应用程序数据输出给别人,你总需要有一个方法来保存它。Android提供了下面4种机制来保存和获取数据:系统偏好Preferences,文件Files,数据库Databases和网络Network。
系统偏好Preferences
系统偏好是一个用来存放和提取元数据类型键-值对的轻量级机制。它通常用来存放应用程序偏好,例如一个应用程序启动时所使用的默认问候或文本字体。通过调用Context.getSharedPreferences() 来读写数值。如果你想分享给应用程序中的其它组件,可以为你的偏好集分配一个名字,或者使用没有名字的Activity.getPreferences()方法来保持对于该调用程序的私有性。你不能跨应用程序共享偏好(除了使用一个内容提供器)。
下面是一个为计算器设置按键静音模式的例子:
import android.app.Activity;
import android.content.SharedPreferences;
public class Calc extends Activity {
public static final String PREFS_NAME = "MyPrefsFile";
. . .
@Override
protected void onCreate(Bundle state){
super.onCreate(state);
. . .
// Restore preferences
SharedPreferences settings = getSharedPreferences(PREFS_NAME, 0);//获取一个SharedPreferences对象。
boolean silent = settings.getBoolean("silentMode", false);//取出保存的selentMode
setSilent(silent);
}
@Override
protected void onStop(){
super.onStop();
// Save user preferences. We need an Editor object to
// make changes. All objects are from android.context.Context
SharedPreferences settings = getSharedPreferences(PREFS_NAME, 0);
SharedPreferences.Editor editor = settings.edit();//调用edit()方法使其处于可以编辑的状态
editor.putBoolean("silentMode", mSilentMode);使用putBoolean把silentMode的值保存起来
// Don't forget to commit your edits!!!
editor.commit();//用commit()方法提交即可保存
}
}
问题:SharedPreferences保存到哪里去了?
SharedPreferences是以XML的格式以文件的方式自动保存的,在DDMS中的File Explorer中展开到/data/data/<package name>/shared_prefs下,以上面这个为例,可以看到一个叫做SETTING_Infos.xml的文件
将其导出到设备中,可以打开这个文件,看到其代码内容为:
<?xml version='1.0' encoding='utf-8' standalone='yes' ?>
<map>
<string name="PASSWORD">Password</string>
<string name="NAME">IceskYsl</string>
</map>
文件Files
你可以直接在移动设备或可移动存储媒介里存放文件。缺省情况下,其它应用程序不能访问这些文件。
为了从文件中读取数据,可调用Context.openFileInput()方法并传递本地文件名和文件路径给它。该方法返回一个标准的Java FileInputStream 对象。为了写一个文件,可调用Context.openFileOutput()并传递文件名和路径,这个方法也返回FileOutputStream对象。从另外的应用程序中调用这些方法将不起作用,你只能访问本地文件。
如果你有一个静态文件需要在编译时打包进应用程序,你可以保存该文件在你项目中res/raw/myDataFile,然后使用Resources.openRawResource (R.raw.myDataFile)打开它。该方法返回一个InputStream对象,你可以使用它读取文件数据。
数据库Databases
Android API包含对创建和使用SQLite数据库的支持。每个数据库都是创建它的应用程序所私有的。
这个SQLiteDatabase对象代表了一个数据库并包含与之交互的方法-生成查询和管理数据。为了创建数据库,调用SQLiteDatabase.create()并同时子类化SQLiteOpenHelper。
作为支持SQLite数据库的一部分,Android暴露了数据库管理函数,这让你可以存储复杂的数据集合,这些数据被包装到有用的对象里。比如,Android为联系人信息定义了一个数据类型;它由很多字段组成,其中包括姓,名(字符串),地址信息和电话号码(也是字符串),照片(位图图像),以及更多其它个人信息。
Android装载了sqlite3数据工具, 利用这些工具你可以浏览表内容,运行SQL命令,并执行SQLite数据库上的其它有用的函数。请查阅检查数据库(Examine databases (sqlite3))得知如何运行这个程序。
所有的数据库,SQLite以及其它,都被保存在设备如下目录里:
/data/data/package_name/databases.
讨论创建多少表格,包含哪些字段以及它们之间如何连接超出了本文的范围,不过Android并没有引入任何在标准SQLite概念之外的限制。我们确实推荐包含一个自增长数值的关键域,作为一个唯一ID用来快速查找一个记录。这对于私有数据并不必要,但如果你实现了一个内容提供器,你必须包含这样一个唯一ID字段。请参见Content Providers文档以获取关于该字段的更多信息,以及NotePadProvider类(在NotePad例子代码里)中创建和组装一个新数据库的方法。你创建的任何数据库都将可以通过名字被应用程序中其它的类访问,但不能从应用程序外部访问。
网络Network
你也可以使用网络来存放和获取数据(当它可用时)。要进行网络操作,可使用如下程序包中的类:
• java.net.*
• android.net.*