安卓数据持久化:文件存储、SharedPreferences存储以及数据库存储

Android系统中主要提供了三种方式用于简单的实现数据持久化功能:

文件存储(手机自带的内存)、SharedPreferences存储以及数据库存储

当然还可以用sd卡存储

读入写出

下面是疯狂java讲义中的关于IO流的一些补充,回忆一下

1,文件存储

手机自带的内存,只能供当前应用程序访问,其他应用程序访问不了,程序卸载这些数据也会随着消失

原理:

基本是先获取一个文件的输出流,然后把信息write进去,最后关闭流

a,通过上下文类context的openFileOutput()方法获得一个FileOutputStream输出流

b,要写入的内容通过write()写到FileOutputStream对象

c,关闭流

openFileOutput()可以将数据存储到指定的文件中,方法有两个参数,第一个参数是文件名,不需要带上完整的路径,因为这里都是默认存储到data/data/file下面的

第二个参数是文件的操作模式,两种:MODE_PRIVATE和MODE_APPEND.前者是覆盖,后者是追加

(MODE_WORLD_READABLE和MODE_WORLD_WRITEABLE,前者是允许只读,后者是只写,但是都被抛弃,因为不安全)

private void save(String inputText ) {
           FileOutputStream fos = null;
           BufferedWriter writer = null;
            try {
               fos = openFileOutput( "data", Context.MODE_PRIVATE);
               writer = new BufferedWriter( new OutputStreamWriter(fos));
                writer.write( inputText);
           } catch (IOException e) {
                 // TODO Auto-generated catch block
                 e.printStackTrace();
           } finally {
                 try {
                      if( writer != null)
                            writer.close();
                }catch (IOException e) {
                      // TODO Auto-generated catch block
                      e.printStackTrace();
                }

主要的就是上面的三句话

openFileOutput打开一个输出流,然后构建一个字节缓冲流对象然后写数据,

此外还有几个方法需要特别注意一下 ,这几个方法对于文件关系提供了更好的支持,配合上面介绍的方式, 就可以对文件的数据进行常规的CRUD操作 (增删改查),方法如下:

File getFIlesDir ():获取文件系统的绝对路径。

boolean deleteFile(String name):删除一个指定文件名为name的文件。

String[] fileList() :当前应用内部存储路径下的所有文件名。

上一节中的保存数据到手机内存也有一些方法

 //帮助我们返回一个目录
  //context.getCacheDir ()的话就是保存到cache缓存文件夹下面
File file=new File(context.getFilesDir(), "userinfo.txt");
       FileOutputStream fos= new FileOutputStream( file);
            //zhangsan 123
            fos.write(( username+ "##"+ password).getBytes());
            fos.close();

也是可以保存数据的

---------------------

来看看读数据

读数据也是主要是用到一个打开输入的函数openFileInput

private void read( ) {
           FileInputStream fis= null;
           BufferedReader reader = null;

           StringBuilder sb=new StringBuilder();
            try {
               fis= openFileInput( "data");
             reader= new BufferedReader( new InputStreamWriter(fis));
               String line="";
               while((line=reader.readerLine())!=null){
                   sb.append(line)
                    }
           } catch (IOException e) {
                 // TODO Auto-generated catch block
                 e.printStackTrace();
           } finally {
                 try {
                      if( reader!= null)
                            reader.close();
                }catch (IOException e) {
                      // TODO Auto-generated catch block
                      e.printStackTrace();
                }

主界面中

String inputText = load();
if (!TextUtils.isEmpty(inputText)) {
edit.setText(inputText);
edit.setSelection(inputText.length());
Toast.makeText(this, "Restoring succeeded",
Toast.LENGTH_SHORT).show();
}

setSelection()方法将输入光标移动到文本的末尾位置以便再次输入

TextUtils.isEmpty()方法可以一次性进行两种空值的判断,当传入的字符串为空或者是null的时候都会返回true

总的来说文件存储不适合用于保存复杂的数据

------------------------------------------------------------------------------------------------------------------------

2,SharedPreferences存储

首先来看存储数据

SharedPreferences存储是利用键值对的方式来存储的,感觉有点类似map集合

要使用该方法来存储数据就要先获得一个SharedPreferences对象,有三种获取方法

a,Context上下文类中的getSharedPreferences

两个参数,第一个是指定文件的名称,不在就创建。目录也是在data/data/包名/shared_prefs目录下

第二个参数是操作模式。MODE_PRIVATE是默认的,只允许当前的应用程序对其进行操作,MODE_MULTI_PROCESS是一般用于有多个进程中对同一个SharedPreferences文件进行读写的情况,同样有MODE_WORLD_WRITEABLE MODE_WORLD_READABLE两个被废弃的模式

b,Activity类中的getPreferences

只有一个参数,也是操作模式,文件的名称是默认的当前活动的类名

c,PreferenceManager管理类中的getDefaultSharedPreferences()

管理类中的一个静态方法,接收一个context参数,自动把当前的包名作为文件命名的前缀

得到一个对象之后,有三步来进行数据的存储

1,调用对象的edit方法获得一个SharedPreferences.Editor对象

2,向.Editor对象添加数据,putBoolean()或者是putString(),,等等putXXX()方法

3,调用commit方法将添加的数据提交,完成数据的存储操作

看一个实例:

@Override
public void onClick(View v) {
      // TODO Auto-generated method stub
 SharedPreferences.Editor editor=getSharedPreferences( "data", MODE_PRIVATE).edit();
             editor.putString( "name", "hl174");
             editor.putInt( "age", 18);
             editor.putBoolean( "吃饭没", false );
             editor.commit();
           }
     });

最后看结果

最后的结果是以xml的方式存在内存中的,外面还是map标签,,呵呵

从SharedPreferences中读取数据

SharedPreferences提供了许多的get方法来进行获取数据,都和相应的put方法对应

get方法也是有两个参数,第一个“键”,第二个是键找不到值时候的默认值,自己设定把

还是继续取出上面存储的xml数据文件的数据

restore_button .setOnClickListener(new OnClickListener() {
            @Override
            public void onClick(View v) {
                 // TODO Auto-generated method stub
   SharedPreferences  pref=getSharedPreferences( "data", MODE_PRIVATE);

           String name= pref.getString( "name", "");
            int age= pref.getInt( "age", 0);
            boolean lunch= pref.getBoolean( "吃饭没", false );

           Log. d("MainActivity" , "名字是:" +name );
           Log. d("MainActivity" , "年龄是:" +age );
           Log. d("MainActivity" , "吃饭没:" +lunch );
           }
     });
     }

结果如下;

----------------------------------------------------------------------------------------------------------------

3,SQLite数据库

为方便管理数据库有一个专门的抽象类SQLiteOpenHelper,它有两个抽象的方法onCreate()和onUpdate()

此外还有两个实例方法,getReadableDatabase()和getWritableDatabase(),都可以创建或者是打开一个现有的数据库(存在就打开,不在就创建),返回一个可对数据库进行操作的对象

当数据库不可写入的时候,前者返回的是一个只能读的方式打开数据库,后者则会出现异常

数据库文件同样还是会放在data/data/包名/databases下面

一般步骤:

1,构建SQLiteOpenHelper的实例,也就是完成其构造函数(参数一是context,第二个是要创建的数据库的名字,第三个是cursor,一般是null,第四个是数据库的版本号)

2,再调用getReadableDatabase()或者getWritableDatabase()方法创建数据库

同时重写onCreate()方法,该方法也会得到执行

界面的操作就不解释,具体看代码

a,自建立一个MyDatabaseHelper实现抽象类SQLiteOpenHelper,里面有相应的建表数据,oncreate()被执行的时候表也会跟着建立起来

b,主活动中MyDatabaseHelper构造函数,然后getWritableDatabase()

public class MyDatabaseHelper extends SQLiteOpenHelper {
/*
补充一下建表的一些类型
integer ---整型
real-----浮点类型
text---文本类型
blob---二进制类型

*/
public static final String CREATE_BOOK= "create table book(id integer primary key autoincrement,"
           + "author text"
           + "price real"
           + "pages integer"
           + "name text)";
     private Context mContext ;

     public MyDatabaseHelper(Context context, String name,
                CursorFactory factory, int version) {
            super( context, name, factory, version);
            // TODO Auto-generated constructor stub
            mContext= context;
     }

     @Override
     public void onCreate(SQLiteDatabase db) {
            // TODO Auto-generated method stub
//执行建表语句
    db.execSQL(CREATE_BOOK);
    Toast.makeText(mContext , "数据库创建成功" , Toast.LENGTH_SHORT).show();
     }

     @Override
     public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
            // TODO Auto-generated method stub
     }
}

