Android数据持久化存储(一)


Android数据持久化存储共有四种方式,分别是文件存储、SharedPreferences、Sqlite数据库和ContentProvider。在本篇幅中只介绍前面三种存储方式,因为ContentProvider属于android四大组件之一,所以它的数据存储方式在介绍四大组件的时候说明。

1、文件存储

文件存储不对存储的内容进行任何的格式化处理,所有数据都是原封不动地保存到文件当中的,因而它比较适合用于存储一些简单的文本数据或二进制数据。

文件存储有两种方式,一是存储到手机内存中(memory),一是存储到sd卡中。

1.1、存储到手机内存中

1)将数据存储到文件中

Context类中提供了一个openFileOutput()方法,可以用于将数据存储到指定的文件中。

方法介绍:

openFileOutput(name,mode):

name:是文件名,注意这里指定的文件名不可以包含路径,因为所有的文件都是默认存储到/data/data//files/(等价于getFilesDir())目录下的

mode:是文件的操作模式,

MODE_PRIVATE:默认的操作模式,同名文件内容会被覆盖。

MODE_APPEND则表示如果该文件已存在就往文件里面追加内容,不存在就创建新文件。

还有另外两种(android4.2被废弃),MODE_WORLD_READABLE和MODE_WORLD_WRITEABLE,这两种模式表示允许其他的应用程序对我们程序中的文件进行读写操作。

openFileOutput()方法返回的是一个FileOutputStream对象,得到了这个对象之后就可以使用Java流的方式将数据写入到文件中了。

publicvoidsave(){

Stringdata="Datatosave";

FileOutputStreamout=null;

BufferedWriterwriter=null;

try{

out=openFileOutput("data",Context.MODE_PRIVATE);

writer=newBufferedWriter(newOutputStreamWriter(out));

writer.write(data);

}catch(IOExceptione){

e.printStackTrace();

}finally{

try{

if(writer!=null){

writer.close();

}

}catch(IOExceptione){}

}

}

}

以上代码就是将将一段文本内容保存到文件中。

完成将数据存储到文件中,接下来如何从文件中读取数据呢?

2)从文件中读取数据

Context类中还提供了一个openFileInput()方法,用于从文件中读取数据。

这个方法只接收一个参数,即要读取的文件名,使用openFileInput("filename")读取存放在/data/data//files目录应用私有的文件。该方法返回一个FileInputStream对象,得到了这个对象之后再通过Java流的方式就可以将数据读取出来了。

publicStringload(){

FileInputStreamin=null;

BufferedReaderreader=null;

StringBuildercontent=newStringBuilder();

try{

in=openFileInput("data");

reader=newBufferedReader(newInputStreamReader(in));

Stringline="";

while((line=reader.readLine())!=null){

content.append(line);

}

}catch(IOExceptione){

e.printStackTrace();

}finally{

if(reader!=null){

try{

reader.close();

}catch(IOExceptione){

e.printStackTrace();

}

}

}

returncontent.toString();

}

1.2、存储到sd卡中

前面说了file储存,数据是存储在应用程序内的,文件大小受到限制;当要存储大的文件时,就要使用sd卡方式存储了。使用sd卡存储主要是使用Environment这个类的功能。

1)将数据存储到sd卡中

Environment类介绍:

①常用常量:

StringMEDIA_MOUNTED:外部存储器可读可写

MEDIA_MOUNTED_READ_ONLY:外部存储器只读

②常用方法:

getExternalStorageDirectory():获取SDCard的目录,/mnt/sdcard

getExternalStorageState():获取外部存储器的当前状态

通过简单价绍Environment类的使用,接下来就是sd卡读写数据了。以下是往sd卡读写数据的步骤:

1)判断sd卡的状态

2)获取存储器的目录,即sd卡的目录

3)使用IO流对sd卡进行文件的读写操作

4)在清单文件中添加权限

写入数据到sd卡中代码如下:

