Android 里的数据储存

  • 数据持久化

关于数据储存,这个话题已经被反复讨论过不少次了,我是不建议把网络存储这种方式归入到数据储存的范围的,由于这个和Android没多少关系,所以就有以下的分类:java

本地储存(也称之为数据持久化,包含文件储存,SharedPreferences,SQLite储存和ContentProvider(内容提供者))android

内存储存(静态变量、全局变量存值)sql

  • 适用场景

若是app内有些数据是须要使用到上次该app关闭时的数据,好比下次启动app没有网络时要求显示以前的省市信息,那么不管,你有多么不肯意,本地储存是必要的,无非就是有数据时从内存先取,没有时从本地存储空间取;shell

内存储存相对于本地储存有着响应快,耗时低的优点,本地储存数据量大IO操做耗时长时甚至要在非UI线程来执行.这就意味着,能不用本地储存就不要用.数据库

  • 基本用法

 使用SharedPreferences存储数据后端

SharedPreferences是Android平台上一个轻量级的存储类,主要是保存一些经常使用的配置好比窗口状态,通常在Activity中 重载窗口状态onSaveInstanceState保存通常使用SharedPreferences完成,它提供了Android平台常规的Long长 整形、Int整形、String字符串型的保存。 数组

它是什么样的处理方式呢? SharedPreferences相似过去Windows系统上的ini配置文件,可是它分为多种权限,能够全局共享访问,最终是以xml方式来保存,总体效率来看不是特别的高,对于常规的轻量级而言比SQLite要好很多,若是真的存储量不大能够考虑本身定义文件格式。xml 处理时Dalvik会经过自带底层的本地XML Parser解析,好比XMLpull方式,这样对于内存资源占用比较好。 其本质是基于XML文件存储key-value键值对数据,一般用来存储一些简单的配置信息。安全

其存储位置在/data/data/<包名>/shared_prefs目录下。服务器

SharedPreferences对象自己只能获取数据而不支持存储和修改,存储修改是经过Editor对象实现。网络

实现SharedPreferences存储的步骤以下:   

根据Context获取SharedPreferences对象   

利用edit()方法获取Editor对象。   

经过Editor对象存储key-value键值对数据。   

经过commit()方法提交数据。

复制代码
//获取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文件。 

SharedPreferences对象与SQLite数据库相比,免去了建立数据库,建立表,写SQL语句等诸多操做,相对而言更加方便,简洁。可是SharedPreferences也有其自身缺陷,好比其职能存储boolean,int,float,long和String五种简单的数据类型,好比其没法进行条件查询等。因此不论SharedPreferences的数据存储操做是如何简单,它也只能是存储方式的一种补充,而没法彻底替代如SQLite数据库这样的其余数据存储方式。 

文件存储数据

关于文件存储,Activity提供了openFileOutput()方法能够用于把数据输出到文件中,具体的实现过程与在J2SE环境中保存数据到文件中是同样的。

文件可用来存放大量数据,如文本、图片、音频等。

