Android的数据存储(下)

移动开发 Android
Android中使用android.database.sqlite.SQLiteDatabase来表示一个数据库对象,它提供了两种模式来帮助开发者进行增删改查等基本数据库操作。

一、Android数据库使用

Android中使用android.database.sqlite.SQLiteDatabase来表示一个数据库对象,它提供了两种模式来帮助开发者进行增删改查等基本数据库操作。

利用SQL语句描述操作
利用SQL语句调用SQLiteDatabase.execSql或SQLiteDatabase.rawQuery来执行操作。

  1. //利用sql查询数据 
  2. Cursor data = db.rawQuery("select id,name from table"); 
  3.  
  4. //利用sql插入数据 
  5. db.execSql("insert into contacts (id,name) values (2,'cpacm')"); 

稍微学过sql语句的人应该都看的懂上面的代码(其实看语句的意思也能知道个大概~)
在 这里我来解释一下Cursor(游标)的作用吧,游标不能顾名思义(up主当时学习数据库时一度将游标当做与C语言里面的指针变量一样,虽然有点对,但意 思还是理解错了),Cursor它是系统为用户开设的一个数据缓冲区,是的,它是一块数据区域,存放SQL语句的执行结果。但是它也提供了能从包括多条数 据记录的结果集中每次提取一条记录的机制,这一点也跟指针很像。游标总是与一条SQL选择语句相关联因为游标由结果集(可以是零条、一条或由相关的选择语 句检索出的多条记录)和结果集中指向特定记录的游标位置组成。当决定对结果集进行处理 时,必须声明一个指向该结果集的游标。用C语言作比较的话,如果写过对文件进行处理的程序,那么游标就像您打开文件所得到的文件句柄一样,只要文件打开成 功,该文件句柄就可代表该文件。总之记住,游标是一块有着特有记号的一块数据区域,能够让用户逐条从中读取出数据。
结构化的方式描述数据库的操作
这样即使我们不熟悉SQL语句,也能使用最熟悉的面向对象的方式进行数据库操作。

  1. //结构化的方式查询数据 
  2. Cursor data = db.query("contacts",new String[]{"id","name"},null,null,null,null,null); 
  3.  
  4. //结构化方式插入数据 
  5. ContentValue values = new ContentValues(); 
  6. values.put("id",2); 
  7. values.put("name","cpacm"); 
  8. db.insert("table",null,values);
  1. /** 
  2. * 参数说明 
  3. * table:数据表名,columns:需要显示的列名,如果为null则相当与* 
  4. * selection:相当于sql语句的where条件;selectionArgs数组放的是where条件要替换的?号 
  5. * groupBy:SQL语句的Group, orderBy: 排序,默认asc 
  6. **/ 
  7. public Cursor query (String table, String[] columns, String selection, String[] selectionArgs, String groupBy, String having, String orderBy){ 

比如说我要查询的SQL语句为

SELECT CustomerName, SUM(OrderPrice) FROM Orders WHERE Country=?        GROUP BY CustomerName        HAVING SUM(OrderPrice)>500  
    ORDER BY CustomerName  

那么我写的代码如下

  1. //数据表名 
  2. String table =  "Orders" ;   
  3. //要显示的列名 
  4. String[] columns = new  String[] { "CustomerName" ,  "SUM(OrderPrice)" };   
  5. //选择条件 
  6. String selection = "Country=?" ;   
  7. //里面的变量对应条件中的问号,多个的时候请一一入座。 
  8. String[] selectionArgs = new  String[]{ "China" };   
  9. //分组名 
  10. String groupBy = "CustomerName" ;   
  11. //分组的条件 
  12. String having = "SUM(OrderPrice)>500" ;   
  13. //按字段排序 
  14. String orderBy = "CustomerName" ;   
  15. Cursor c = db.query(table, columns, selection, selectionArgs, groupBy, having, orderBy);   

这样就能实现数据库的查询了。其它的语句参数都是差不多的,这里就不一一介绍了。

 

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

 

 

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

 

 

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

 

课外小知识:关于GroupBy和Having的使用
group by 顾名思义就是按照xxx进行分组,它必须有“聚合函数”来配合才能使用,使用时至少需要一个分组标识字段。聚合函数有:sum()、count()、 avg()等,使用group by目的就是要将数据分组进行汇总操作。比如上面sql语句的CustomerName,如果它有四个行{“张三”,“李四”,“张三”,“李四”},那 么此时就会分成两组,分别为张三组和李四组,然后统计出他们使用的orderprice总和。
HAVING作用就是为每一个组指定条件,像 where指定条件一样,也就是说,可以根据你指定的条件来选择行。如果你要使用HAVING子句的话,它必须处在GROUP BY子句之后。还是上面的SQL语句,如果张三的SUM(OrderPrice)没有超过500,那么张三组就不会显示。

SQL语句的预编译
在实践中,有的SQL语句需要被反复使用,为了避免反复解析SQL语句产生的开销,可以对需要复用的SQL语句进行预编译,来提高数据库操作的执行效率。

//编译复杂的SQL语句
SQLiteStatement compiledSql = db.compileStatement(aSQL); //执行SQL
compiledSql.execute();
  1. //编译复杂的SQL语句 
  2. SQLiteStatement compiledSql = db.compileStatement(aSQL); 
  3. //执行SQL 
  4. compiledSql.execute(); 

课外小知识:所谓事务是用户定义的一 个数据库操作序列,这些操作要么全做要么全不做,是一个不可分割的工作单位。例如,在关系数据库中,一个事务可以是一条SQL语句、一组SQL语句或整个 程序。 简单举个例子就是你要同时修改数据库中两个不同表的时候,如果它们不是一个事务的话,当***个表修改完,可是第二表改修出现了异常而没能修改的情况下,就 只有第二个表回到未修改之前的状态,而***个表已经被修改完毕。 而当你把它们设定为一个事务的时候,当***个表修改完,可是第二表改修出现了异常而没能修改的情况下,***个表和第二个表都要回到未修改的状态!这就是所 谓的事务回滚。

SQLiteOpenHelper
在SQLiteOpenHelper中,封装了一个SqliteDatabase对象,使用着可以通过使用此类来进行数据库的操作。

  1. package com.example.notebook; 
  2.  
  3. import android.content.Context; 
  4. import android.database.sqlite.SQLiteDatabase; 
  5. import android.database.sqlite.SQLiteOpenHelper; 
  6. import android.database.sqlite.SQLiteDatabase.CursorFactory; 
  7.  
  8.  
  9. public class DBHelper extends SQLiteOpenHelper{ 
  10.     private static final int VERSION=1
  11.     /**  
  12.      * 在SQLiteOpenHelper的子类当中,必须有该构造函数  
  13.      * @param context   上下文对象  
  14.      * @param name      数据库名称  
  15.      * @param factory  
  16.      * @param version   当前数据库的版本,值必须是整数并且是递增的状态  
  17.      */ 
  18.     public DBHelper(Context context,String name,CursorFactory factory,int version){ 
  19.         super(context,name,factory,version); 
  20.     } 
  21.     public DBHelper(Context context, String name, int version){   
  22.         this(context,name,null,version);   
  23.     }   
  24.    
  25.     public DBHelper(Context context, String name){   
  26.         this(context,name,VERSION);   
  27.     } 
  28.     @Override 
  29.     public void onCreate(SQLiteDatabase db) { 
  30.          // 数据库***构造时,会调用该函数,可以在这里构造表、索引,等等  
  31.         System.out.println("create a database");   
  32.         //execSQL用于执行SQL语句   
  33.         db.execSQL("create table notebook(_id integer primary key autoincrement,pic varchar(50),title varchar(20),content text,time varchar)"); 
  34.          
  35.     } 
  36.     @Override 
  37.     public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { 
  38.         // 如果给定的当前数据库版本高于已有数据库版本,调用该函数 
  39.         System.out.println("upgrade a database"); 
  40.     }   
  41.  

SQLiteOpenHelper的应用
新建一个数据库管理类DBManager

  1. package com.example.notebook; 
  2.  
  3. import android.content.ContentValues; 
  4. import android.content.Context; 
  5. import android.database.Cursor; 
  6. import android.database.sqlite.SQLiteDatabase; 
  7. import android.database.sqlite.SQLiteException; 
  8. import android.util.Log; 
  9.  
  10. public class DBManager { 
  11.      
  12. private Context mContext = null
  13.      
  14.     private SQLiteDatabase mSQLiteDatabase = null;//用于操作数据库的对象 
  15.     private DBHelper dh = null;//用于创建数据库的对象 
  16.      
  17.     private String dbName = "note.db";//数据库的名称 
  18.     private int dbVersion = 1;//数据库的版本 
  19.     public DBManager(Context context){ 
  20.         mContext = context; 
  21.     } 
  22.      
  23. public void open(){ 
  24.         try
  25.             dh = new DBHelper(mContext, dbName, null, dbVersion);//建立数据库 
  26.             if(dh == null){ 
  27.                 Log.v("msg""is null"); 
  28.                 return ; 
  29.             } 
  30.             mSQLiteDatabase = dh.getWritableDatabase();//以可写方式打开数据库 
  31.             //dh.onOpen(mSQLiteDatabase); 
  32.         }catch(SQLiteException se){ 
  33.             se.printStackTrace(); 
  34.         } 
  35.     } 
  36. public void close(){ 
  37.      
  38.     mSQLiteDatabase.close();//关闭数据库 
  39.     dh.close(); 
  40.      
  41. public Cursor selectAll(){ 
  42.     Cursor cursor = null
  43.     try
  44.         //sql语句操作 
  45.         String sql = "select * from notebook"
  46.         cursor = mSQLiteDatabase.rawQuery(sql, null); 
  47.     }catch(Exception ex){ 
  48.         ex.printStackTrace(); 
  49.         cursor = null
  50.     } 
  51.     return cursor; 
  52. public Cursor selectById(int id){ 
  53.      
  54.     //String result[] = {}; 
  55.     Cursor cursor = null
  56.     try
  57.         //sql语句操作 
  58.         String sql = "select * from notebook where _id='" + id +"'"
  59.         cursor = mSQLiteDatabase.rawQuery(sql, null); 
  60.     }catch(Exception ex){ 
  61.         ex.printStackTrace(); 
  62.         cursor = null
  63.     } 
  64.      
  65.     return cursor; 
  66. public long insert(String title, String content,String pic){ 
  67.      
  68.     long datetime = System.currentTimeMillis(); 
  69.     long l = -1
  70.     try
  71.         //结构化方式操作 
  72.         ContentValues cv = new ContentValues(); 
  73.         cv.put("title", title); 
  74.         cv.put("content", content); 
  75.         cv.put("time", datetime); 
  76.         cv.put("pic", pic); 
  77.         l = mSQLiteDatabase.insert("notebook"null, cv); 
  78.     //    Log.v("datetime", datetime+""+l); 
  79.     }catch(Exception ex){ 
  80.         ex.printStackTrace(); 
  81.         l = -1
  82.     } 
  83.     return l; 
  84.      
  85. public int delete(int id){ 
  86.     int affect = 0
  87.     try
  88.         //结构化方式操作 
  89.         affect = mSQLiteDatabase.delete("notebook""_id=?"new String[]{String.valueOf(id)}); 
  90.     }catch(Exception ex){ 
  91.         ex.printStackTrace(); 
  92.         affect = -1
  93.     } 
  94.      
  95.     return affect; 
  96. public int update(int id, String title, String content,String pic){ 
  97.     int affect = 0
  98.     try
  99.         //结构化方式操作 
  100.         ContentValues cv = new ContentValues(); 
  101.         cv.put("title", title); 
  102.         cv.put("content", content); 
  103.         cv.put("pic", pic); 
  104.         String w[] = {String.valueOf(id)}; 
  105.         affect = mSQLiteDatabase.update("notebook", cv, "_id=?", w); 
  106.     }catch(Exception ex){ 
  107.         ex.printStackTrace(); 
  108.         affect = -1
  109.     } 
  110.     return affect; 
  111.  

获取数据示例

  1. private DBManager dm = null;// 数据库管理对象 
  2. rivate Cursor cursor = null
  3.            dm = new DBManager(this);//数据库操作对象 
  4.            dm.open();//打开数据库操作对象 
  5.            cursor = dm.selectAll();//获取所有数据 
  6.            cursor.moveToFirst();//将游标移动到***条数据,使用前必须调用 
  7.             
  8.            int count = cursor.getCount();//个数 
  9.            ArrayList<String> contents = new ArrayList<String>();//图片的所有集合 
  10.            ArrayList<String> imgs = new ArrayList<String>();//图片的所有集合 
  11.            ArrayList<String> items = new ArrayList<String>();//标题的所有集合 
  12.            ArrayList<String> times = new ArrayList<String>();//时间的所有集合 
  13.            for(int i= 0; i < count; i++){ 
  14.                contents.add(cursor.getString(cursor.getColumnIndex("content"))); 
  15.                imgs.add(cursor.getString(cursor.getColumnIndex("pic"))); 
  16.                items.add(cursor.getString(cursor.getColumnIndex("title"))); 
  17.                times.add(cursor.getString(cursor.getColumnIndex("time"))); 
  18.                //cursor.getInt(cursor.getColumnIndex("_id")) 
  19.                cursor.moveToNext();//将游标指向下一个 
  20.            } 
  21.            dm.close();//关闭数据操作对象 

数据库的并发问题
并发问题是使用数据库过程中最容易碰到的问题,如果在开发中碰到了android.database.SQLException异常,并提示"database is locked",那很有可能是出现了数据库的死锁导致无法访问。原因是Sqlite会对文件的读写进行加锁,防止数据被破坏。而在Android框架层SqliteDatabase会对所有数据库对象进行加锁保护,一旦出现了指向同一个数据库的多个SqliteDatabase对象同时在多个线程中被使用,那就跳脱了SqliteDatabase锁保护,就会导致数据库出现被锁的异常。因此在实践中,需要保证同时访问数据库的SqliteDatabase对象仅有一个。(可以使用全局变量来保存数据库对象,在整个数据源对象中使用同一个连接)
课外小知识:在Android SDK中提供了工具Sqlite3,在shell模式下,可以对数据库进行增删改查。
cmd->adb shell ->sqlite3 <路径>/<数据库名> ->sqlite > select * from sqmple;

二、Android数据的云端服务

本质上而言,云端存储就是通过网络将移动设备上的数据存储到远端服务器上。在Android中,增加了一些辅助功能,使得整个流程的实现变得更为简单。首先是通过Google账号来标识用户身份。在android中,默认支持使用Google账号作为用户身份的标识,系统上各个应用都可以通过账号系统获得用户的登录信息。其次,有了Google账号,使得开发者不需要自行构建后台服务系统。

Android的云端数据存取由系统服务BackupManagerService来统一管理。当应用提交备份数据请求时,BackupManagerService会将该请求放入备份队列中,该队列会按照一定的控制逻辑定时提交到云端。当有新应用安装到系统时,会触发数据恢复事件,BackupManagerService会凭借应用包名和用户账号从云端取出相应的备份数据,尝试恢复。

在实践中,Android会构造一个派生自BackupAgent类的子类android.app.backup.BackupAgentHelper的对象,来更方便地构建云端存储组件。

  1. import java.io.File; 
  2. import java.io.IOException; 
  3.  
  4. import android.app.backup.BackupAgentHelper; 
  5. import android.app.backup.BackupDataInput; 
  6. import android.app.backup.BackupDataOutput; 
  7. import android.app.backup.FileBackupHelper; 
  8. import android.os.ParcelFileDescriptor; 
  9.  
  10.  
  11.  
  12. public class MyBackupAgent extends BackupAgentHelper { 
  13.  
  14.     private static final String KEY = "my_backup"
  15.      
  16.     @Override 
  17.     public void onCreate() { 
  18.         //构造文件读写对象,声明需要备份的文件 
  19.         FileBackupHelper helper = new FileBackupHelper(this,"backup_file"); 
  20.         addHelper(KEY,helper); 
  21.         super.onCreate(); 
  22.     } 
  23.      
  24.     @Override 
  25.     public void onBackup(ParcelFileDescriptor oldState, BackupDataOutput data, 
  26.             ParcelFileDescriptor newState) throws IOException { 
  27.         //调用父类方法,提交整个文件到云端 
  28.         super.onBackup(oldState, data, newState); 
  29.     } 
  30.  
  31.     @Override 
  32.     public void onRestore(BackupDataInput data, int appVersionCode, 
  33.             ParcelFileDescriptor newState) throws IOException { 
  34.         // 调用父类方法,将从云端获取的文件覆盖本地文件 
  35.         super.onRestore(data, appVersionCode, newState); 
  36.     } 
  37.  
  38.     @Override 
  39.     public void onRestoreFile(ParcelFileDescriptor data, long size, 
  40.             File destination, int type, long mode, long mtime) 
  41.             throws IOException { 
  42.         // TODO Auto-generated method stub 
  43.         super.onRestoreFile(data, size, destination, type, mode, mtime); 
  44.     } 
Android不会自行将数据提交到云端,开发者需要显性调用android.app.backup.BackupManager的dataChanged函数来触发。

和所有组件一样,云端存储组件是由系统进行托管的。这就需要把组件的相关信息放入配置文件中。

 

<application android:backupAgent = "MyBackupAgent"  ...> 

 

责任编辑:chenqingxiang 来源: cnblogs
相关推荐

2014-08-26 10:04:51

数据存储

2017-02-23 10:27:59

2012-08-14 10:21:29

2013-12-10 14:31:41

华为HPC大数据存储

2017-11-29 09:48:19

存储数据备份

2011-05-31 17:32:32

Android SharedPref

2010-01-26 14:43:53

Android数据存储

2013-06-14 15:43:46

Android开发移动开发数据存储

2018-10-08 13:52:28

Android数据安全存储安全

2013-06-14 15:24:57

Android开发移动开发数据存储方式

2012-06-08 09:46:26

大数据

2017-03-13 20:48:47

2017-03-08 11:56:49

2024-06-21 13:47:17

2017-02-15 14:47:34

冷存储

2023-11-10 11:02:28

Android10分区存储

2021-01-11 05:13:50

数据存储字节

2012-05-04 09:47:56

虚拟化存储虚拟化思杰

2022-12-29 08:57:34

Android本地数据存储
点赞
收藏

51CTO技术栈公众号