再看看主活动里面,非常简单主要就是  dbHelper. getWritableDatabase();

dbHelper= new MyDatabaseHelper( this, "BookStore.db", null, 1);
     Button createDatabase=(Button) findViewById(R.id.create_database );
     createDatabase.setOnClickListener( new OnClickListener() {

            @Override
            public void onClick(View v) {
                 // TODO Auto-generated method stub
                 dbHelper. getWritableDatabase();
           }
     });

-------------------------------------------------------------------------------------------------------------

补充一下abd shell的操作,主要是用来看SQLite数据库的操作

C:\Users\Administrator>adb shell
[email protected]_x86:/ # cd /data/data/com.example.databasetest/databases/
cd /data/data/com.example.databasetest/databases/
[email protected]_x86:/data/data/com.example.databasetest/databases # ls
ls
BookStore.db
BookStore.db-journal
[email protected]_x86:/data/data/com.example.databasetest/databases # sqlite3 BookSto
re.db
BookStore.db                                                                 <
SQLite version 3.7.11 2012-03-20 11:35:50
Enter ".help" for instructions
Enter SQL statements terminated with a ";"
sqlite> .table
.table
android_metadata  book
sqlite> .schema
.schema
CREATE TABLE android_metadata (locale TEXT);
CREATE TABLE book(id integer primary key autoincrement,author textprice realpage
s integername text);
sqlite> .exit
.exit
[email protected]_x86:/data/data/com.example.databasetest/databases # exit
exit