publicvoidwrite2Sd(){

if(Environment.getExternalStorageState().equals(

Environment.MEDIA_MOUNTED)){

 

Stringcontent=edtContent.getText().toString();

 

try{

 

//获取SDcard路径

FilesdCardDir=Environment

.getExternalStorageDirectory();

 

//SDCard目录:/mnt/sdcard

StringsdcardPath=sdCardDir.getAbsolutePath();

 

Filefile=newFile(sdCardDir,FILE_NAME);

//Filefile=newFile(sdcardPath

//+File.separator+FILE_NAME);

//以指定文件创建RandomAccessFile对象

RandomAccessFileraf=newRandomAccessFile(file,"rw");

//将文件记录指针移动最后

raf.seek(file.length());

//输出文件内容

raf.write(content.getBytes());

raf.close();

 

}catch(Exceptione){

//TODO:handleexception

}

}

}

 

以上代码可知,往sd卡中写数据先调用getExternalStorageState()判断sd卡状态,接着获取sd卡的存储路径,以路径很文件名创建文件,最后使用RandomAccessFile类存储数据,使用该类的好处是可以定位的指定位置进行读写数据。

2)从sd卡中读取数据

publicvoidreadFromSd(){

if(Environment.getExternalStorageState().equals(

Environment.MEDIA_MOUNTED)){

//获取SDcard路径

 

StringBuildersb=newStringBuilder();

try{

FilesdCardDir=Environment

.getExternalStorageDirectory();

Filefile=newFile(sdCardDir,FILE_NAME);

 

InputStreaminputStream=newFileInputStream(file);

intlen=0;

byte[]buffer=newbyte[1024];

while((len=inputStream.read(buffer))!=-1){

 

sb.append(newString(buffer,0,len));

}

tvResult.setText(sb.toString());

//关闭流

inputStream.close();

 

}catch(Exceptione){

//TODOAuto-generatedcatchblock

e.printStackTrace();

}

}

}

2、SharedPreferences存储

2.1、将数据存储到sp中

SharedPreferences类,它是一个轻量级的存储类,适合用于保存软件配置参数。SharedPreferences是使用键值对的方式来存储数据的,存储数据是以xml文件形式存储,文件存放在/data/data//shared_prefs目录下。

步骤:

1)获取SharedPreferences对象。

要想使用sp来存储数据,首先需要获取到sp对象。Android中主要提供了三种方法用于得到sp对象。

1、Context类中的getSharedPreferences(name,mode)方法

Name:用于指定SharedPreferences文件的名称

mode指定操作模式,和文件存储模式一样。

2、Activity类中的getPreferences(mode)方法

使用这个方法时会自动将当前活动的类名作为SharedPreferences的文件名。

3.PreferenceManager类中的getDefaultSharedPreferences(context)方法 这是一个静态方法,使用当前应用程序的包名作为前缀名来命名文件。

2)调用sp对象的edit()方法来获取一个SharedPreferences.Editor对象。

3)向SharedPreferences.Editor对象中添加数据。比如添加一个布尔型数据就使用putBoolean方法,添加一个字符串则使用putString()方法,以此类推。

4)调用commit()方法将添加的数据提交,从而完成数据存储操作

2.2、从sp中读取数据

步骤:

1)获取SharedPreferences对象。

2)然后分别调用它的geXxx()方法获取存储的值。如getString()、getInt()和getBoolean(),没有找到相应的值就会使用方法中传入的默认值来代替

3、Sqlite数据库存储

SQLite是一款轻量级的关系型数据库,它的运算速度非常快,占用资源很少,通常只需要几百K的内存就足够了。SQLite不仅支持标准的SQL语法,还遵循了数据库的ACID事务。

Android提供了一个SQLiteOpenHelper帮助类,借助这个类就可以非常简单地对数据库进行创建和升级。

SQLiteOpenHelper是一个抽象类,有两个抽象方法,分别是onCreate()和onUpgrade(),分别在这两个方法中去实现创建、升级数据库的逻辑。SQLiteOpenHelper中还有两个实例方法,getReadableDatabase()和getWritableDatabase()。这两个方法都可以创建或打开一个现有的数据库(如果数据库已存在则直接打开,否则创建一个新的数据库),并返回一个可对数据库进行读写操作的对象。

3.1、创建数据库

