RSS Reader实例开发之存储本地数据

        存储本地数据

        OPhone系统为应用程序提供了多种存储本地数据的方式,包括文件和数据库。为了保证安全,每个应用程序都有自己独立的存储区,其他应用程序无法直接访问。此外,大数据还可以存储在手机的SD卡内,存储在SD卡的数据可以被所有应用程序读写。
 

       受到无线网络的速度限制,RSS Reader需要把抓取的内容存储到本地,以便快速显示给用户,这个功能利用数据库存储最容易实现。此外,RSS Reader还需要保存用户的设置。下面,我们分别来编写这两个功能。

  • 使用SQLite数据库

       和其他智能手机平台相比,OPhone平台提供了一个非常完善的数据库存储机制。OPhone系统使用SQLite作为内置的数据库引擎,应用程序可以非常方便地使用数据库来存储数据,并使用标准的SQL语言来查询和修改数据,极大地增强了应用程序的能力和灵活性。

       SQLite是用C语言编写的微型数据库引擎,特点是小巧,速度快,以单一文件存储数据库数据,非常适合移动平台。OPhone系统已经对SQLite进行了封装,提供了简单的Java接口。
在使用SQLite时,我们还需要注意:


1. SQLite不支持外键,因此,一对多关系需要由应用程序自身维护;
2. SQLite不支持JDBC接口,需要熟悉OPhone提供的API,好消息是API接口相当简单;
3. SQLite仅支持几种数据类型:INTEGER,REAL,TEXT和BLOB等;
4. SQLite不严格区分数据的类型,例如存储的是INTEGER照样可以用getString()读出来。
 

           RSS Reader需要处理两种类型的数据:RSS订阅源和RSS的Item,因此,我们建立Subscription和Item这两种类型的JavaBean,并创建对应的表结构。
 

          Subscription对应的表subs:

 

列名
类型
描述
_id
long
自增主键
url
text
RSS的URL地址
title
text
RSS的标题
description
text
RSS的描述
last_updated
long
最后更新时间
frequency
long
抓取频率

 

            Item对应的表item:

 

列名
类型
描述
_id
long
自增主键
subs_id
long
模拟外键,关联Subscription
unread
long
是否未读,1=未读,0=已读
url
text
Item的URL地址
title
text
Item的标题
author
text
Item的作者
published
text
Item的发布时间
content
text
Item的内容

 

        SQLite支持long类型的自增主键,且OPhone的系统应用程序均默认使用该类型主键。为了与OPhone的系统应用程序和其他应用程序保持一致的主键规则,应当首先考虑使用这种类型的主键。此外,OPhone系统默认的主键名称是“_id”,该常量定义在android.provider.BaseColumns._ID,根据一致性原则,我们自定义的SubscriptionColumns和ItemColumns应当从BaseColumns派生,并直接使用常量_ID作为主键名称。
 

         SubscriptionColumns定义如下:

 ItemColumns定义如下:

SQLite数据库API的主要接口就是SQLiteDatabase对象,提供了创建和删除数据库、创建和删除表以及最常用的增删改查的全部功能。要使用SQLite数据库,可以直接创建SQLiteDatabase对象,还可以利用OPhone系统提供的SQLiteOpenHelper对象更方便地操作SQLite数据库。
 

         使用SQLiteOpenHelper时,我们需要从SQLiteOpenHelper派生一个子类,并提供一个显示的构造方法:

在构造方法中,需要传入数据库文件名“reader.db”,该文件由于放在RSS Reader应用程序的私有目录下,并且只能被RSS Reader应用程序访问,所以不存在文件名冲突。最后一个参数1表示版本号。
 

         当第一次运行RSS Reader时,数据库并不存在,此时,SQLiteOpenHelper会自动为我们创建数据库,然后,onCreate()方法将被调用,我们需要覆写该方法,创建所有的表和索引,并插入初始数据:

当RSS Reader升级后,例如,从版本1升级到版本2时,SQLiteOpenHelper检查到已存在的数据库版本和升级版本不同,就会调用onUpgrade()方法,让应用程序自己更新旧版本的数据。我们需要覆写该方法,并编写升级数据的逻辑。在RSS Reader中,我们用最简单的方法,即删除旧的表,然后再创建新表:

  升级数据库的逻辑仅在新版本和旧版本使用了不同的数据库表结构时才需要。
