1、AndroidAndroid数据存储数据存储计算机系 康钦马AndroidAndroid数据存储概述数据存储概述 Android数据存储主要是通过Shared Preferences、本地文件和SQLite数据库。Android是基于Linux系统,每个用户有独立的进程,这些进程之间是不能互相访问的,如果有需要在各个用户之间共享数据,我们需要使用CotentProivder实现。另外,ContentProvider可以提供一个统一的接口使上层调用者不用关心数据存储的细节问题。Shared Shared PreferencesPreferences SharedPreferences用于简单的数
2、据存储,是通过“name-value对”的机制存储数据,可以存储一些基本的数据类型包括:Boolean,string,float,long和integer类型。数据是存储在XML文件中的。案例案例 在文本框中输入文字,点击保存按钮,保存文字到SharedPreferences中。为了查看数据是否成功保存并持久化,可以关闭模拟器,重新启动程序后,点击“获取数据”按钮。保存数据保存数据save.setOnClickListener(new View.OnClickListener()save.setOnClickListener(new View.OnClickListener()Override
3、Overridepublic void onClick(View v)public void onClick(View v)/存储数据到存储数据到XMLXML记录文件记录文件SharedPreferences mySharedPreferences=getSharedPreferences(SharedPreferences mySharedPreferences=getSharedPreferences(MYPREFS,MODE_PRIVATE);MYPREFS,MODE_PRIVATE);/获得修改所用的获得修改所用的SharedPreferencesSharedPreferences对象
4、对象SharedPreferences.Editor editor=mySharedPreferences.edit();SharedPreferences.Editor editor=mySharedPreferences.edit();editor.putString(TEXT_KEY,text.getText().toString();editor.putString(TEXT_KEY,text.getText().toString();/确定修改确定修改mit();mit();););getSharedPreferencesgetSharedPreferences方法方法 MYPREF
5、SMYPREFS参数是我们自己定义的参数是我们自己定义的SharedPreferences文件名,这个文件名是自定义,Android按照这个名字将数据保存成XML文件。MODE_PRIVATEMODE_PRIVATE是数据操作模式,该模式只能是被是数据操作模式,该模式只能是被具有相同的用户具有相同的用户IDID的应用程序访问,此外还有:的应用程序访问,此外还有:MODE_WORLD_READABLEMODE_WORLD_READABLE,可以被其它所有的应用程序读,可以被其它所有的应用程序读取数据。取数据。MODE_WORLD_WRITEABLEMODE_WORLD_WRITEABLE,可以被
6、其它所有的应用程序,可以被其它所有的应用程序写入数据。写入数据。SharedPreferences.EditorSharedPreferences.Editor 当我们是SharedPreferences修改数据时候可以使用SharedPreferences.Editor对象。SharedPreferences.Editor editor=mySharedPreferences.edit();mit()提交修改后的数据。获得数据获得数据find.setOnClickListener(new View.OnClickListener()find.setOnClickListener(new Vi
7、ew.OnClickListener()Override Override public void onClick(View v)public void onClick(View v)/存储数据到存储数据到XMLXML记录文件记录文件SharedPreferences mySharedPreferences=getSharedPreferences(SharedPreferences mySharedPreferences=getSharedPreferences(MYPREFS,MODE_PRIVATE);MYPREFS,MODE_PRIVATE);String stringPreferen
8、ce;String stringPreference;stringPreference=mySharedPreferences.getString(TEXT_KEY,stringPreference=mySharedPreferences.getString(TEXT_KEY,););text.setText(stringPreference);text.setText(stringPreference);););SQLiteSQLite数据库数据库SQLite是一个开源的嵌入式关系数据库,它在2000年由D.Richard Hipp发布,它的减少应用程序管理数据的开销,SQLite可移植性好
9、,很容易使用,很小,高效而且可靠。SQLite嵌入到使用它的应用程序中,它们共用相同的进程空间,而不是单独的一个进程。从外部看,它并不像一个RDBMS,但在进程内部,它却是完整的,自包含的数据库引擎。嵌入式数据库的一大好处就是在你的程序内部不需要网络配置,也不需要管理。因为客户端和服务器在同一进程空间运行。SQLite 的数据库权限只依赖于文件系统,没有用户帐户的概念。SQLite 有数据库级锁定,没有网络服务器。它需要的内存,其它开销很小,适合用于嵌入式设备。你需要做的仅仅是把它正确的编译到你的程序。SQLiteSQLite数据类型数据类型 SQLite是无类型的,这意味着你可以保存任何类型
10、的数据到你所想要保存的任何表的任何列中,无论这列声明的数据类型是什么,对于SQLite来说对字段不指定类型是完全有效的,如:Create Table ex1(a,b,c);SQLite允许忽略数据类型,但是仍然建议在你的Create Table语句中指定数据类型,因为数据类型对于你和其他的程序员交流,或者你准备换掉你的数据库引擎。SQLite支持常见的数据类型,如:CREATE TABLE ex2(a VARCHAR(10),b NVARCHAR(15),c TEXT,d INTEGER,e FLOAT,f BOOLEAN,g CLOB,h BLOB,i TIMESTAMP,j NUMERIC
11、(10,5)k VARYING CHARACTER(24),l NATIONAL VARYING CHARACTER(16);创建表创建表 CREATE TABLE语句CREATE TABLE Table_Name(column_name datatype(size),column_name datatype(size),)关系数据类型关系数据类型 字符串数据 数字数据 时间数据 大型对象字符串数据字符串数据 固定长度 固定长度的字段总是占据等量的内存空间,不管实际上在它们中间存放的数据量有多少。可变长度 而可变长度的字符串只占据它们的内容所消耗的内存量,不管它们的最大尺寸是多少。字符串数据示
12、例字符串数据示例CREATE TABLE Studios(name CHAR(20),city VARCHAR(50),state CHAR(2),revenue FLOAT)固定长度可变长度数字数据数字数据 多数数据库都提供至少2种数字数据类型:一种用于整数整数,另一种用于浮点数浮点数。另外还有一些数据库提供更加独特的数字类型。整数整数 浮点数浮点数数字数据示例数字数据示例CREATE TABLE Studios(name CHAR(20),city VARCHAR(50),state CHAR(2),revenue FLOAT)数字指定键指定键 主键 每个表中只能指定一个主键 PRIMAR
13、Y KEY主键示例主键示例 还可以在定义列的时候同时指定候选键和主键,以此代替在CREATE TABLE语句结尾处单独的子句中创建键。CREATE TABLE Studios(studio_id INTEGER PRIMARY KEY,name CHAR(20)UNIQUE,city VARCHAR(50),state CHAR(2)指定studio_id为主键INSERTINSERT语句语句 语法 INSERT INTO table_dame (column_list)VALUES (value_list)table_name是表名称,记录将要添加到该表中。INSERTINSERT语句语句
14、示例示例1 1 Studios表的INSERT语句 INSERT INTO StudiosVALUES(1,Giant,LosAngeles,CA)INSERTINSERT语句语句 示例示例2 2 Studios表中的列分别是:studio_id、name、city和state。因为这些值都按照上述顺序包括在VALUES子句中,所以INSERT语句可以正常工作。INSERT INTO Studios(city,state,name,studio_id)VALUES(Burbank,CA,MPM,2)INSERTINSERT语句语句 小结小结 在多数数据库中,表中的列都按照它们创建的顺序出现。当
15、使用CREATE TABLE创建新的表时,列的顺序将保持为它们在原始语句中指定的顺序。UPDATEUPDATE语句语句 UPDATE语句用来对表中现有的行作改动。UPDATE语句的结构如下:UPDATE table SET column=value,.WHERE conditionUPDATEUPDATE语句语句 UPDATE语句用来对表中现有的行作改动。UPDATE语句的结构如下:UPDATE table SET column=value,.WHERE conditionUPDATEUPDATE语句语句 UPDATE语句有3个部分。第一,必须指定要更新哪一个表。该语句的第二部分是SET子句,
16、应当指定其中要更新的列和要插入的值。最后,WHERE子句可以用来指定表中哪些行将要更新。UPDATEUPDATE语句语句 示例示例1 1 更改某工作室的城市和州的更改某工作室的城市和州的UPDATEUPDATE语句语句 UPDATE Studios SET city=New York,state=NY WHERE studio_id=1 1 row updated.UPDATEUPDATE语句语句 示例示例1 1 可以看到,在SET子句中,将city和state字段都进行了更改。WHERE子句表明只有studioID为1的行才能被更新。在编写只对表中某一行产生影响的UPDATE语句时,在WHE
17、RE子句中使用主关键字来确保只有一行受到改变的影响往往是一个好办法。如果忽略UPDATE语句中的WHERE子句,那么在更新表中的所有行都将受到该语句的影响。UPDATEUPDATE语句语句 示例示例2 2 用UPDATE来更改表中所有行的语句 UPDATE StudiosSET state=AK2 rows updated.SELECT*FROM StudiosSTUDIO_ID NAME CITY STATE1 Giant New York AK2 MPM Burbank AKDELETEDELETE语句语句 DELETE语句也可以用来将记录从表中删除。DEILETE语句的结构非常简单:DE
18、LETE FROM tableWHERE conditionDELETEDELETE语句语句 可选的WHERE子句可用来限制DELETE语句删除的行数。如果忽略WHERE子句,表中所有的行都会被删除。通过使用WHERE子句,可以指定要想删除每行所必须满足的条件。DELETEDELETE语句语句 示例示例 删除Studios 表中所有的行 DELETE FROM StudiosWHERE state=AK2 rows deleted.AndroidAndroid访问访问SQLiteSQLite 在Android访问SQLite数据库常用有两种方式:扩展SQLiteOpenHelper类提供数据访
19、问接口。扩展ContentProvider类来提供数据访问接口。SQLiteOpenHelperSQLiteOpenHelper SQLiteOpenHelper类对于数据库访问能够实现很好的封装。如果我们在打开之前创建或更新数据库,可以通过实现SQLiteOpenHelper类实现这一需求。在实现类中覆盖onCreate和onUpgrade方法,以及对应的构造方法。有关数据库处理的代码片段有关数据库处理的代码片段DBHelper(Context _context)DBHelper(Context _context)super(_context,DATABASE_NAME,null,DATAB
20、ASE_VERSION);super(_context,DATABASE_NAME,null,DATABASE_VERSION);context=_context;context=_context;OverrideOverridepublic void onCreate(SQLiteDatabase db)public void onCreate(SQLiteDatabase db)try try db.execSQL(CREATE TABLE t_user(db.execSQL(CREATE TABLE t_user(+_ID INTEGER PRIMARY KEY autoincremen
21、t,+_ID INTEGER PRIMARY KEY autoincrement,+NAME TEXT+););+NAME TEXT+););catch(Exception e)catch(Exception e)OverrideOverridepublic void onUpgrade(SQLiteDatabase db,int oldVersion,int public void onUpgrade(SQLiteDatabase db,int oldVersion,int newVersion)newVersion)db.execSQL(DROP TABLE IF EXISTS t_use
22、r);db.execSQL(DROP TABLE IF EXISTS t_user);/Create a new one./Create a new one.onCreate(db);onCreate(db);有关有关UIUI的代码片段的代码片段protected void onCreate(Bundle savedInstanceState)protected void onCreate(Bundle savedInstanceState)super.onCreate(savedInstanceState);super.onCreate(savedInstanceState);this.se
23、tContentView(R.layout.main);this.setContentView(R.layout.main);/初始化初始化UIUIbtnAdd=(Button)findViewById(R.id.btnAdd);btnAdd=(Button)findViewById(R.id.btnAdd);btnViewAll=(Button)findViewById(R.id.btnViewAll);btnViewAll=(Button)findViewById(R.id.btnViewAll);viewAll=(TextView)findViewById(R.id.viewAll);v
24、iewAll=(TextView)findViewById(R.id.viewAll);inputTxt=(EditText)findViewById(R.id.txtInput);inputTxt=(EditText)findViewById(R.id.txtInput);/初始化初始化DBDBdb=new DBHelper(this);db=new DBHelper(this);/初始化监听初始化监听OnClickListener listener=new OnClickListener()OnClickListener listener=new OnClickListener()publ
25、ic void onClick(View v)public void onClick(View v)if(v.getId()=R.id.btnAdd)if(v.getId()=R.id.btnAdd)db.save(inputTxt.getText().toString();db.save(inputTxt.getText().toString();else if(v.getId()=R.id.btnViewAll)else if(v.getId()=R.id.btnViewAll)/浏览所有数据浏览所有数据Cursor cur=db.loadAll();Cursor cur=db.loadA
26、ll();StringBuffer sf=new StringBuffer();StringBuffer sf=new StringBuffer();cur.moveToFirst();cur.moveToFirst();while(!cur.isAfterLast()while(!cur.isAfterLast()sf.append(cur.getInt(0).append(:).append(cur.getString(1).append(n);sf.append(cur.getInt(0).append(:).append(cur.getString(1).append(n);cur.m
27、oveToNext();cur.moveToNext();viewAll.setText(sf.toString();viewAll.setText(sf.toString();btnAdd.setOnClickListener(listener);btnAdd.setOnClickListener(listener);btnViewAll.setOnClickListener(listener);btnViewAll.setOnClickListener(listener);小结小结 SQLiteDatabaseSQLiteDatabase void execSQL(String sql)v
28、oid execSQL(String sql),可以执行插入、删除、,可以执行插入、删除、修改和建表修改和建表sqlsql语句。语句。Cursor query()Cursor query()实现查询。实现查询。Cursor Cursor 返回的读取数据库查询结果集,主要方法:返回的读取数据库查询结果集,主要方法:moveToFirstmoveToFirst是将结果集指针移到第一条记录是将结果集指针移到第一条记录 isAfterLast isAfterLast 是将判断是否到达最后一条记录之后。是将判断是否到达最后一条记录之后。moveToNextmoveToNext是将结果集指针移向后移动一条
29、记录。是将结果集指针移向后移动一条记录。遍历游标写法:.c.moveToNext();c.moveToNext();while(!c.isAfterLast()while(!c.isAfterLast().c.moveToNext();c.moveToNext();.ContentProviderContentProvider 由于Android是基于Linux系统,每个用户有比较的进程,这些进程之间是不能互相访问的,如果有需要在各个用户之间共享数据,我们需要使用CotentProivder实现。在Android中数据存储可以是文件系统,数据库(SQLite等),ContentProvider
30、可以提供一个统一的接口使上层调用者不用关心数据存储的细节问题。Content URIContent URIA.标准前缀表明这个数据被一个内容提供器所控制。它不会被修改。B.URI的权限部分;它标识这个内容提供器。对于第三方应用程序,这应该是一个全称类名以确保唯一性。权限在元素的权限属性中进行声明:C.用来判断请求数据类型的路径。这可以是0或多个段长。这个分段可以没有。D.被请求的特定记录的ID,如果有的话。这是被请求记录的_ID数值。如果这个请求不局限于单个记录,这个分段和尾部的斜线会被忽略:provider android:name=Content URI Content URI 总结总结
31、content:/media/internal/images 这个URI将返回设备上存储的所有图片 content:/contacts/people/这个URI将返回设备上的所有联系人信息 content:/contacts/people/45 这个URI返回单个结果(联系人信息中ID为45的联系人记录)DataProviderDataProvider的代码片段的代码片段OverrideOverridepublic boolean onCreate()public boolean onCreate()mOpenHelper=new DatabaseHelper(getContext();mOp
32、enHelper=new DatabaseHelper(getContext();return true;return true;public Uri insert(Uri uri,ContentValues values)public Uri insert(Uri uri,ContentValues values)SQLiteDatabase db=mOpenHelper.getWritableDatabase();SQLiteDatabase db=mOpenHelper.getWritableDatabase();long rowId=db.insert(TABLE_NAME,null,
33、values);long rowId=db.insert(TABLE_NAME,null,values);Log.v(TAG,insert Table t_user ok);Log.v(TAG,insert Table t_user ok);if(rowId 0)if(rowId 0)Uri insertedUri=ContentUris.withAppendedId(uri,rowId);Uri insertedUri=ContentUris.withAppendedId(uri,rowId);getContext().getContentResolver()getContext().get
34、ContentResolver().notifyChange(insertedUri,null);.notifyChange(insertedUri,null);return insertedUri;return insertedUri;throw new SQLException(Failed to insert row into +uri);throw new SQLException(Failed to insert row into +uri);解释解释 Uri insertedUri=ContentUris.withAppendedId(uri,rowId);重新构建一个URI对象,
35、如果uri是content:/contacts/people,rowId是45。重新构建的URI是content:/contacts/people/45 getContext().getContentResolver().notifyChange(insertedUri,null);notifyChange()方法则用来通知注册在此URI上的观察者(observer)数据发生了改变。最后返回删除或修改数据的行数。DataProviderDataProvider的代码片段的代码片段OverrideOverridepublic Cursor query(Uri uri,String project
36、ion,String public Cursor query(Uri uri,String projection,String selection,selection,String selectionArgs,String sortOrder)String selectionArgs,String sortOrder)SQLiteDatabase db=mOpenHelper.getReadableDatabase();SQLiteDatabase db=mOpenHelper.getReadableDatabase();Cursor c=db.query(TABLE_NAME,project
37、ion,null,null,Cursor c=db.query(TABLE_NAME,projection,null,null,null,null,null);null,null,null);return c;return c;OverrideOverridepublic int update(Uri uri,ContentValues values,String where,public int update(Uri uri,ContentValues values,String where,String whereArgs)String whereArgs)String rowId=uri
38、.getPathSegments().get(1);String rowId=uri.getPathSegments().get(1);SQLiteDatabase db=mOpenHelper.getReadableDatabase();SQLiteDatabase db=mOpenHelper.getReadableDatabase();int count=db.update(TABLE_NAME,values,_ID=+rowIdint count=db.update(TABLE_NAME,values,_ID=+rowId+(!TextUtils.isEmpty(where)?AND(
39、+where+(!TextUtils.isEmpty(where)?AND(+where+):),whereArgs);):),whereArgs);return count;return count;DataProviderDataProvider的代码片段的代码片段OverrideOverridepublic int delete(Uri uri,String where,String whereArgs)public int delete(Uri uri,String where,String whereArgs)String rowId=uri.getPathSegments().ge
40、t(1);String rowId=uri.getPathSegments().get(1);SQLiteDatabase db=mOpenHelper.getReadableDatabase();SQLiteDatabase db=mOpenHelper.getReadableDatabase();int count=db.delete(TABLE_NAME,_ID=+rowIdint count=db.delete(TABLE_NAME,_ID=+rowId+(!TextUtils.isEmpty(where)?AND(+where+):+(!TextUtils.isEmpty(where
41、)?AND(+where+):),whereArgs);),whereArgs);return count;return count;OverrideOverridepublic String getType(Uri uri)public String getType(Uri uri)return null;return null;DatabaseHelperDatabaseHelper的代码片段的代码片段private class DatabaseHelper extends SQLiteOpenHelper private class DatabaseHelper extends SQLi
42、teOpenHelper DatabaseHelper(Context _context)DatabaseHelper(Context _context)super(_context,DATABASE_NAME,null,DATABASE_VERSION);super(_context,DATABASE_NAME,null,DATABASE_VERSION);OverrideOverridepublic void onCreate(SQLiteDatabase db)public void onCreate(SQLiteDatabase db)try try db.execSQL(CREATE
43、 TABLE t_user(db.execSQL(CREATE TABLE t_user(+_ID INTEGER PRIMARY KEY autoincrement,+_ID INTEGER PRIMARY KEY autoincrement,+NAME TEXT+););+NAME TEXT+););Log.v(TAG,Create Table t_user ok);Log.v(TAG,Create Table t_user ok);catch(Exception e)catch(Exception e)Log.v(TAG,Create Table t_user err,table Log
44、.v(TAG,Create Table t_user err,table exists.);exists.);OverrideOverridepublic void onUpgrade(SQLiteDatabase db,int oldVersion,int public void onUpgrade(SQLiteDatabase db,int oldVersion,int newVersion)newVersion)db.execSQL(DROP TABLE IF EXISTS t_user);db.execSQL(DROP TABLE IF EXISTS t_user);onCreate(
45、db);onCreate(db);调用的调用的ActivityActivity代码片段代码片段if(v.getId()=R.id.btnAdd)if(v.getId()=R.id.btnAdd)/增加增加ContentValues values=new ContentValues();ContentValues values=new ContentValues();values.put(NAME,inputId.getText().toString();values.put(NAME,inputId.getText().toString();getContentResolver().inser
46、t(CONTENT_URI,values);getContentResolver().insert(CONTENT_URI,values);/db.close();/db.close();else if(v.getId()=R.id.btnViewAll)else if(v.getId()=R.id.btnViewAll)/浏览所有数据浏览所有数据Cursor cur=getContentResolver().query(CONTENT_URI,Cursor cur=getContentResolver().query(CONTENT_URI,new String _ID,NAME,null,
47、null,null);new String _ID,NAME,null,null,null);StringBuffer sf=new StringBuffer();StringBuffer sf=new StringBuffer();cur.moveToFirst();cur.moveToFirst();while(!cur.isAfterLast()while(!cur.isAfterLast()sf.append(cur.getInt(0).append(:).append(sf.append(cur.getInt(0).append(:).append(cur.getString(1).
48、append(n);cur.getString(1).append(n);cur.moveToNext();cur.moveToNext();/db.close();/db.close();viewAll.setText(sf.toString();viewAll.setText(sf.toString();else if(v.getId()=R.id.btnDelete)else if(v.getId()=R.id.btnDelete)/删除数据删除数据long selectedID=new Long(inputId.getText().toString();long selectedID=
49、new Long(inputId.getText().toString();Uri deletedUri=ContentUris.withAppendedId(CONTENT_URI,selectedID);Uri deletedUri=ContentUris.withAppendedId(CONTENT_URI,selectedID);getContentResolver().delete(deletedUri,null,null);getContentResolver().delete(deletedUri,null,null);使用使用CursorAdapter CursorAdapte
50、r 在UI部分会使用Adapter绑定ListView,CursorAdapter是将数据库查询得到的Cursor对象绑定到ListView的对象。由于CursorAdapter是一个抽象类,所以我们可以使用自己定义的CursorAdapter,也可以使用Android SDK提供的SimpleCursorAdapter。SimpleCursorAdapterSimpleCursorAdapter构造方法构造方法 SimpleCursorAdapter(Context context,int layout,Cursor c,String from,int to)context,是上下文对象 l