-----------------------------------------------------------------------------------------------------------------

基本上上面的操作就已经完整的建立了一个表了

但是当要添加一个表的时候,比如这样

public void onCreate(SQLiteDatabase db )
{

// TODO Auto-generated
method stub

db.execSQL( CREATE_BOOK);

db.execSQL( CREATE_CATEGORY );

Toast. makeText( mContext, "数据库创建成功" ,
Toast.LENGTH_SHORT ).show();

}

会发现不成功

因为数据库BookStore.db已经有了,所以不会再次去执行onCreate()语句

可以在onUpgrade()中添加语句

@Override
      public void onUpgrade(SQLiteDatabase db, int oldVersion, intnewVersion) {
            // TODO Auto-generated method stub
            db.execSQL( "drop table if exists book" );
            db.execSQL( "drop table if exists category" );
           onCreate( db);
     }

然后修改主MainActivity中的版本号,再次执行程序就ok了

-------------------------------------------------

上面完成了数据库的创建和升级,再看CRUD操作

关于CRUD操作,由于之前用到了getWritableDatabase或者是getReadableDatabase方法,这两个方法可以返回一个SQLiteDatabase对象,通过这个对象就可以操作数据库

该对象有一个Insert()方法,接收三个参数:第一个是表名,第二个是给未指定添加数据的情况下某些可为空的列赋null,第三个是ContentValue对象,该对象里面有一系列的get put方法,有点类似于SharedPreferences里面的的构造参数的方法

插入数据:Insert()

public long insert (String table, String nullColumnHack, ContentValues values)

Added in API level 1

Convenience method for inserting a row into the database.