因为SQLiteOpenHelper是一个抽象类,创建数据库的时候可以创建一个类继承SQLiteOpenHelper,并重写其两个抽象方法。新建MyDatabaseHelper类继承自SQLiteOpenHelper,代码如下所示:

publicclassMyDatabaseHelperextendsSQLiteOpenHelper{

publicstaticfinalStringCREATE_BOOK="createtablebook("

+"idintegerprimarykeyautoincrement,"

+"authortext,"

+"pricereal,"

+"pagesinteger,nametext)";

privateContextmContext;

 

publicMyDatabaseHelper(Contextcontext,Stringname,

CursorFactoryfactory,intversion){

super(context,name,factory,version);

mContext=context;

}

 

@Override

publicvoidonCreate(SQLiteDatabasedb){

db.execSQL(CREATE_BOOK);

Toast.makeText(mContext,"Createsucceeded",Toast.LENGTH_SHORT).show();

}

 

@Override

publicvoidonUpgrade(SQLiteDatabasedb,intoldVersion,intnewVersion){

}

}

如上所示,先把建表语句写成字符串常量,在onCreate()方法中调用SQLiteDatabase的execSQL()方法去执行这条建表语句,以保证数据库创建的同时也成功的创建Book表。

然后再布局文件加入了一个id是.create_database的Button按钮,用于创建数据库。最后修改MainActivity中的代码,如下所示:

publicclassMainActivityextendsActivity{

privateMyDatabaseHelperdbHelper;

 

@Override

protectedvoidonCreate(BundlesavedInstanceState){

super.onCreate(savedInstanceState);

setContentView(R.layout.activity_main);

dbHelper=newMyDatabaseHelper(this,"BookStore.db",null,1);

ButtoncreateDatabase=(Button)findViewById(R.id.create_database);

createDatabase.setOnClickListener(newOnClickListener(){

@Override

publicvoidonClick(Viewv){

dbHelper.getWritableDatabase();

}

});

}

}

如上所示,在onCreate()方法中构建了一个MyDatabaseHelper对象,并且通过构造函数的参数将数据库名指定为BookStore.db,版本号指定为1,然后在Createdatabase按钮的点击事件里调用了getWritableDatabase()方法。这样当第一次点击按钮时,就会检测到当前程序中并没有BookStore.db这个数据库,于是会创建该数据库并调用MyDatabaseHelper中的onCreate()方法同时也创建Book表。

3.2、升级数据库

MyDatabaseHelper中的onUpgrade()方法是用于对数据库进行升级的。

接着我们再添加一张Category表用于记录书籍的分类,修改MyDatabaseHelper代码如下:

publicclassMyDatabaseHelperextendsSQLiteOpenHelper{

publicstaticfinalStringCREATE_CATEGORY="createtableCategory("

+"idintegerprimarykeyautoincrement,"

+"category_nametext,"

+"category_codeinteger)";

.....

 

@Override

publicvoidonCreate(SQLiteDatabasedb){

db.execSQL(CREATE_BOOK);

db.execSQL(CREATE_CATEGORY);

}

 

@Override

publicvoidonUpgrade(SQLiteDatabasedb,intoldVersion,intnewVersion){

switch(oldVersion){

case1:

db.execSQL(CREATE_CATEGORY);

case2:

db.execSQL("altertableBookaddcolumncategory_idinteger");

default:

}

}

}

 

 

可以看到,这里在onUpgrade()方法中加入了一个switch判断,如果oldVersion等于1,就再创建一个CATEGORY表。接着只需要在创建MyDatabaseHelper时构造函数第四个参数改成大于1的整数即可。

紧接着,发现CATEGORY表少添加了category_id列,为了保留原有数据库的数据,只需在添加一个case语句,使用alter..add添加该列就行,注意case之间没有break。使用这种方式升级数据库就不会导致表中的数据丢失问题。

3.3、数据库的CRUD操作

数据库的crud其中C代表添加(Create),R代表查询(Retrieve),U代表更新(Update),D代表删除(Delete)。每一种操作又各自对应了一种SQL命令。

调用SQLiteOpenHelper的getReadableDatabase()或getWritableDatabase()

方法是可以用于创建和升级数据库的,这两个方法还都会返回一个SQLiteDatabase对象,借助这个对象就可以对数据进行CRUD操作。