有了ReadingOpenHelper,创建和升级数据库的功能就实现了。下一步,我们编写一个ReadingOperator类,它持有一个ReadingOpenHelper和一个SQLiteDatabase对象:

注意到SQLiteDatabase是通过ReadingOpenHelper的getWritableDatabase()获取的,表示该SQLiteDatabase可读写。如果你只需要读取而不需要写入数据库,可以用getReadableDatabase()获取。SQLiteDatabase对象不需要反复打开和关闭,通常我们在应用程序启动时打开,在应用程序结束时关闭,在应用程序的生命周期内,可以随时通过getCurrentDb()方法获取SQLiteDatabase对象。

  • 查询数据

        除了可以直接构造SQL语句查询外,更简单常用的是使用SQLiteDatabase提供的query()方法,该方法定义如下:

如果要循环Cursor获得所有的行数据,使用如下代码:

  • 插入数据

        插入数据需要使用SQLiteDatabase提供的insert()方法,例如,插入一个Subscription的代码如下:

  insert()方法需要传入一个ContentValues对象,SQLiteDatabase会根据ContentValues对象构造出“INSERT table (column list) VALUES( value list)”语句。
 

  • 更新数据

       更新数据和插入数据类似,把需要更新的字段放入ContentValues中,然后调用SQLiteDatabase的update()方法,并传入where语句和可选的参数即可。例如:

  • 删除数据

        删除数据需要使用SQLiteDatabase的delete()方法,需要传入table名称和可选的where条件及参数:

通过SQLiteDatabase,我们就可以完成所有的增删改查操作。
 

  • 使用SharedPreferences存储应用程序设置

        当我们需要在应用程序中保存用户的一些设置时,由于这些数据基本上是以key-value形式表示的,用SQLite数据库就显得大材小用了,而且,查询起来比较麻烦。实际上,OPhone系统已经为我们提供了SharedPreferences来存储应用程序的设置。
 

        使用SharedPrefernces非常容易。在Activity中,直接调用getSharedPreferences()方法就可以获得SharedPrefernces的实例:

SharedPreferences pref = getSharedPreferences("app_pref", MODE_PRIVATE);  

 getSharedPreferences()方法需要两个参数:SharedPrefernces的名称和打开模式。默认的MODE_PRIVATE表示该SharedPrefernces仅仅能够被这个应用程序访问(包含该应用程序所有的组件),其他应用程序均无法访问。MODE_WORLD_READABLE和MODE_WORLD_WRITEABLE模式则表示允许其他应用程序以只读方式和可读写方式访问SharedPrefernces。
 

           然后,我们就可以通过SharedPreferences的getInt(),getBoolean()等方法获得对应的值,例如:
 int value = pref.getInt("key"3);  

 

 两个参数分别是key和默认值。当SharedPrefernces不存在该key时,将返回默认值。
需要保存一个设置时,通过SharedPreferences的edit()方法获得一个SharedPreferences.Editor的实例,然后,调用put方法设置key和对应的value,最后,不要忘记通过commit()方法将更改写入文件:

 

SharedPreferences.Editor editor = pref.edit(); editor.putBoolean("key1"true); editor.putInt("key2"123); editor.commit();  

 RSS Reader使用SharedPrefernces保存用户的3个设置:

 