Parameters
table the table to insert the row into
nullColumnHack optional; may be null. SQL doesn‘t allow inserting a completely empty row without naming at least one column name. If your provided values is
empty, no column names are known and an empty row can‘t be inserted. If not set to null, thenullColumnHack parameter provides the name of nullable column name to explicitly insert a
NULL into in the case where your values is empty.
values this map contains the initial column values for the row. The keys should be the column names and the values the column values
Returns
  • the row ID of the newly inserted row, or -1 if an error occurred
Button addData =(Button) findViewById(R.id. add_data);
      addData.setOnClickListener( new OnClickListener() {
            @Override
            public void onClick(View v) {
                 // TODO Auto-generated method stub
                SQLiteDatabase dbOperate= dbHelper.getWritableDatabase();
                ContentValues values= new ContentValues();
            //下面没有给表中的id赋值,因为在建表的时候,id是默认自动增长的
                 //添加第一条记录到Book
                 values.put( "name", "安卓入门之路" );
                 values.put( "author", "hl174");
                 values.put( "pages", 800);
                 values.put( "price", 50);
                 dbOperate.insert( "book", null, values);

                 values.clear();

                 //插入第二条记录到book
                 values.put( "name", "安卓精通" );
                 values.put( "author", "hl174");
                 values.put( "pages", 700);
                 values.put( "price", 45);
              dbOperate.insert( "book", null, values);
           }
     });

更新数据:

update():四个参数

public int update (String table, ContentValues values, String whereClause, String[]whereArgs)

Added in API level 1

Convenience method for updating rows in the database.

Parameters
table the table to update in
values a map from column names to new column values. null is a valid value that will be translated to NULL.
whereClause the optional WHERE clause to apply when updating. Passing null will update all rows.
whereArgs You may include ?s in the where clause, which will be replaced by the values from whereArgs. The values will be bound as Strings.
Retu rns
  • the number of rows affected

第一个参数:表名  第二个参数:ContentValues对象,

最后两个参数是用来约束更新某一行或几行中的数据,不指定的话就是更新所有行

//更新数据
     Button updateData=(Button) findViewById(R.id. update_data);
      updateData.setOnClickListener( new OnClickListener() {
            @Override
            public void onClick(View v) {
                 // TODO Auto-generated method stub
                SQLiteDatabase db= dbHelper.getWritableDatabase();
                ContentValues values= new ContentValues();
                 values.put( "price", 10000);

        db.update( "book", values, "name=?", new String[]{"安卓入门之路" });
           }
     });

上面第三个参数对应的是类似sql中的where语句,?是一个占位符,第四个参数的内容可以替换掉该占位符

删除数据

很明显,删除数据就是delete()方法了

public int delete (String table, String whereClause, String[] whereArgs)

Added in API level 1

Convenience method for deleting rows in the database.

Parameters
table the table to delete from
whereClause the optional WHERE clause to apply when deleting. Passing null will delete all rows.
whereArgs You may include ?s in the where clause, which will be replaced by the values from whereArgs. The values will be bound as Strings.
Returns
  • the number of rows affected if a whereClause is passed in, 0 otherwise. To remove all rows and get a count pass "1" as the whereClause.

三个参数:第一个是表名

第二个第三个同样是进行约束的条件行

//删除数据
     Button deleteData=(Button) findViewById(R.id. delete_data);
      deleteData.setOnClickListener( new OnClickListener() {
            @Override
            public void onClick(View v) {
                 // TODO Auto-generated method stub
                SQLiteDatabase db= dbHelper.getWritableDatabase();
                 //删除页数大于500的记录
                 db.delete( "book", "pages>?", new String[]{"500" });
           }
     });

查询语句

CRUD中其实用的最多的还是查询语句

同样SQLiteDatabase也提供了query()方法,但是这个方法有好多个参数的类型,看最少的参数为7个

public Cursor query (String table, String[] columns, String selection, String[]selectionArgs, String groupBy, String having, String orderBy)

Added in API level 1

Query the given table, returning a Cursor over
the result set.