默认位置:/data/data/<包>/files/***.***。

 

代码示例:

复制代码
public void save() {

        try {
            FileOutputStream outStream=this.openFileOutput("a.txt",Context.MODE_WORLD_READABLE);
            outStream.write(text.getText().toString().getBytes());
            outStream.close();
            Toast.makeText(MyActivity.this,"Saved",Toast.LENGTH_LONG).show();
        } catch (FileNotFoundException e) {
            return;
        }
        catch (IOException e){
            return ;
        }

 }

复制代码

     

openFileOutput()方法的第一参数用于指定文件名称,不能包含路径分隔符“/” ,若是文件不存在,Android 会自动建立它。

建立的文件保存在/data/data/<package name>/files目录.

android有一套本身的安全模型,当应用程序(.apk)在安装时系统就会分配给他一个userid,当该应用要去访问其余资源好比文件的时候,就须要userid匹配。默认状况下,任何应用建立的文件,sharedpreferences,数据库都应该是私有的(位于/data/data/<package name>/files),其余程序没法访问。

除非在建立时指定了Context.MODE_WORLD_READABLE或者Context.MODE_WORLD_WRITEABLE ,只有这样其余程序才能正确访问。

 

读取文件示例:

复制代码
public void load()
{
    try {
        FileInputStream inStream=this.openFileInput("a.txt");
        ByteArrayOutputStream stream=new ByteArrayOutputStream();
        byte[] buffer=new byte[1024];
        int length=-1;

while((length=inStream.read(buffer))!=-1)   {
            stream.write(buffer,0,length);
        }

        stream.close();
        inStream.close();
        text.setText(stream.toString());
        Toast.makeText(MyActivity.this,"Loaded",Toast.LENGTH_LONG).show();
    } catch (FileNotFoundException e) {
        e.printStackTrace();
    }
    catch (IOException e){
        return ;
    }

}  

 
复制代码

 

对于私有文件只能被建立该文件的应用访问,若是但愿文件能被其余应用读和写,能够在建立文件时,指定Context.MODE_WORLD_READABLE和Context.MODE_WORLD_WRITEABLE权限。  

Activity还提供了getCacheDir()和getFilesDir()方法: getCacheDir()方法用于获取/data/data/<package name>/cache目录 getFilesDir()方法用于获取/data/data/<package name>/files目录。

 

把文件存入SDCard:

使用Activity的openFileOutput()方法保存文件,文件是存放在手机空间上,通常手机的存储空间不是很大,存放些小文件还行,若是要存放像视频这样的大文件,是不可行的。对于像视频这样的大文件,咱们能够把它存放在SDCard。  

 在AndroidManifest.xml中加入访问SDCard的权限以下:

    <!-- 在SDCard中建立与删除文件权限 -->
    <uses-permission android:name="android.permission.MOUNT_UNMOUNT_FILESYSTEMS"/>
 
    <!-- 往SDCard写入数据权限 -->
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/> 

 

要往SDCard存放文件,程序必须先判断手机是否装有SDCard,而且能够进行读写。

注意:访问SDCard必须在AndroidManifest.xml中加入访问SDCard的权限。

 

复制代码
 
    if(Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)){ 

File sdCardDir = Environment.getExternalStorageDirectory();//获取SDCard目录         

File saveFile = new File(sdCardDir, “a.txt”);
        FileOutputStream outStream = new FileOutputStream(saveFile);
        outStream.write("test".getBytes());
        outStream.close();

 
复制代码

 

Environment.getExternalStorageState()方法用于获取SDCard的状态,若是手机装有SDCard,而且能够进行读写,那么方法返回的状态等于Environment.MEDIA_MOUNTED。  

Environment.getExternalStorageDirectory()方法用于获取SDCard的目录,固然要获取SDCard的目录,你也能够这样写:

File sdCardDir = new File("/sdcard"); //获取SDCard目录

File saveFile = new File(sdCardDir, "abc.txt");

SQLite数据库存储数据

SQLite是轻量级嵌入式数据库引擎,它支持 SQL 语言,而且只利用不多的内存就有很好的性能。此外它仍是开源的,任何人均可以使用它。许多开源项目((Mozilla, PHP, Python)都使用了 SQLite.SQLite 由如下几个组件组成:SQL 编译器、内核、后端以及附件。SQLite 经过利用虚拟机和虚拟数据库引擎(VDBE),使调试、修改和扩展 SQLite 的内核变得更加方便。

 特色:面向资源有限的设备,没有服务器进程,全部数据存放在同一文件中跨平台,可自由复制。

SQLite 内部结构:

SQLite 基本上符合 SQL-92 标准,和其余的主要 SQL 数据库没什么区别。它的优势就是高效,Android 运行时环境包含了完整的 SQLite。  

 Android 集成了 SQLite 数据库 Android 在运行时(run-time)集成了 SQLite,因此每一个 Android 应用程序均可以使用 SQLite 数据库。 

 数据库存储在 data/< 项目文件夹 >/databases/ 下。 Android 开发中使用 SQLite 数据库 Activites 能够经过 Content Provider 或者 Service 访问一个数据库。

 

下面会详细讲解若是建立数据库,添加数据和查询数据库。 建立数据库 Android 不自动提供数据库。在 Android 应用程序中使用 SQLite,必须本身建立数据库,而后建立表、索引,填充数据。

Android 提供了 SQLiteOpenHelper 帮助你建立一个数据库,你只要继承 SQLiteOpenHelper 类,就能够轻松的建立数据库。SQLiteOpenHelper 类根据开发应用程序的须要,封装了建立和更新数据库使用的逻辑。

 

SQLiteOpenHelper 的子类,至少须要实现三个方法:

1 构造函数,调用父类 SQLiteOpenHelper 的构造函数。这个方法须要四个参数:上下文环境(例如,一个 Activity),数据库名字,一个可选的游标工厂(一般是 Null),一个表明你正在使用的数据库模型版本的整数。

2 onCreate()方法,它须要一个 SQLiteDatabase 对象做为参数,根据须要对这个对象填充表和初始化数据。

3 onUpgrage() 方法,它须要三个参数,一个 SQLiteDatabase 对象,一个旧的版本号和一个新的版本号,这样你就能够清楚如何把一个数据库从旧的模型转变到新的模型。

 

下面示例代码展现了如何继承 SQLiteOpenHelper 建立数据库:

复制代码
 
public class DatabaseHelper extends SQLiteOpenHelper {    

  DatabaseHelper(Context context, String name, CursorFactory cursorFactory, int version) 
  {     
    super(context, name, cursorFactory, version);     
     }     
     
     @Override    
     public void onCreate(SQLiteDatabase db) {     
         // TODO 建立数据库后,对数据库的操做     
     }     
     
     @Override    
 public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {     
         // TODO 更改数据库版本的操做     
     }     
     
 @Override    
 public void onOpen(SQLiteDatabase db) {     
         super.onOpen(db);       
         // TODO 每次成功打开数据库后首先被执行     
     }     
 }     

 
复制代码
 

 

接下来讨论具体如何建立表、插入数据、删除表等等。调用 getReadableDatabase() 或 getWriteableDatabase() 方法,你能够获得 SQLiteDatabase 实例,具体调用那个方法,取决于你是否须要改变数据库的内容:

 

     db=(new DatabaseHelper(getContext())).getWritableDatabase();
       return (db == null) ? false : true;  

    

上面这段代码会返回一个 SQLiteDatabase 类的实例,使用这个对象,你就能够查询或者修改数据库。 当你完成了对数据库的操做(例如你的 Activity 已经关闭),须要调用 SQLiteDatabase 的 Close() 方法来释放掉数据库链接。 建立表和索引 为了建立表和索引,须要调用 SQLiteDatabase 的 execSQL() 方法来执行 DDL 语句。若是没有异常,这个方法没有返回值。 

 

例如,你能够执行以下代码:

db.execSQL("CREATE TABLE mytable (_id INTEGER PRIMARY KEY AUTOINCREMENT, title TEXT, value REAL);");  

 

这条语句会建立一个名为 mytable 的表,表有一个列名为 _id,而且是主键,这列的值是会自动增加的整数(例如,当你插入一行时,SQLite 会给这列自动赋值),另外还有两列:title( 字符 ) 和 value( 浮点数 )。 SQLite 会自动为主键列建立索引。 一般状况下,第一次建立数据库时建立了表和索引。

 

若是你不须要改变表的 schema,不须要删除表和索引 . 删除表和索引,须要使用 execSQL() 方法调用 DROP INDEX 和 DROP TABLE 语句。 给表添加数据 上面的代码,已经建立了数据库和表,如今须要给表添加数据。有两种方法能够给表添加数据。

 

像上面建立表同样,你可使用 execSQL() 方法执行 INSERT, UPDATE, DELETE 等语句来更新表的数据。execSQL() 方法适用于全部不返回结果的 SQL 语句。

例如: db.execSQL("INSERT INTO widgets (name, inventory)"+ "VALUES ('Sprocket', 5)");

另外一种方法是使用 SQLiteDatabase 对象的 insert(), update(), delete() 方法。这些方法把 SQL 语句的一部分做为参数。

 

示例以下:

ContentValues cv=new ContentValues();

cv.put(Constants.TITLE, "example title");

cv.put(Constants.VALUE, SensorManager.GRAVITY_DEATH_STAR_I);

db.insert("mytable", getNullColumnHack(), cv);

update()方法有四个参数,分别是表名,表示列名和值的 ContentValues 对象,可选的 WHERE 条件和可选的填充 WHERE 语句的字符串,这些字符串会替换 WHERE 条件中的“?”标记。

update() 根据条件,更新指定列的值,因此用 execSQL() 方法能够达到一样的目的。 WHERE 条件和其参数和用过的其余 SQL APIs 相似。

 

例如:

String[] parms=new String[] {"this is a string"};

db.update("widgets", replacements, "name=?", parms);

delete() 方法的使用和 update() 相似,使用表名,可选的 WHERE 条件和相应的填充 WHERE 条件的字符串。 查询数据库 相似 INSERT, UPDATE, DELETE,有两种方法使用 SELECT 从 SQLite 数据库检索数据。  

 

1 .使用 rawQuery() 直接调用 SELECT 语句; 使用 query() 方法构建一个查询。

Raw Queries 正如 API 名字,rawQuery() 是最简单的解决方法。经过这个方法你就能够调用 SQL SELECT 语句。

例如: Cursor c=db.rawQuery( "SELECT name FROM sqlite_master WHERE type='table' AND name='mytable'", null);

在上面例子中,咱们查询 SQLite 系统表(sqlite_master)检查 table 表是否存在。返回值是一个 cursor 对象,这个对象的方法能够迭代查询结果。 若是查询是动态的,使用这个方法就会很是复杂。

例如,当你须要查询的列在程序编译的时候不能肯定,这时候使用 query() 方法会方便不少。

Regular Queries query() 方法用 SELECT 语句段构建查询。SELECT 语句内容做为 query() 方法的参数,好比:要查询的表名,要获取的字段名,WHERE 条件,包含可选的位置参数,去替代 WHERE 条件中位置参数的值,GROUP BY 条件,HAVING 条件。 除了表名,其余参数能够是 null。因此,之前的代码段能够可写成:

String[] columns={"ID", "inventory"}; 

 String[] parms={"snicklefritz"}; 

 Cursor result=db.query("widgets", columns, "name=?",parms, null, null, null);   

 

使用游标  

无论你如何执行查询,都会返回一个 Cursor,这是 Android 的 SQLite 数据库游标,

使用游标,你能够:

经过使用 getCount() 方法获得结果集中有多少记录;

经过 moveToFirst(), moveToNext(), 和 isAfterLast() 方法遍历全部记录;

经过 getColumnNames() 获得字段名;

经过 getColumnIndex() 转换成字段号;

经过 getString(),getInt() 等方法获得给定字段当前记录的值;

经过 requery() 方法从新执行查询获得游标;

经过 close() 方法释放游标资源;

 

例如,下面代码遍历 mytable 表:

复制代码
 
Cursor result=db.rawQuery("SELECT ID, name, inventory FROM mytable");     

result.moveToFirst(); 
    while (!result.isAfterLast()) { 
        int id=result.getInt(0); 
        String name=result.getString(1); 
        int inventory=result.getInt(2); 
        // do something useful with these 
        result.moveToNext(); 
      } 

 result.close();   

 
复制代码

     

在 Android 中使用 SQLite 数据库管理工具 在其余数据库上做开发,通常都使用工具来检查和处理数据库的内容,而不是仅仅使用数据库的 API。 

首先,模拟器绑定了 sqlite3 控制台程序,可使用 adb shell 命令来调用他。只要你进入了模拟器的 shell,在数据库的路径执行 sqlite3 命令就能够了。

数据库文件通常存放在: /data/data/your.app.package/databases/your-db-name 若是你喜欢使用更友好的工具,你能够把数据库拷贝到你的开发机上,使用 SQLite-aware 客户端来操做它。这样的话,你在一个数据库的拷贝上操做,若是你想要你的修改能反映到设备上,你须要把数据库备份回去。

把数据库从设备上考出来,你可使用 adb pull 命令(或者在 IDE 上作相应操做)。

存储一个修改过的数据库到设备上,使用 adb push 命令。 一个最方便的 SQLite 客户端是 FireFox SQLite Manager 扩展,它能够跨全部平台使用。

 

下图是SQLite Manager工具:

 

 

若是你想要开发 Android 应用程序,必定须要在 Android 上存储数据,使用 SQLite 数据库是一种很是好的选择。 

 

 使用ContentProvider存储数据

Android这个系统和其余的操做系统还不太同样,咱们须要记住的是,数据在Android当中是私有的,固然这些数据包括文件数据和数据库数据以及一些其余类型的数据。那这个时候有读者就会提出问题,难道两个程序之间就没有办法对于数据进行交换?Android这么优秀的系统不会让这种状况发生的。解决这个问题主要靠ContentProvider。一个Content Provider类实现了一组标准的方法接口,从而可以让其余的应用保存或读取此Content Provider的各类数据类型。也就是说,一个程序能够经过实现一个Content Provider的抽象接口将本身的数据暴露出去。外界根本看不到,也不用看到这个应用暴露的数据在应用当中是如何存储的,或者是用数据库存储仍是用文件存储,仍是经过网上得到,这些一切都不重要,重要的是外界能够经过这一套标准及统一的接口和程序里的数据打交道,能够读取程序的数据,也能够删除程序的数据,固然,中间也会涉及一些权限的问题。 

 

一个程序能够经过实现一个ContentProvider的抽象接口将本身的数据彻底暴露出去,并且ContentProviders是以相似数据库中表的方式将数据暴露,也就是说ContentProvider就像一个“数据库”。那么外界获取其提供的数据,也就应该与从数据库中获取数据的操做基本同样,只不过是采用URI来表示外界须要访问的“数据库”。 

 

Content Provider提供了一种多应用间数据共享的方式,好比:联系人信息能够被多个应用程序访问。

Content Provider是个实现了一组用于提供其余应用程序存取数据的标准方法的类。 应用程序能够在Content Provider中执行以下操做: 查询数据 修改数据 添加数据 删除数据

标准的Content Provider: Android提供了一些已经在系统中实现的标准Content Provider,好比联系人信息,图片库等等,你能够用这些Content Provider来访问设备上存储的联系人信息,图片等等。

 

查询记录:  

在Content Provider中使用的查询字符串有别于标准的SQL查询。不少诸如select, add, delete, modify等操做咱们都使用一种特殊的URI来进行,这种URI由3个部分组成, “content://”, 表明数据的路径,和一个可选的标识数据的ID。

 

如下是一些示例URI:

content://media/internal/images 这个URI将返回设备上存储的全部图片

content://contacts/people/ 这个URI将返回设备上的全部联系人信息

content://contacts/people/45 这个URI返回单个结果(联系人信息中ID为45的联系人记录)

尽管这种查询字符串格式很常见,可是它看起来仍是有点使人迷惑。为此,Android提供一系列的帮助类(在android.provider包下),里面包含了不少以类变量形式给出的查询字符串,这种方式更容易让咱们理解一点,参见下例:

MediaStore.Images.Media.INTERNAL_CONTENT_URI Contacts.People.CONTENT_URI

所以,如上面content://contacts/people/45这个URI就能够写成以下形式:

Uri person = ContentUris.withAppendedId(People.CONTENT_URI, 45);

而后执行数据查询: Cursor cur = managedQuery(person, null, null, null);

 

这个查询返回一个包含全部数据字段的游标,咱们能够经过迭代这个游标来获取全部的数据:

复制代码
 
public class ContentProviderDemo extends Activity {
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
       displayRecords();
    }

    private void displayRecords() {
        //该数组中包含了全部要返回的字段
     String columns[] = new String[] { People.NAME, People.NUMBER };
       Uri mContacts = People.CONTENT_URI;
       Cursor cur = managedQuery(
           mContacts,
          columns,  // 要返回的数据字段
       null,          // WHERE子句
       null,         // WHERE 子句的参数
       null         // Order-by子句
     );
       if (cur.moveToFirst()) {
           String name = null;
           String phoneNo = null;
           do {
              // 获取字段的值
         name = cur.getString(cur.getColumnIndex(People.NAME));
             phoneNo = cur.getString(cur.getColumnIndex(People.NUMBER));
             Toast.makeText(this, name + ” ” + phoneNo, Toast.LENGTH_LONG).show();
          } while (cur.moveToNext());
       }
    }

 
复制代码

 

上例示范了一个如何依次读取联系人信息表中的指定数据列name和number。 

 

修改记录:  

咱们可使用ContentResolver.update()方法来修改数据,咱们来写一个修改数据的方法:

复制代码
 
    private void updateRecord(int recNo, String name) {

         Uri uri = ContentUris.withAppendedId(People.CONTENT_URI, recNo);
         ContentValues values = new ContentValues();
         values.put(People.NAME, name);
         getContentResolver().update(uri, values, null, null);

    }  

 
复制代码

 

如今你能够调用上面的方法来更新指定记录: updateRecord(10, ”XYZ”); //更改第10条记录的name字段值为“XYZ”  

 

添加记录:

要增长记录,咱们能够调用ContentResolver.insert()方法,该方法接受一个要增长的记录的目标URI,以及一个包含了新记录值的Map对象,调用后的返回值是新记录的URI,包含记录号。

上面的例子中咱们都是基于联系人信息簿这个标准的Content Provider,如今咱们继续来建立一个insertRecord() 方法以对联系人信息簿中进行数据的添加:

 

复制代码
 
 private void insertRecords(String name, String phoneNo) {

    ContentValues values = new ContentValues();
    values.put(People.NAME, name);
    Uri uri = getContentResolver().insert(People.CONTENT_URI, values);
    Log.d(”ANDROID”, uri.toString());
    Uri numberUri = Uri.withAppendedPath(uri, People.Phones.CONTENT_DIRECTORY);
    values.clear();
    values.put(Contacts.Phones.TYPE, People.Phones.TYPE_MOBILE);
    values.put(People.NUMBER, phoneNo);
    getContentResolver().insert(numberUri, values);

}  

 
复制代码

 

这样咱们就能够调用insertRecords(name, phoneNo)的方式来向联系人信息簿中添加联系人姓名和电话号码。  

 

删除记录:

Content Provider中的getContextResolver.delete()方法能够用来删除记录。

下面的记录用来删除设备上全部的联系人信息:

private void deleteRecords() {

Uri uri = People.CONTENT_URI;

getContentResolver().delete(uri, null, null);

}

 

你也能够指定WHERE条件语句来删除特定的记录:

getContentResolver().delete(uri, “NAME=” + “‘XYZ XYZ’”, null);

这将会删除name为‘XYZ XYZ’的记录。

 

建立Content Provider:  

至此咱们已经知道如何使用Content Provider了,如今让咱们来看下如何本身建立一个Content Provider。

要建立咱们本身的Content Provider的话,咱们须要遵循如下几步:

1. 建立一个继承了ContentProvider父类的类

2. 定义一个名为CONTENT_URI,而且是public static final的Uri类型的类变量,你必须为其指定一个惟一的字符串值,最好的方案是以类的全名称,

如: public static final Uri CONTENT_URI = Uri.parse( “content://com.google.android.MyContentProvider”);

3. 建立你的数据存储系统。大多数Content Provider使用Android文件系统或SQLite数据库来保持数据,可是你也能够以任何你想要的方式来存储。

4. 定义你要返回给客户端的数据列名。若是你正在使用Android数据库,则数据列的使用方式就和你以往所熟悉的其余数据库同样。可是,你必须为其定义一个叫_id的列,它用来表示每条记录的惟一性。

5. 若是你要存储字节型数据,好比位图文件等,那保存该数据的数据列实际上是一个表示实际保存文件的URI字符串,客户端经过它来读取对应的文件数据,处理这种数据类型的Content Provider须要实现一个名为_data的字段,_data字段列出了该文件在Android文件系统上的精确路径。这个字段不只是供客户端使用,并且也能够供ContentResolver使用。客户端能够调用ContentResolver.openOutputStream()方法来处理该URI指向的文件资源,若是是ContentResolver自己的话,因为其持有的权限比客户端要高,因此它能直接访问该数据文件。

6. 声明public static String型的变量,用于指定要从游标处返回的数据列。

7. 查询返回一个Cursor类型的对象。全部执行写操做的方法如insert(), update() 以及delete()都将被监听。咱们能够经过使用ContentResover().notifyChange()方法来通知监听器关于数据更新的信息。

8. 在AndroidMenifest.xml中使用标签来设置Content Provider。

9. 若是你要处理的数据类型是一种比较新的类型,你就必须先定义一个新的MIME类型,以供ContentProvider.geType(url)来返回。

MIME类型有两种形式:

一种是为指定的单个记录的,还有一种是为多条记录的。这里给出一种经常使用的格式: vnd.android.cursor.item/vnd.yourcompanyname.contenttype (单个记录的MIME类型) 好比, 一个请求列车信息的URI如content://com.example.transportationprovider/trains/122 可能就会返回typevnd.android.cursor.item/vnd.example.rail这样一个MIME类型。

vnd.android.cursor.dir/vnd.yourcompanyname.contenttype (多个记录的MIME类型) 好比, 一个请求全部列车信息的URI如content://com.example.transportationprovider/trains 可能就会返回vnd.android.cursor.dir/vnd.example.rail这样一个MIME 类型。

下列代码将建立一个Content Provider,它仅仅是存储用户名称并显示全部的用户名称(使用 SQLLite数据库存储这些数据):

 

复制代码
 
public class MyUsers {
    public static final String AUTHORITY  = “com.wissen.MyContentProvider”;

    // BaseColumn类中已经包含了 _id字段
   public static final class User implements BaseColumns {
        public static final Uri CONTENT_URI  = Uri.parse(”content://com.wissen.MyContentProvider”);
        // 表数据列
     public static final String  USER_NAME  = “USER_NAME”;
    }

}  

复制代码

 

上面的类中定义了Content Provider的CONTENT_URI,以及数据列。下面咱们将定义基于上面的类来定义实际的Content Provider类: 

 

复制代码
 
public class MyContentProvider extends ContentProvider {
    private SQLiteDatabase     sqlDB;
    private DatabaseHelper    dbHelper;
    private static final String  DATABASE_NAME     = “Users.db”;
    private static final int        DATABASE_VERSION         = 1;
    private static final String TABLE_NAME   = “User”;
    private static final String TAG = “MyContentProvider”;

    private static class DatabaseHelper extends SQLiteOpenHelper {
        DatabaseHelper(Context context) {
            super(context, DATABASE_NAME, null, DATABASE_VERSION);
        }

        @Override
        public void onCreate(SQLiteDatabase db) {
            //建立用于存储数据的表
        db.execSQL(”Create table ” + TABLE_NAME + “( _id INTEGER PRIMARY KEY AUTOINCREMENT, USER_NAME TEXT);”);
        }

        @Override
        public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
            db.execSQL(”DROP TABLE IF EXISTS ” + TABLE_NAME);
            onCreate(db);
        }
    }

    @Override
    public int delete(Uri uri, String s, String[] as) {
        return 0;
    }

    @Override
    public String getType(Uri uri) {
        return null;
    }

    @Override
    public Uri insert(Uri uri, ContentValues contentvalues) {
        sqlDB = dbHelper.getWritableDatabase();
        long rowId = sqlDB.insert(TABLE_NAME, “”, contentvalues);
        if (rowId > 0) {
            Uri rowUri = ContentUris.appendId(MyUsers.User.CONTENT_URI.buildUpon(), rowId).build();
            getContext().getContentResolver().notifyChange(rowUri, null);
            return rowUri;
        }
        throw new SQLException(”Failed to insert row into ” + uri);
    }

    @Override
    public boolean onCreate() {
        dbHelper = new DatabaseHelper(getContext());
        return (dbHelper == null) ? false : true;
    }

    @Override
    public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) {
        SQLiteQueryBuilder qb = new SQLiteQueryBuilder();
        SQLiteDatabase db = dbHelper.getReadableDatabase();
        qb.setTables(TABLE_NAME);
        Cursor c = qb.query(db, projection, selection, null, null, null, sortOrder);
        c.setNotificationUri(getContext().getContentResolver(), uri);
        return c;
    }

    @Override
    public int update(Uri uri, ContentValues contentvalues, String s, String[] as) {
        return 0;
    }

}  

复制代码

 

一个名为MyContentProvider的Content Provider建立完成了,它用于从Sqlite数据库中添加和读取记录。 

Content Provider的入口须要在AndroidManifest.xml中配置:

以后,让咱们来使用这个定义好的Content Provider: 

 

复制代码
 
public class ContentDemo extends Activity {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        insertRecord(”MyUser”);
        displayRecords();
    }
   
    private void insertRecord(String userName) {
        ContentValues values = new ContentValues();
        values.put(MyUsers.User.USER_NAME, userName);
        getContentResolver().insert(MyUsers.User.CONTENT_URI, values);
    }

    private void displayRecords() {
        String columns[] = new String[] { MyUsers.User._ID, MyUsers.User.USER_NAME };
        Uri myUri = MyUsers.User.CONTENT_URI;
        Cursor cur = managedQuery(myUri, columns,null, null, null );
        if (cur.moveToFirst()) {
            String id = null;
            String userName = null;
            do {
                id = cur.getString(cur.getColumnIndex(MyUsers.User._ID));
                userName = cur.getString(cur.getColumnIndex(MyUsers.User.USER_NAME));
                Toast.makeText(this, id + ” ” + userName, Toast.LENGTH_LONG).show();
           } while (cur.moveToNext());
       }
    }

复制代码

 

上面的类将先向数据库中添加一条用户数据,而后显示数据库中全部的用户数据。 

  •  总结

  我我的而言,使用的多点的就是sp储存,sqlite储存,文件储存;contentprovider使用的最少,用到基本就是调用系统的内容提供者获取一下联系人列表什么的;若是存储的是较复杂的javabean,那仍是老老实实的用sqlite吧,别忘了增删改查放在工做线程;本地储存的话尤为要注意权限问题,7.1的这些储存权限已是危险权限了,单单在AndroidManifest定义是没用的,须要配合使用动态权限申请(定制的系统就例外了,哈哈,本身定制的什么都好说);从性能最优的角度说,能不用本地储存就不要用,用到本地储存时,能用sp,就不要用sqlite;文件储存慎用...

相关文章
相关标签/搜索