`

Android数据的四种存储方式之SharedPreferences、SQLite、ContentProvider和File

阅读更多

  

  Android系统一共提供了四种数据存储方式,分别是:SharedPreference、SQLite、Content Provider和File。由于Android系统中数据基本都是私有的的,都是存放于“data/data/程序包名”目录下,所以要实现数据共享,正确方式是使用ContentProvider。

  SQLite:SQLite是一个轻量级的数据库,支持基本SQL语法,是常被采用的一种数据存储方式。Android为此数据库提供了一个名为SQLiteDatabase的类,封装了一些操作数据库的API。

  SharedPreference:除SQLite数据库外,另一种常用的数据存储方式,其本质就是一个XML文件,常用于存储较简单的参数设置。

  File:即文件(I/O)存储方法,常用于存储大数量的数据,缺点是更新数据困难。

  ContentProvider:Android系统中能实现所有应用程序共享的一种数据存储方式,由于数据通常在各应用间的是互相私密的,所以此存储方式较少使用,但是其又是必不可少的一种存储方式。例如音频,视频,图片和通讯录,一般都可以采用此种方式进行存储。每个ContentProvider都会对外提供一个公共的URI(包装成Uri对象),如果应用程序有数据需要共享时,就需要使用ContentProvider为这些数据定义一个URI,然后其他的应用程序就通过ContentProvider传入这个URI来对数据进行操作。PS: URI由3个部分组成:"content://"、数据的路径、标识ID(可选)。

SQLite是一种专为嵌入式设备设计的轻型数据库,其只有五种数据类型,分别是:

    NULL: 空值

    INTEGER: 有符号整形,根据值的大小以1,2,3,4,6或8字节存放

    REAL: 浮点型,以8字节IEEE浮点数存放

    TEXT: 字符串,使用数据库编码(UTF-8,UTF-16BE或者UTF-16LE)存放

    BLOB: 大数据,只是一个数据块,完全按照输入存放

  在SQLite中,并没有专门设计BOOLEAN和DATE类型,因为BOOLEAN型可以用INTEGER的0和1代替true和false,而DATE类型则可以拥有特定格式的TEXT、REAL和INTEGER的值来代替显示,为了能方便的操作DATE类型,SQLite提供了一组函数,详见:http://www.sqlite.org/lang_datefunc.html。这样简单的数据类型设计更加符合嵌入式设备的要求。关于SQLite的更多资料,请参看:http://www.sqlite.org/

  在Android系统中提供了android.database.sqlite包,用于进行SQLite数据库的增、删、改、查工作。其主要方法如下:

  beginTransaction(): 开始一个事务。

  close(): 关闭连接,释放资源。

  delete(String table, String whereClause, String[] whereArgs): 根据给定条件,删除符合条件的记录。

  endTransaction(): 结束一个事务。

  execSQL(String sql): 执行给定SQL语句。

  insert(String table, String nullColumnHack, ContentValues values): 根据给定条件,插入一条记录。 

  openOrCreateDatabase(String path, SQLiteDatabase.CursorFactory factory): 根据给定条件连接数据库,如果此数据库不存在,则创建。

  query(String table, String[] columns, String selection, String[] selectionArgs, String groupBy, String having, String orderBy): 执行查询。

  rawQuery(String sql, String[] selectionArgs): 根据给定SQL,执行查询。

  update(String table, ContentValues values, String whereClause, String[] whereArgs): 根据给定条件,修改符合条件的记录。

  除了上诉主要方法外,Android还提供了诸多实用的方法。

  一、 创建数据库

  通过openOrCreateDatabase(String path, SQLiteDatabase.CursorFactory factory)方法创建数据库。
1) SQLiteDatabase db =this.openOrCreateDatabase("test_db.db", Context.MODE_PRIVATE, null);
2) SQLiteDatabase db2 = SQLiteDatabase.openOrCreateDatabase("/data/data/com.test/databases/test_db2.db3", null);
上两种方式均能创建数据库,this.openOrCreateDatabase是由SQLiteDatabase.openOrCreateDatabase而来,原生的SQLiteDatabase.openOrCreateDatabase()方法第一参数要求输入绝对路径,而所有的数据库文件都是储存于“data/data/应用报名/databases”目录下,显然输入绝对路径显得有些麻烦,采用this.openOrCreateDatabase则省去了此操作。执行操作后的结果如下图:
 
  另外还可以通过写一个继承SQLiteOpenHelper类的方式创建数据库,此种方式是一种更加进阶的创建方式,在此不做描述。

二、创建数据表,插入数据。
Android系统并没有提供特别的创建数据表的方法,数据表通过SQL语句创建,如下:

    db.execSQL("create table tab(_id integer primary key autoincrement,name text not null)");

表创建好之后,通过insert(String table, String nullColumnHack, ContentValues values)方法插入数据,其中参数含义分别为:

 

    table: 目标表名

    nullColumnHack:指定表中的某列列名。因为在SQLite中,不允许不允许插入所有列均为null的记录,因此初始值有值为空时,此列需显式赋予null

    values: ContentValues对象,类似于java中的Map。以键值对的方式保存数据。

  数据插入代码如下:

 ContentValues values =new ContentValues();
 for(int i=0;i<10;i++){
        values.put("name", "test"+ i);
        db.insert("tab", "_id", values);
 }

  执行此操作后,会新增一个名为“tab”的数据表,利用SQLite客户端(推荐:SQLite Expert Personal 3)可轻松查看此表结构和数据。如下图:

 

三、修改数据

    update(String table, ContentValues values, String whereClause, String[] whereArgs)方法用于修改数据,其四个参数的具体含义如下:

    table: 目标表名

    values: 要被修改成为的新值

    whereClause: where子句,除去where关键字剩下的部分,其中可带?占位符。如没有子句,则为null。

    whereArgs: 用于替代whereClause参数中?占位符的参数。如不需传入参数,则为null。

 修改数据代码如下:

  ContentValues values =new ContentValues();
  values.put("name", "name");
  db.update("tab", values, "_id=1", null);
  db.update("tab", values, "_id=?", new String[]{"5"});

 执行此操作后,执行结果如下图,_id=1和_id=5的数据,name字段的值被修改为了“name”。如下图:

四、查询数据。
之前一直使用SQLite客户端查看数据情况了,这里就使用android提供的query()和rowQuery()方法执行查询。具体代码如下:

 Cursor c = db.query("tab", null, null, null, null, null, null);
 c.moveToFirst();
 while(!c.isAfterLast()){
 int index = c.getColumnIndex("name");
     Log.d("SQLite", c.getString(index));
     c.moveToNext();
 }
 c = db.rawQuery("select * from tab", null);
 c.moveToFirst();
 while(!c.isAfterLast()){
 int index = c.getColumnIndex("name");
     Log.d("SQLite", c.getString(index));
     c.moveToNext();
 }

查询结果如下图:

  可以清晰的在查询结果中,红线上下的数据是完全一致的,也就是说query和rawQuery方法在的不同仅仅在于所需参数的不同。rawQuery方法需要开发者手动写出查询SQL,而query方法是由目标表名、where子句、order by子句、having子句等诸多子句由系统组成SQL语句。两方法同返回Cursor对象,所以两方在使用时孰优孰劣,就看具体情况了。本人更喜欢rawQuery的方式,因为此方式更接近传统Java开发,也可以由专业DBA来书写SQL语句,这样更符合MVC的思想,而且这样的代码可读性更高。(query方法里面参数实在太多,有点记不住谁是order by子句,谁是having子句了)

 

  Cursor对象可以理解为游标对象,凡是对数据有所了解的人,相信对此对象都不会陌生,在这里机不再累述。只提醒一点,在第一次读取Cursor对象中的数据时,一定要先移动游标,否则此游标的位置在第一条记录之前,会引发异常。

五、删除数据

  删除数据也是一件很简单的事,只需要调用delete方法,传入参数即可,delete(String table, String whereClause, String[] whereArgs)的参数三个参数具体含义如下:

    table: 目标表名

    whereClause: where子句,除去where关键字剩下的部分,其中可带?占位符。如没有子句,则为null。

 whereArgs: 用于替代whereClause参数中?占位符的参数。如不需传入参数,则为null。

具体代码如下:

   db.delete("tab", "_id=? or name=?", new String[]{"8", "name"});

执行结果如下:

其中_id=8和name=‘name’的数据统统被删除了。
整个数据库的CRUD操作到此演示完了。最后提醒一点,在操作完数据后,一定要记得调用close()方法关闭连接,释放资源。

 

  SharedPreferences也是一种轻型的数据存储方式,它的本质是基于XML文件存储key-value键值对数据,通常用来存储一些简单的配置信息。其存储位置在/data/data/<包名>/shared_prefs目录下。SharedPreferences对象本身只能获取数据而不支持存储和修改,存储修改是通过Editor对象实现。实现SharedPreferences存储的步骤如下:
一、根据Context获取SharedPreferences对象
二、利用edit()方法获取Editor对象。
三、通过Editor对象存储key-value键值对数据。
四、通过commit()方法提交数据。

具体实现代码如下:

publicclass MainActivity extends Activity {
     @Override
 publicvoid onCreate(Bundle savedInstanceState) {
 super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
        
 //获取SharedPreferences对象
        Context ctx = MainActivity.this;       
        SharedPreferences sp = ctx.getSharedPreferences("SP", MODE_PRIVATE);
   //存入数据
        Editor editor = sp.edit();
        editor.putString("STRING_KEY", "string");
        editor.putInt("INT_KEY", 0);
        editor.putBoolean("BOOLEAN_KEY", true);
        editor.commit();
        
   //返回STRING_KEY的值
        Log.d("SP", sp.getString("STRING_KEY", "none"));
   //如果NOT_EXIST不存在,则返回值为"none"
        Log.d("SP", sp.getString("NOT_EXIST", "none"));
     }
 }

  这段代码执行过后,即在/data/data/com.test/shared_prefs目录下生成了一个SP.xml文件,一个应用可以创建多个这样的xml文件。如图所示:

 

SP.xml文件的具体内容如下:

 <?xml version='1.0' encoding='utf-8' standalone='yes'?>
 <map>
   <string name="STRING_KEY">string</string>
   <int name="INT_KEY" value="0"/>
   <boolean name="BOOLEAN_KEY" value="true"/>
 </map>

  在程序代码中,通过getXXX方法,可以方便的获得对应Key的Value值,如果key值错误或者此key无对应value值,SharedPreferences提供了一个赋予默认值的机会,以此保证程序的健壮性。如下图运行结果中因为并无值为“NOT_EXIST”的Key,所以Log打印出的是其默认值:“none”。在访问一个不存在key值这个过程中,并无任何异常抛出。
  

  SharedPreferences对象与SQLite数据库相比,免去了创建数据库,创建表,写SQL语句等诸多操作,相对而言更加方便,简洁。但是SharedPreferences也有其自身缺陷,比如其只能存储boolean,int,float,long和String五种简单的数据类型,无法进行条件查询等。所以不论SharedPreferences的数据存储操作是如何简单,它也只能是存储方式的一种补充,而无法完全替代如SQLite数据库这样的其他数据存储方式。

  

  ContentProvider是Android平台中,在不同应用程序之间实现数据共享的一种机制。一个应用程序如果需要让别的程序可以操作自己的数据,即可采用这种机制。并且此种方式忽略了底层的数据存储实现,ContentProvider提供了一种统一的通过Uri实现数据操作的方式。其步骤为:

  1. 在当前应用程序中定义一个ContentProvider。

  2. 在当前应用程序的AndroidManifest.xml中注册此ContentProvider

  3. 其他应用程序通过ContentResolver和Uri来获取此ContentProvider的数据。

 ContentResolver提供了诸如insert(), delete(), query()和update()之类的方法。用于实现对ContentProvider中数据的存取操作。

  Uri是一个通用资源标志符,将其分为A,B,C,D 4个部分:

    A:无法改变的标准前缀,包括;"content://"、"tel://"等。当前缀是"content://"时,说明通过一个ContentProvider控制这些数据  

    B:URI的标识,它通过authorities属性声明,用于定义了是哪个ContentProvider提供这些数据。对于第三方应用程序,为了保证URI标识的唯一性,它必须是一个完整的、小写的类名。例如;"content://com.test.data.myprovider"  

    C:路径,可以近似的理解为需要操作的数据库中表的名字,如:"content://hx.android.text.myprovider/name"中的name

    D:如果URI中包含表示需要获取的记录的ID;则就返回该id对应的数据,如果没有ID,就表示返回全部;

下面通过是代码示例,演示一下如何在应用之间相互获取数据。

  在应用程序A中,继承ContProvider类,并重写其中方法。

public class MyProvider extends ContentProvider{
   @Override
   public int delete(Uri uri, String selection, String[] selectionArgs) {
      // TODO Auto-generated method stub
      return 0;
   }
   @Override
   public String getType(Uri uri) {
     // TODO Auto-generated method stub
      return null;
   }
   @Override
   public Uri insert(Uri uri, ContentValues values) {
     return null;
   }
   //在Create中初始化一个数据库
   @Override
   public boolean onCreate() {
         SQLiteDatabase db =this.getContext().openOrCreateDatabase

("test_db.db3", Context.MODE_PRIVATE, null);
         db.execSQL("create table if not exists tab (_id integer primary key 

autoincrement,name text not null)");
         ContentValues values =new ContentValues();
         values.put("name", "test");
         db.insert("tab", "_id", values);
         db.close();
         return true;
     }
    //实现query方法
     @Override
     public Cursor query(Uri uri, String[] projection, String selection,
             String[] selectionArgs, String sortOrder) {
         SQLiteDatabase db =this.getContext().openOrCreateDatabase

("test_db.db3", Context.MODE_PRIVATE, null);
         Cursor c = db.query("tab", null, null, null, null, null,null);
         return c;
     }
     @Override
     public int update(Uri uri, ContentValues values, String selection,
             String[] selectionArgs) {
       // TODO Auto-generated method stub
       return 0;
     }
 }

  在其AndroidManifest.xml中声明此ContentProvider,其中authorities属性定义了此ContentProvider的Uri标识。

<provider android:name=".MyProvider" android:authorities="com.test.MyProvider"/>

  在应用程序B中,通过ContentResolver获取程序A的ContentProvider中的数据。

public class MainActivity extends Activity {
    @Override
   public void onCreate(Bundle savedInstanceState) {
   super.onCreate(savedInstanceState);
         setContentView(R.layout.main);
       //获取上下文
         Context ctx = MainActivity.this;
     //获取ContentResolver对象
         ContentResolver resolver = ctx.getContentResolver();
     //获取Uri对象
         Uri uri = Uri.parse("content://com.test.MyProvider");
     //获取数据
         Cursor c = resolver.query(uri, null, null, null, null);
         c.moveToFirst();
     for(int i=0; i<c.getCount(); i++){
       int index = c.getColumnIndexOrThrow("name");
             String src = c.getString(index);
             Log.d("", src);
             c.moveToNext();
         }
     }
 }

  应用程序B的运行结果如下,从此图可以发现我们在程序B中成功的获取到了程序A中的数据:

  再观察两个应用程序的结构,如下图,其中红框是应用程序A的程序结构,可以清楚看到其有一个名为“test_db.db3”的数据库,蓝框是应用程序B的程序结构,其并没有任何数据库用于存储数据。由此图,可以确定应用程序B中查询出来的数据结果是来自于应用程序A。

  以上就是ContentProvider的使用方式,这种存储方式相比SQLite和SharedPreferences,其复杂性是显而易见的,但是程序间的数据交互需求令ContentProvider存储机制变成必不可少的一部分。


参考博客:http://wisekingokok.cnblogs.com/

SQLite Expert Personal 3下载地址:http://download.csdn.net/detail/xinzheng_wang/4414355

 

 

 

分享到:
评论

相关推荐

    Android四种存储方式

    android 四种存储方式 file SharedPreferences ContentProvider SQLite的简单使用示例

    android数据存储与访问

    数据存储在开发中是使用最频繁的,在这里主要介绍Android平台中实现数据存储的4种方式,分别是:1 使用SharedPreferences存储数据;2 文件存储数据;3 SQLite数据库存储数据;4 使用ContentProvider存储数据;

    疯狂Android讲义源码

     8.2 File存储 311  8.2.1 openFileOutput和open  FileInput 312  8.2.2 读写SD卡上的文件 314  8.3 SQLite数据库 321  8.3.1 简介SQLiteDatabase 321  8.3.2 创建数据库和表 323  8.3.3 使用SQL语句操作...

    Android实例代码

    8.2、File存储:openFileOutput和openFileInput; 读写SD卡文件; 8.3、SQLite数据库:SQL语句; SQLiteDatabase; SQLiteOpenHelper; sqlite3 tools; 8.4、手势(Gesture): 8.5、自动朗读(TTS): 8.6、网络存储...

    疯狂Android讲义(第2版)源代码 第6章~第9章

    8.2、File存储:openFileOutput和openFileInput; 读写SD卡文件; 8.3、SQLite数据库:SQL语句; SQLiteDatabase; SQLiteOpenHelper; sqlite3 tools; 8.4、手势(Gesture): 8.5、自动朗读(TTS): 8.6、网络存储...

    疯狂Android讲义.part2

    8.2 File存储 311 8.2.1 openFileOutput和open FileInput 312 8.2.2 读写SD卡上的文件 314 8.3 SQLite数据库 321 8.3.1 简介SQLiteDatabase 321 8.3.2 创建数据库和表 323 8.3.3 使用SQL语句操作SQLite 数据库 323 ...

    疯狂Android讲义.part1

    8.2 File存储 311 8.2.1 openFileOutput和open FileInput 312 8.2.2 读写SD卡上的文件 314 8.3 SQLite数据库 321 8.3.1 简介SQLiteDatabase 321 8.3.2 创建数据库和表 323 8.3.3 使用SQL语句操作SQLite 数据库 323 ...

    Android典型技术模块开发详解

    10.2 SharedPreferences(键值数据存储) 10.3 SQLite语法 10.4 SQLite数据操作 10.4.1 SQLLiteOpenHelper类 10.4.2 数据库表的增删改查 10.4.3 分页查询 10.5 SQLite事务支持 10.6 Content Providers(数据共享) ...

    8天快速掌握Android教程源码

    27_网络通信之通过GET和POST方式提交参数给web应用.avi 所在项目:newsmanage & Web端应用:web 28_网络通信之通过HTTP协议实现文件上传.avi 所在项目:newsmanage & Web端应用:web 29_发送xml数据和调用webservice...

    传智播客的android开发源代码

    27_网络通信之通过GET和POST方式提交参数给web应用.avi 所在项目:newsmanage & Web端应用:web 28_网络通信之通过HTTP协议实现文件上传.avi 所在项目:newsmanage & Web端应用:web 29_发送xml数据和调用webservice...

    source.zip

    27_网络通信之通过GET和POST方式提交参数给web应用.avi 所在项目:newsmanage & Web端应用:web 28_网络通信之通过HTTP协议实现文件上传.avi 所在项目:newsmanage & Web端应用:web 29_发送xml数据和调用webservice...

Global site tag (gtag.js) - Google Analytics