1)C数据库的add操作

SQLiteDatabase中的insert(table,nullColumnHack,values)方法

Table:表名

nullColumnHack:通常用不到,直接传null。

Value:ContentValue,是map集合,表示待添加的数据。

publicvoidadd(){

SQLiteDatabasedb=dbHelper.getWritableDatabase();

ContentValuesvalues=newContentValues();//开始组装第一条数据

values.put("name","TheDaVinciCode");

values.put("author","DanBrown");

values.put("pages",454);

values.put("price",16.96);

db.insert("Book",null,values);

}

 

2)R数据库的query操作

SQLiteDatabase中的query(table,columns,selection,selectionArgs,groupBy,having,orderBy)方法:

table,表名

columns,指定去查询哪几列

selection,selectionArgs,指定约束条件和占位符,表示查询哪几行

groupBy,对查询结果进行groupby操作

having,对groupBy后的数据进行进一步过滤

orderBy,对查询的结果进行排序

 

publicvoidquery(){

SQLiteDatabasedb=dbHelper.getWritableDatabase();

 

//查询Book表中所有的数据

Cursorcursor=db.query("Book",null,null,null,null,null,null);

if(cursor.moveToFirst()){

do{

//遍历Cursor对象

Stringname=cursor.getString(cursor.getColumnIndex("name"));

Stringauthor=cursor.getString(cursor

.getColumnIndex("author"));

intpages=cursor.getInt(cursor.getColumnIndex("pages"));

doubleprice=cursor.getDouble(cursor.getColumnIndex("price"));

 

}while(cursor.moveToNext());

}

cursor.close();

}

 

3)U数据库的update操作

SQLiteDatabase中的update(table,values,whereClause,whereArgs)方法

Table:表名

Value:ContentValue,是map集合。

whereClause、whereArgs:指定第三和第四个参数用于指定修改哪些行

publicvoidupdate(){

SQLiteDatabasedb=dbHelper.getWritableDatabase();

 

ContentValuesvalues=newContentValues();

values.put("price",10.99);

db.update("Book",values,"name=?",

newString[]{"xiaoming"});

}

 

4)D数据库的delete操作

SQLiteDatabase中的delete(table,whereClause,whereArgs)

table,表名

whereClause,whereArgs:指定要删除哪些行,对应sql语句的where部分。

publicvoiddelete(){

SQLiteDatabasedb=dbHelper.getWritableDatabase();

 

db.delete("Book","pages>?",newString[]{"500"});

}

3.4、使用SQL操作数据库

Android中支持使用原生的sql语句执行数据库的增删改查操作。具体如下:

添加数据的方法如下:

db.execSQL("insertintoBook(name,author,pages,price)values(?,?,?,?)",newString[]{"TheDaVinciCode","DanBrown","454","16.96"});

更新数据的方法如下:

db.execSQL("updateBooksetprice=?wherename=?",newString[]{"10.99","TheDaVinciCode"});

删除数据的方法如下:

db.execSQL("deletefromBookwherepages>?",newString[]{"500"});

查询数据的方法如下:

db.rawQuery("select*fromBook",null);

3.5、使用LitePal框架完成Android数据库的存储

使用SQLiteOpenHelper有一个弊端,就是如果数据库中某一列已经没有用了,我想把这一列删除,使用SQLite是无法实现删除某一列的(开发中多是忽视它,反正以后也用不到,留着也占用不了多少空间),另外就是如果表之间存在关联关系,建表语句就会变得很复杂,因此使用LitePal来自动建立表关联又是一个非常不错的选择,我们不需要关心什么外键、中间表等实现的细节,只需要在对象中声明好它们相互之间的引用关系,LitePal就会自动在数据库表之间建立好相应的关联关系了。

注:虽然使用Litepal框架可以轻松实习表的关联,但表的关联知识了解下还是有必要的。多表设计数据库口诀:

一对一关联的实现方式是用外键,多对一关联的实现方式也是用外键(外键必须加载多方),多对多关联的实现方式是用中间表。

LitePal开源项目地址:https://github.com/LitePalFramework/LitePal


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值