Parameters
table The table name to compile the query against.
columns A list of which columns to return. Passing null will return all columns, which is discouraged to prevent reading data from storage that isn‘t going to be used.
selection A filter declaring which rows to return, formatted as an SQL WHERE clause (excluding the WHERE itself). Passing null will return all rows for the given table.
selectionArgs You may include ?s in selection, which will be replaced by the values from selectionArgs, in order that they appear in the selection. The values will be bound as Strings.
groupBy A filter declaring how to group rows, formatted as an SQL GROUP BY clause (excluding the GROUP BY itself). Passing null will cause the rows to not be grouped.
having A filter declare which row groups to include in the cursor, if row grouping is being used, formatted as an SQL HAVING clause (excluding the HAVING itself). Passing null will cause all row groups to be included, and is required when row grouping is not being
used.
orderBy How to order the rows, formatted as an SQL ORDER BY clause (excluding the ORDER BY itself). Passing null will use the default sort order, which may be unordered.
Returns
  • Cursor object,
    which is positioned before the first entry. Note that Cursors
    are not synchronized, see the documentation for more details.
See Also

1:表名

2:要查询的是哪几列,不指定的话就是默认查询所有列

3、4:约束某一行或者是某几行的数据,不指定则默认是查询所有的数据

5,groupby指定的列,不指定这项参数就相当于跳过

6,having是对group之后的数据进行过滤

7可以指定查询结果的排序方式

当然调用query之后会返回一个Cursor对象,查询到的数据都将从这个对象中取出

public void onClick(View v ) {
          // TODO Auto-generated method stub
       SQLiteDatabase db= dbHelper.getWritableDatabase();
        //查询book表中的所有数据
 Cursor cursor=  db.query( "book", null, null, null, null, null, null);
   if( cursor.moveToFirst()){
          do{
             //遍历cursor对象,取出数据并打印
         String name= cursor.getString( cursor.getColumnIndex( "name"));
       String author= cursor.getString( cursor.getColumnIndex( "author"));
     int pages= cursor.getInt( cursor.getColumnIndex( "pages"));
     double price= cursor.getDouble( cursor.getColumnIndex( "price"));

         Log. d("MainActivity" , "书名是:" +name );
         Log. d("MainActivity" , "书的作者是:" +author );
         Log. d("MainActivity" ,"书的页数是:" +pages );
         Log. d("MainActivity" , "书的价钱是:" +price );
          } while( cursor.moveToNext());
         }
     cursor.close();
    }
  });

首先是通过db.query得到cursor对象,在cursor类中我们发现了下面的一些方法,getString,getDouble,getInt等但是都需要传一个参数进去,也就是所在列的下标,从0开始计数

要得到相应列所在的下标同样有方法getColumnIndex(String columnName),这样的话先得到列所在的下标,然后通过该下标得到相应的记录值

public abstract int getColumnIndex (String columnName)

Added in API level 1

Returns the zero-based index for the given column name, or -1 if the column doesn‘t exist. If you expect the column to exist use getColumnIndexOrThrow(String) instead,
which will make the error more clear.

Parameters
columnName the name of the target column.
Returns
  • the zero-based column index for the given column name, or -1 if the column name does not exist.
See Also

结果如下:

时间: 2024-10-10 05:44:20

安卓数据持久化:文件存储、SharedPreferences存储以及数据库存储的相关文章

iOS数据持久化之二——归档与设计可存储化的数据模型基类

iOS数据持久化之二--归档与设计可存储化的数据模型基类 一.引言 在上一篇博客中,我们介绍了用plist文件进行数据持久化的方法.虽然简单易用,但随着开发的深入,你会发现,这种方式还是有很大的局限性.试想,如果我们可以将用户的登录返回信息模型,游戏中角色的属性信息模型进行直接的持久化存取,那是不是非常爽的事,幸运的是,我们可以通过归档,来设计一个这样的数据模型. 二.先来精通归档吧 归档也是iOS提供给开发者的一种数据存储的方式,事实上,几乎所有的数据类型都可以通过归档来进行存取.其存储与读取

Python3网络爬虫实战-33、数据存储:非关系型数据库存储:MongoDB