描述
Key
类型
缺省值
是否仅显示未读项
UnreadOnly
boolean
true
刷新频率(分钟)
Freq
int
3
删除过期项(周)
Expires
int
3

 

        除了使用SharedPrefernces外,对于一个单独的Activity,还可以通过Bundle保存或读取Activity的一些状态,Bundle实例是通过Activity的onCreate()方法参数传递给Activity的,用Bundle保存的数据只能被这个Activity访问,适合保存Activity自身的状态。
 

  • 使用ContentProvider

        我们已经知道,每个OPhone应用程序都有自己独立的存储区,如果应用程序A需要访问应用程序B的数据时,就需要应用程序B提供对外的数据接口。在OPhone平台中,任何应用程序都可以通过ContentProvider对外提供数据访问接口。
 

        ContentProvider为OPhone应用程序定义了一套统一的数据访问接口,通过URI进行数据操作。例如,OPhone系统的Contacts系统应用程序就提供了访问联系人的ContentProvider。要列出所有联系人,使用的URI为“content://contacts/people”,要获得ID为1的某个联系人,使用的URI为“content://contacts/people/1”。所有的查询和修改操作都可以通过ContentResolver完成。
 

      通过ContentProvider机制访问第三方应用程序数据的好处在于,无需了解数据是由谁提供的,以及数据的存储方式,所有应用程序都使用统一的接口访问其他应用程序的数据,应用程序之间的耦合度极低。
 

       以OPhone系统自带的Contacts应用程序为例,我们来通过ContentProvider读取手机联系人信息:

 

  ContentResolver通过Activity的getContentResolver()获得,然后,根据联系人的URI查询,该URI常量定义在android.provider.Contacts.People.CONTENT_URI中,得到游标对象Cursor,类似JDBC的ResultSet。循环Cursor就可以读取所有的联系人信息。
 

       ContentResolver还提供insert()、update()和delete()方法,可以完整地实现数据的增删改查操作。
封装ContentProvider
 

        RSS Reader也可以通过ContentProvider对外暴露数据访问接口,这样,其他应用程序就可以访问到Subscription和Item这两种数据。
要实现ContentProvider,我们首先要定义URI。在OPhone系统中,URI格式如下:

content://authority/entity/id  

 

其中,“content://”是OPhone系统定义的schema,无法改变,authority用于区分不同的ContentProvider,由小写字母和“.”构成,在系统中必须唯一,通常由域名和应用程序名构成(注意:OPhone系统应用程序的authority是简单的单词,例如contacts,但是第三方应用要避免简单单词形式的authority),entity是数据类型,若要查询某个ID的entity,则可以附加上“/id”。
 

        下面,我们开始为RSS Reader定义一个ContentProvider,供第三方应用程序使用。
首先,从android.content.ContentProvider派生一个ReadingProvider:

package org.expressme.wireless.reader.provider; public class ReadingProvider extends   ContentProvider { ... } 

 

其次,我们需要定义一个唯一的authority,可以通过ReadingProvider的完整类名获得:

public static final String AUTHORITY = ReadingProvider.class.getName().toLowerCase();  

我们需要暴露Subscription和Item这两种数据类型。根据ContentProvider的惯例,定义URI如下:

数据类型
URI
全部Subscription
content:// org.expressme.wireless.reader.provider.readingprovider/subscriptions
某个ID的Subscription
content:// org.expressme.wireless.reader.provider.readingprovider/subscriptions/#
全部Item
content:// org.expressme.wireless.reader.provider.readingprovider/items
某个ID的Item
content:// org.expressme.wireless.reader.provider.readingprovider/items/#

 

 

    当第三方应用程序通过URI对我们的数据进行增删改查操作时,相应的insert()、delete()、update()和query()方法就会执行。我们需要实现这4个方法,并且,根据不同的URI做不同的操作。

    我们不需要编写解析URI的代码,OPhone系统已经为我们提供了UriMatcher来匹配URI。只需要为以上4种URI定义好常量,添加匹配规则即可:

 

 以query()为例,根据传入的Uri,分别查询所有的Subscription,某个ID的Subscription,所有的Item和某个ID的Item:

 

 

 insert()和query()类似,但是,仅允许TYPE_ALL_SUBSCRIPTIONS和TYPE_ALL_ITEMS这两种类型,insert()和query()类似,但是,仅允许TYPE_ALL_SUBSCRIPTIONS和TYPE_ALL_ITEMS这两种类型,并且,需要返回新插入数据的Uri,因为需要向客户端返回包含ID的URI。使用静态方法ContentUris.withAppendedId就可以构造一个含ID的Uri对象:

 

当第三方应用程序使用我们的ContentProvider时,还需要知道URI、字段名称等信息,这些常量定义在SubscriptionColumns和ItemColumns中:

 

现在,我们已经实现了一个完整的ContentProvider,可以对外提供两种类型的数据访问接口。对于RSS Reader自身的Activity来说,需要做的代码重构就是使用ContentProvider来代替直接操作数据库的ReadingOperator,这样,就使得应用程序各组件之间的耦合程度更低了,此外,第三方应用程序也可以通过ContentProvider读取RSS Reader应用的数据,而无需知道数据的提供者和存储方式。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值