Android Learning:数据存储方案归纳与总结
前言
最近在学习《第一行android代码》和《疯狂android讲义》,我的感触是Android应用的本质其实就是数据的处理,包括数据的接收,存储,处理以及显示,我想针对这几环分别写一篇博客,记得我的学习心得,也希望跟各位新手同学相互努力促进。今天这篇博客,我想介绍一下数据的存储,因为数据的接收,存储,处理以及显示这几环环环相扣,而数据的存储直接关系到数据的处理和显示,所以显得尤为重要。
所以本文针对数据存储的常见方案和其使用进行了归纳。分为程序内存储和程序间数据访问,程序内存储介绍了文件存储、SharedPreferences存储、数据库存储三种不同的存储方式的优缺点以及区别,用用例子详细说明了各自的用法,重点介绍了数据库存储的操作。 程序间数据访问介绍了内容提供器。
Android程序内数据存储方案:
Android系统中主要提供了三种方式用于简单地实现数据持久化功能,即文件存储、SharedPreferences存储以及数据库存储。当然,除了这三种方式之外,还可以将数据保存在手机的SD卡中,不过使用文件存储、SharedPreferences存储以及数据库存储会相对于将数据保存在手机的SD卡中更安全也更简单一些[1]。
一、文件存储:
文件存储是Android中最基本的一种数据存储方式,它不对存储的内容进行任何的格式化处理,所有数据都是原封不动地保存到文件当中的,因而它比较适合用于存储一些简单的文本数据或二进制数据。如果你想使用文件存储的方式来保存一些较为复杂的文本数据,就需要定义一套自己的格式规范,这样方便于之后将数据从文件中重新解析出来。由于文件存储的方式有着起局限性,在实际工程项目中用的很少,我们也就不具体展开说。
二、SharedPreferences存储:
不同于文件的存储方式,SharedPreference是使用键值对的方式来存储数据的。也就是说当保存一条数据的时候,需要给这条数据提供一个对应的键,这样在读取数据的时候就可以通过这个键把相应的值取出来。而且SharedPreferences还支持多种不同的数据类型存储,这就是SharedPreference比文件存储的优越性所在。
三种获取SharedPreferences对象的方法
-
Context类中的getSharedPreferences()方法
SharedPreferences pref = getSharedPreferences("data", MODE_PRIVATE);
第一个参数用于指定SharedPreferences文件的名称。第二个参数指定操作模式,MODE_PRIVATE表示只有当前的应用程序才可以对这个SharedPreferences进行读写,MODE_MULTI_PROCESS则一般用于会有多个进程中对同一个SharedPreferences文件进行读写的情况。
-
Activity类中的getPreferences()方法
跟Context类中的getSharedPreferences()方法很相似,不过只接受一个操作模式参数
-
PreferenceManager类中的getDefaultSharedPreferences()方法
SharedPreferences pref = PreferenceManager.getDefaultSharedPreferences(this);
这是一个静态方法,接收Context参数。
例子:通过SharedPreferences进行读写数据
shareRead = (Button) findViewById(R.id.share_read); shareWrite = (Button) findViewById(R.id.share_write); /** * pref = PreferenceManager.getDefaultSharedPreferences(this); */ pref = getSharedPreferences("data", MODE_PRIVATE); editor = pref.edit(); shareWrite.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { editor.putString("write", "This is A String"); /*数据提交*/ editor.commit(); } }); shareRead.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { String str = pref.getString("write", null); Toast.makeText(MainActivity.this, str, Toast.LENGTH_LONG).show(); } });
-
存储数据(shareWrite)的实现
1. 调用SharedPreferences对象的Edit()方法获取一个SharedPreferences.Editor对象
2. 向SharedPreferences.Editor对象中添加数据,采用putBoolean、putString等方法
3. 调用commint()方法将添加的数据提交
-
获取数据(shareRead)的实现
1. 直接采用SharedPreferences对象的get方法
2. get方法接收两个参数,第一个是键,第二个参数是默认值,当传入的键找不大到对应的值,返回默认值
效果图
sharedPreferences对象与SQLite数据库相比,免去了创建数据库,创建表,写SQL语句等诸多操作,相对而言更加方便,简洁。但是SharedPreferences也有其自身缺陷,比如其只能存储boolean,int,float,long和String五种简单的数据类型,比如其无法进行条件查询等。所以不论SharedPreferences的数据存储操作是如何简单,它也只能是存储方式的一种补充,而无法完全替代如SQLite数据库这样的其他数据存储方式[2]。
三、数据库存储
创建数据库: SQLiteDatabase.execSQL()
public class MyDatabaseHelper extends SQLiteOpenHelper { /*建立一个student的数据表*/ public static final String CREATE_STUDENT = "create table student (" + "id integer primary key autoincrement," + "number integer," + "name text," + "major text)"; private Context myContext; public MyDatabaseHelper(Context context, String name, SQLiteDatabase.CursorFactory factory, int version) { super(context, name, factory, version); myContext = context; } @Override public void onCreate(SQLiteDatabase db) { /*创建数据库*/ db.execSQL(CREATE_STUDENT); } @Override public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { } }
更新数据库:onUpgrade()
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { /*drop table if exists student:发现数据库已经存在student这张表就删除*/ db.execSQL("drop table if exists student"); onCreate(db); }
当表中没有我们所要的数据时:
添加数据:insert(String table, String nullColumnHack, ContentValues values)
MyDatabaseHelper dbHelper = new MyDatabaseHelper(this, "student.db, null, 2"); SQLiteDatabase db = dbHelper. getWritableDatabase(); ContentValues values = new ContentValues(); values.put("number", 123456); values.put("name", "steve"); values.put("major", "CS"); db.insert("student", null, values);
当表中数据已经存在,但是数据需要修改:
更新数据:update(String table, ContentValues values, String whereClause, String[] whereArgs)
db.update("student", values, "name = ?", new String[]{"steve"});
删除数据:delete(String table, String whereClause, String[] whereArgs)
db.delete("student", "name = ?", new String[]{"steve"});
查询数据:
Cursor query(String table, String[] columns, String selection,String[] selectionArgs, String groupBy, String having,String orderBy)
Cursor rawQuery(String sql, String[] selectionArgs)
查询表
query()方法参数 | 对应SQL部分 | 描述 |
table | from table_name | 指定查询的表明 |
columns | select column1,column2 | 指定查询的列名 |
selection | where column = value | 指定where的约束条件 |
selectionArgs | - | 为where中的占位符提供具体的值 |
groupBy | group by column | 指定需要group by的列 |
having | having column = value | 对group by后的结果进一步约束 |
orderBy | order by column1, column2 | 指定查询结果的排序方式 |
Cursor cursor = db.query("student", null, "number = ?", new String[] {"123456"}, null, null, null);
到这里,我们可以看到update()、delete()、query()等方法中都传入了String[] whereArgs参数,这是一个字符串数组,也是判断条件,这可以帮助我们在同一个类目中搜索几类关键字的数据
-
使用事务:事务的特性可以保证让某一系列的操作要么全部完成,要么一个都不会完成
SQLiteDatabase中包含两个方法控制事务:
开始事务:beginTransaction()
结束事务:endTransaction()
是否在事务中:inTransaction()
设置事务标志:setTransactionSuccessful()
表示事物执行成功。如果程序在事务执行中调用该方法设置了事务成功则提交事务,否则程序将会回滚事务。
注意:
数据库查看,我们可以通过命令行方式进入SDK/platform-tools,输入adb shell,即可输入数据库的基本指令。但我觉得这种方式并不是很方便,我的做法是,登录SQLite Expert官方网站(http://www.sqliteexpert.com/download.html)下载一个SQLite Expert Personal 3,个人版的是免费的。然后就可以在Android Studio或者Eclipse中进如Android Monitor Device,在File Explorer中找到data/data/包名/database/*.db,点击导出,然后就可以用这个软件打开。
Android程序间数据内容提供器:
上述三种方案是单个程序数据的存储,而我们在有些场景中,需要用到系统应用的数据,比如使用系统的通讯录、短信和媒体库等功能,那这就需要我们使用内容提供器(Content Provider),它可以用于在不同的应用程序之间实现数据共享。
一、获取其他应用的数据
内容提供器是通过ContentResolver类来访问内容提供器中共享的数据,ContentResolver是由Context.getContentResolver() 得到的。
Cursor cursor = getContentResolver().query( uri, projection, selection, selectionArgs, sortOrder) ;
查询表
query()方法参数 | 对应SQL部分 | 描述 |
uri | from table_name | 指定查询的表明 |
projection | where column = value | 指定where的约束条件 |
selection | - | 为where中的占位符提供具体的值 |
selectionArgs | group by column | 指定需要group by的列 |
orderBy | order by column1, column2 | 指定查询结果的排序方式 |
讲到这里,我们会发现,内容提供器的查询方法跟数据库很类似,使用query()方法,不同的是,传进query()方法的参数有所区别。内容提供器传进query的值包含着URI,而URI记录着目标数据的地址,而数据库传进query的值包含着表名,因为数据库是对自身程序文件目录下进行的操作,所以并不需要定位,而内容提供器首先要定位到程序的包名,进而定位到数据文件夹,最终定位到表名,在URI中,我们可以看到最后一个斜杠后就是表名。
-
内容URI
URI的标准格式
content://com.example.app.provider/table
URI的解析
Uri uri = Uri.parse("content://com.example.app.provider/table")
URI规则
* 表示匹配任意长度的任意字符
content://com.example.app.provider/*
# 表示匹配任意长度的数字
content://com.example.app.provider/table/#
二、提供应用自身的数据
-
内容提供器的六个方法
1、onCreate()
初始化内容提供器。完成数据库的创建和升级,只有在ContentResolver尝试访问程序数据才会被初始化
2、query()
查询数据。uri确定查哪张表,projection查询列,selection和selectionArgs查询哪些行
3、insert()
添加数据
4、update()
更新内容提供器已有的数据
5、delete()
删除数据
6、getType()
根据传入的内容URI来返回相应的MIME类型。
一个内容URI对应的MIME字符串只要有三部分组成
(1)必须以vnd开头
(2)以路径结尾,后接android.cursor.dir/,以id结尾,后接android.cursor.item/
(3)最后接上vnd.<authority>.<path>
-
匹配内容URI
UriMatcher类
addURI()方法,这个方法接受三个参数,分别把权限、路径和一个自定义代码传进去
match()方法,将Uri对象传入,返回值是某个能够匹配这个Uri对象说对应的自定义代码
内容提供类的核心是通过URI对象来定位数据的,但数据定位成功之后,那么就可以用类似于数据库操作的手段进行读取。如果想共享自身的数据,也可以通过URI对象,抛出自己的位置,等待其他程序定位到自身获取数据
总结:
存储方式 | 用途 | 特点 |
文件存储 | 存储一些简单的文本数据或二进制数据 | 不对存储的内容进行任何的格式化处理 |
SharedPreferences存储 | 存储boolean,int,float,long和String五种简单的数据类型,数据量小 | 方便,简洁,使用键值对的方式来存储数据,支持多种不同的数据类型存储,但不能条件查询 |
数据库存储 | 数据量大,且有条件 | 功能强大,条件查询,数据的增删改查,但使用较为复杂 |
内容提供器 | 跨程序之间的数据交换 | 通过URI对象来定位数据源 |
[1] 郭霖. 第一行代码 Android[J]. 2014.
[2] 大气象.Android四种存储方式.http://www.cnblogs.com/greatverve/archive/2011/12/27/android-sharedpreference-file-SQlite-contentprovider.html