NoSQL,全称 Not Only SQL,意为不仅仅是 SQL,泛指非关系型的数据库.NoSQL 是基于键值对的,而且不需要经过 SQL 层的解析,数据之间没有耦合性,性能非常高. 非关系型数据库又可以细分如下: 键值存储数据库,代表有 Redis, Voldemort, Oracle BDB 等. 列存储数据库,代表有 Cassandra, HBase, Riak 等. 文档型数据库,代表有 CouchDB, MongoDB 等. 图形数据库,代表有 Neo4J, InfoGrid, Inf

DataPersistence(数据持久化: 文件读写, NSUserDefault, 归档)

 DataPersistence(数据持久化) 1.数据持久化?   数据永久的保存 2.为什么要做数据持久化?   要把之前的操作(存档, 记录等)保存起来 3.为什么数据之前保存不了呢?   以前的数据都存在内存中, 程序一旦终止, 内存就会被收回 4.数据保存到哪里, 才能做数据持久化   把数据存到硬盘里 5.存到硬盘的哪个位置呢?   沙盒机制: iOS会为每一个应用, 单独创建一个文件夹(沙盒), 这个文件夹只允许当前应用访问 6.如何通过代码获取路径? 7.沙盒文件夹又分了多个子文

【IOS开发之Objective-C】数据持久化--文件和归档

在OC中数据持久化的方式有多种,例如:写入一个文件再或是归档.写入文件的方式我们一般都可以直接读取文件中的数据.比如我把数据写进一个XX.txt文档里面.只有数据写入成功了我就可以直接读取这个文件了.但是这样对于一些用户关键的数据并不能起到保密的作用,如果需要保密还需要使用归档操作. 文件的操作 //首先声明一个字符串 NSString * str1 = @"123456789"; 方式一 直接使用方法: - (BOOL)writeToFile:(NSString *)path ato

数据存储之非关系型数据库存储----MongoDB存储

MongoDB存储----文档型数据库 利用pymongo连接MongoDB import pymongo client = pymongo.MongoClient(host='localhost', port=27017) # 或 pymongo.MongoClient('mongodb://localhost:23017/') # 默认端口为:27017 # pymongo.MongoClient()方法 指定数据库 # 指定操作test数据库# db = client.test 或 db

android小功能实现之简单数据持久化保存(SharedPreferences)

为了保存一些简单的配置,类似iOS的NSUserDefault和cocos2dx的CCUserDefault,Android提供了SharedPreferences. 一 布局 先看效果图: 打开main.xml修改内容如下: <?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/andr

安卓数据持久化工具类总结

程序猿是最懒的生物,开发中从不重复造轮子,实际开发中数据吃就化是必然要处理的一个问题,先总结了几个除处理sqlite外的几个工具类,因为sqlite可以直接用orm,持久化数据有I/O,SharedPreference等等方式. 外置储存卡 package cn.edu.zafu.utils; import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import java

Python3网络爬虫实战-34、数据存储:非关系型数据库存储:Redis

Redis 是一个基于内存的高效的键值型非关系型数据库,存取效率极高,而且支持多种存储数据结构,使用也非常简单,在本节我们介绍一下 Python 的 Redis 操作,主要介绍 RedisPy 这个库的用法. 1. 准备工作 在本节开始之前请确保已经安装好了 Redis 及 RedisPy库,如果要做数据导入导出操作的话还需要安装 RedisDump,如没有安装可以参考第一章的安装说明. 2. Redis.StrictRedis RedisPy 库提供两个类 Redis 和 StrictRedi

python爬虫11--文件存储之非关系型数据库存储MongoDB

NoSQL,Not Only SQL,不仅仅是SQL,泛指非关系型数据库,基于键值对的,不需要经过SQL层的解析,数据之间没有耦合性,性能高. 非关系型数据库细分如下: 键值存储数据库:Redis.Voldemort.Oracle BDB: 列表存储数据库:Cassandra.HBase.Riak: 文档型数据库:CouchDB.MongoDB: 图形数据库:Neo4j.InfoGrid.Infinite Graph. 爬虫数据使用非关系型数据库原因:简单高效.爬虫数据可能存在某些字段提取失败或