SQLite ContentProvider

SharedPreferences & SQLite & ContentProvider & File

Android 的四种常用数据存储方式:

  1. SharedPreferences
  2. SQLite
  3. ContentProvider
  4. File

 

SharedPreferences:

         1, 是一种轻型的数据存储方式

         2,本质是基于 XML 文件存储 key-value 键值对数据

         3,通常用来存储一些简单的配置信息

  1. SharedPreferences 对象本身只能获取数据而不支持存储和修改,存储修改时通过 Editor 对象实现。
  2. 实现 SharedPreferences 存储的步骤如下:
  1. 获得 SharedPreferences 对象
  2. 获得 SharedPreferences.Editor 对象
  3. 通过 Editor 接口的 putXxx 方法保存 key-value 对,其中 Xxx 表示不同的数据类型
  4. 通过 Editor 接口的 commit 方法保存 key-value 对

 

SQLite 特点:

  1. 轻量级 一个动态库、单文件
  2. 独立性 没有依赖、无需安装
  3. 隔离性 全部在一个文件夹中
  4. 跨平台 支持众多操作系统
  5. 多语言接口 支持众多编程语言
  6. 安全性 事务

 

关于事务处理的安全性问题:

         -通过数据库上的独占性和共享锁来实现独立事务处理

         -多个进程可以在同一时间从同一数据库读取数据,但只有一个可以写入数据

 

SQLite 支持的数据类型:

         -NULL、INTEGER、REAL、TEXT、BLOB

         -空值、整型值、浮点值、字符串值、二进制对象

 

动态数据类型 (弱引用)

         -当某个值插入到数据库时,SQLite 将会检查它的类型,如果该类型与关联的列不匹配,SQLite 则会尝试将该值转换成该列的类型,如果不能转换,则该值将作为本身的类型存储。

 

使用须知:

         -由于资源占用少,性能良好和零管理成本,嵌入式数据库有了它的用武之地。

                   例如 Android,IOS

         -没有可用于SQLite 的网络服务器,只能通过网络共享可能存在文件锁定或者性能问题

         -只提供数据库级的锁定

         -没有用户账户概念,而是根据文件系统确定所有数据库的权限。

 

SQLiteDatabase:

         -提供了一些管理 SQLite 数据库的类

         -提供创建,删除,执行 SQL 命令,并执行其他常见的数据库管理任务的方法

         -每个程序的数据库名字是唯一的

 

         .db.execSQL(sql) // 执行任何 SQL 语句

         .db.insert(table, nullColumnHack, values)

         .db.delete(table, whereClause, whereArgs)

         .db.update(table, values, whereClause, whereArgs)

         .db.query(table, columns, selection, selectionArgs, groupBy, having, orderBy)

         .db.rawQuery(sql, selectionArgs)

 

Cursor 是 Android 查询数据后得到的一个管理数据集合的类,正常情况下,如果查询得到的数据量较小时不会有内存问题,而且虚拟机能够保证 Cursor 最终会被释放掉。

然而如果 Cursor 的数据量特别大,特别是如果里面有 Blob 信息时,应该保证 Cursor 占用的内存被及时的释放掉,而不是等待 GC 来处理。而且 Android 明显是倾向于编程者手动的将 Cursor close 掉, 因为在源代码中我们发现,如果等到垃圾回收器来回收时,也就是如果不手动关闭,系统会报错,会给用户以错误提示。

 

Cursor: 游标接口,提供了遍历查询结果的方法,如移动指针方法 move(), 获得列值方法 getString() 等。 常用方法:

  • getCount() 总记录条数
  • isFirst() 判断是否第一条记录
  • isLast() 判断是否最后一条记录
  • moveToFirst() 移动到第一条记录
  • moveToLast() 移动到最后一条记录
  • move(int offset) 移动到指定记录
  • moveToNext() 移动到下一条记录
  • moveToPrevious() 移动到上一条记录
  • getColumnIndex (String columnName)
  • getColumnIndexOrThrow(String columnName)据列名称获得列索引
  • getInt(int columnIndex) 获得指定列索引的 int 类型值
  • getString(int columnIndex) 获得指定列索引的 String 类型值

 

SQLiteDatabase db =  openOrCreateDatabase("user.db", MODE_PRIVATE, null);
db.execSQL(
"create table if not exists usertb(_id integer primary key autoincrement, name text not null, age integer not null, sex text not null)");
db.execSQL(
"insert into usertb(name, sex, age) values('zhangsan', 'nv', 18)");
db.execSQL(
"insert into usertb(name, sex, age) values('lisi', 'nv', 19)");
db.execSQL(
"insert into usertb(name, sex, age) values('wangwu', 'nan', 20)");
Cursor cursor = db.rawQuery(
"select * from usertb", null);
if(cursor != null){
   
while(cursor.moveToNext()){
        Log.i(
"yiusanfendi", "**********************");
        Log.i(
"yiusanfendi", "_id:" + cursor.getInt(cursor.getColumnIndex("_id")));
        Log.i(
"yiusanfendi", "name:" + cursor.getString(cursor.getColumnIndex("name")));
        Log.i(
"yiusanfendi", "age:" + cursor.getInt(cursor.getColumnIndex("age")));
        Log.i(
"yiusanfendi", "sex:" + cursor.getString(cursor.getColumnIndex("sex")));
    }
    Log.i(
"Chunlei", "**********************");
    cursor.close();
}
db.close();

 

ContentValues:

用来存储一组可以被 ContentResolver 处理的值。

         ContentValues values = new ContentValues(); // 类似 HashMap key value

         value.put(“name”, “张三”);

         执行对应的 Sql 操作

SQLiteDatabase db = openOrCreateDatabase("stu.db", MODE_PRIVATE, null);
db.execSQL(
"create table if not exists stutb(_id integer primary key autoincrement, name text not null, sex text not null, age integer not null)");
ContentValues values =
new ContentValues();
values.put(
"name","张三");
values.put(
"sex","男");
values.put(
"age","19");
long rowId = db.insert("stutb", null, values);
values.clear();
values.put(
"name", "张三丰");
values.put(
"sex", "男");
values.put(
"age", 99);
db.insert(
"stutb", null, values);
values.clear();
values.put(
"name", "张三疯");
values.put(
"sex", "男");
values.put(
"age", 59);
db.insert(
"stutb", null, values);
values.clear();
values.put(
"name", "张三峰");
values.put(
"sex", "男");
values.put(
"age", 39);
db.insert(
"stutb", null, values);
values.clear();
values.put(
"name", "张三封");
values.put(
"sex", "男");
values.put(
"age", 29);
db.insert(
"stutb", null, values);
values.clear();
values.put(
"sex", "女");
// 将全部 id > 3 的人的性别改成女
db.update("stutb", values, "_id > ?", new String[]{"3"});
// 删除所有名字中带有丰的人
db.delete("stutb", "name like ? ", new String[]{"%丰%"});
Cursor cursor = db.query(
"stutb", null, "_id > ? ", new String[]{"0"}, null, null, "name");
if(cursor != null){
    String[] columns = cursor.getColumnNames();
    Log.i(
"yiusanfendi", "*********************");
   
while (cursor.moveToNext()){
       
for(String columnName : columns){
            Log.i(
"yiusanfendi", cursor.getString(cursor.getColumnIndex(columnName)));
        }
    }
    Log.i(
"yiusanfendi", "*********************");
    cursor.close();
}
db.close();

 

SQLiteOpenHelper:

         -SQLiteDatabase的帮助类,用于管理数据库的创建和版本更新

         -一般是建立一个类继承它,并重写 onCreate() 和 onUpgrade() 方法

         -方法说明

                   -onCreate(SQLiteDatabase db)                                                                         创建数据库时调用

                   -onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion)           版本更新时调用

                   -getReadableDatabase()                                                                                    创建或打开一个只读数据库

                   -getWritableDatabase()                                                                                      创建或打开一个读写数据库

 

调用处:

DBOpenHelper helper = new DBOpenHelper(this, "student.db");
// 获取一个只读的数据库,只能查询,不能写入,不能更新
SQLiteDatabase db = helper.getReadableDatabase();
Cursor cursor = db.rawQuery(
"select * from studenttb", null);
if(cursor != null){
    String[] cols = cursor.getColumnNames();
   
while(cursor.moveToNext()){
        Log.i(
"yiusanfendi", "***************************");
       
for(String str : cols){
            Log.i(
"yiusanfendi", str + " = " + cursor.getString(cursor.getColumnIndex(str)));
        }
        Log.i(
"yiusanfendi", "***************************");
    }
    cursor.close();
}
db.close();

 

DBOpenHelper

public class DBOpenHelper extends SQLiteOpenHelper{

    public DBOpenHelper(Context context, String name) {

        super(context, name, null, 1);

    }

    public DBOpenHelper(Context context, String name, SQLiteDatabase.CursorFactory factory, int version) {

        super(context, name, factory, version);

    }

    // 首次创建数据库的时候调用,一般用于建库,建表的操作

    @Override

    public void onCreate(SQLiteDatabase db) {

        db.execSQL("create table if not exists studenttb(_id integer primary key autoincrement, name text not null, sex text not null, age integer not null)");

        db.execSQL("insert into studenttb(name, sex, age) values('张三', '女', 18)");

    }

    // 当数据库的版本发生变化的时候会自动执行

    @Override

    public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {

    }

}

 

 

File

Android 中文件存储的操作:

         -Activity 的 openFileOutput() 方法可以用于把数据输出到文件中

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

         -实现过程与在 Java 中保存数据到文件中是一样的

 

把文件写到外置SD卡上

<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>

<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
File file = new File(Environment.getExternalStorageDirectory().getPath() + "/test.txt");

  if(!file.exists()){

    try {

        file.createNewFile();

    } catch (IOException e) {

        e.printStackTrace();

    }

} else {

    Toast.makeText(this, "文件已经存在", Toast.LENGTH_LONG).show();

}

file.delete();

 

// 当前应用程序默认的数据存储目录

  File file = getFilesDir();

Log.i("yimusanfendi", "file = " + file);

file = getCacheDir();

Log.i("yimusanfendi", "file = " + file);

file = getDir("imooc", MODE_PRIVATE);

Log.i("yimusanfendi", "file = " + file);

file = getExternalFilesDir(Environment.DIRECTORY_MUSIC);

Log.i("yimusanfendi", "file = " + file);

07-04 18:52:33.901 3314-3314/? I/yimusanfendi: file = /data/user/0/com.samsung.mycontentproviderandsqlite/files

07-04 18:52:33.901 3314-3314/? I/yimusanfendi: file = /data/user/0/com.samsung.mycontentproviderandsqlite/cache

07-04 18:52:33.902 3314-3314/? I/yimusanfendi: file = /data/user/0/com.samsung.mycontentproviderandsqlite/app_imooc

07-04 18:52:33.905 3314-3314/? I/yimusanfendi: file = /storage/emulated/0/Android/data/com.samsung.mycontentproviderandsqlite/files/Music

 

.MODE_PRIVATE

-为默认操作模式,代表该文件是私有数据,只能被应用本身访问,在该模式下,写入的内容会覆盖原文件的内容。

.MODE_APPEND

         -模式会检查文件是否存在,存在就往文件追加内容,否则就创建新文件。

.MODE_WORLD_READABLE 和 MODE_WORLD_WRITEABLE

         -用来控制其他应用是否有权限读写该文件

.MODE_WORLD_READABLE

         -表示当前文件可以被其他应用读取

.MODE_WORLD_WRITEABLE

         -表示当前文件可以被其他应用写入

 

getExternalFilesDir(type), getExternalCacheDir():

         -可以得到外部的存储位置,该位置的数据和内容的使用时一样的,如果 APP 卸载了,这里面的数据也会自动清除掉,如果开发者不遵守这样的规则,不把数据放到 /data/data/<包名>, /mnt/sdcard/Android/data/<包名>,卸载之后数据将不会自动清除掉,将会造成所谓的数据垃圾

 

Android中文件存储的操作:

         -Activity提供了 openFileOutput() 方法可以用于把数据输出到文件中,具体的实现过程与在 Java 中保存数据到文件中是一样的。

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

当应用程序在安装时系统会分配给他一个 userid, 当该引用要去访问其他资源比如文件的时候,就需要 userid 匹配。默认情况下,任何应用创建的文件,sharedpreferences, 数据库都应该是私有的(位于 /data/data/<package name>/files), 其他程序无法访问。除非在创建时指定了 MODE_WORLD_READABLE 或者 MODE_WORLD_WRITEABLE.

 

// 保存文件内容

  public void writeFiles(String content){

    try {

        FileOutputStream fos = openFileOutput("a.txt", MODE_PRIVATE);

        fos.write(content.getBytes());

        fos.close();

    } catch (FileNotFoundException e) {

        e.printStackTrace();

    } catch (IOException e) {

        e.printStackTrace();

    }

}

  // 读取文件内容

  public String readFiles(){

    String content = null;

    FileInputStream fis = null;

    ByteArrayOutputStream baos = null;

    try {

        fis = openFileInput("a.txt");

        baos = new ByteArrayOutputStream();

        byte[] buffer = new byte[1024];

        int len = 0;

        while((len = fis.read(buffer)) != -1){

            baos.write(buffer, 0, len);

        }

        content = baos.toString();

        fis.close();

        baos.close();

    } catch (FileNotFoundException e) {

        e.printStackTrace();

    } catch (IOException e) {

        e.printStackTrace();

    }

    return content;

}

 

ContentProvider:

 1, 继承抽象类 ContentProvider 实现一系列针对于数据的增、删、改、查等方法;

 2, 需在 Androidmanifest.xml 中完成对 ContentProvider 的注册。

         <provider

                   android:name=”com.imooc.MusicProvider”

                   android:authorities=”com.provider.music”

/>

注:注册的authorities 属性值时全局唯一的

 

Uri 是指通用资源标识符:

         content://         com.imooc.provider         /        music        /        #

      A               B                     C         D

A: 前缀表明数据受控于一个内容提供者。它从不修改,也就是 schema

B: 是指在 AndroidManifest.xml 中我们注册的 provider 中的 android:authorities 属性所对应的

C: 具体操作于哪个条目

D: 具体指定到哪个条目下的哪条记录(#表示通配符)

 

public class MyContentProvider extends ContentProvider{
   
// ContentProvider 创建后被调用
   
@Override
   
public boolean onCreate() {
       
return false;
    }
   
// 根据 Uri 查询出 selection 指定的条件所匹配的全部记录,并且可以指定查询哪些类,以什么方式排序
   
@Nullable
    @Override
   
public Cursor query(@NonNull Uri uri, @Nullable String[] projection, @Nullable String selection, @Nullable String[] selectionArgs, @Nullable String sortOrder) {
       
return null;
    }
   
// 返回当前 Uri 的 MIME 类型,如果该 Uri 对应的数据可能包括多条记录
    // 那么 MIME 类型字符串就是以 vnd.android.dir/ 开头
    // 如果该 Uri 对应的数据只有一条记录,该 MIME 类型字符串 就是以 vnd.android.cursor.item/ 开头
   
@Nullable
    @Override
   
public String getType(@NonNull Uri uri) {
       
return null;
    }
   
// 根据 URI 插入 Values 对应的数据
   
@Nullable
    @Override
   
public Uri insert(@NonNull Uri uri, @Nullable ContentValues values) {
       
return null;
    }
   
// 根据 Uri 删除 selection 指定的条件所匹配的全部记录
   
@Override
   
public int delete(@NonNull Uri uri, @Nullable String selection, @Nullable String[] selectionArgs) {
       
return 0;
    }
   
// 根据 Uri 修改 selection 指定的条件所匹配的全部记录
   
@Override
   
public int update(@NonNull Uri uri, @Nullable ContentValues values, @Nullable String selection, @Nullable String[] selectionArgs) {
       
return 0;
    }
}

 

UriMatcher 类:

         -UriMatcher matcher = new UriMatcher(UriMatcher.NO_MATCH);

                   -UriMatcher.NO_MATCH 表示不匹配任何路径的返回码

         -matcher.addURI(“com.imooc.provider”, “music”, 1);

                   -往UriMatcher 类里添加一个拼凑的 Uri

                   -UriMatcher 为一个 Uri 的容器,容器里面包含着我们即将可能要操作的 Uri

                   -如果通过 match() 方法匹配成功就返回 code 值

         -matcher.match(uri)

                   -首先与找通过 addURI() 方法添加进来的 Uri 匹配

                   匹配成功则返回设置的 code 值,反之,返回一个 UriMatcher.NO_MATCH 常量(-1)

 

ContentResolver:

         -使用ContentResolver 操作 ContentProvider 中的数据:

                   -当外部应用需要对 ContentProvider 中的数据进行添加、删除、修改和查询操作时,可以使用 ContentResolver 类来完成

                   -使用 Activity 提供的 getContentResolver() 方法获取 ContentResolver 对象

                   -ContentResolver 类提供了与 ContentResolver 类相同签名的四个方法

 

对ContentProvider 查询,插入,修改

<uses-permission android:name="android.permission.WRITE_CONTACTS"/>
<
uses-permission android:name="android.permission.READ_CONTACTS"/>

 

ContentResolver cr = getContentResolver();
Cursor cursor = cr.query(Contacts.
CONTENT_URI, new String[]{Contacts._ID, Contacts.DISPLAY_NAME},null,null,null);
if(cursor != null){
   
while(cursor.moveToNext()){
       
int id = cursor.getInt(cursor.getColumnIndex(Contacts._ID));
        Log.i(
"yimusanfendi", "_id = " + id);
        Log.i(
"yimusanfendi", "name = " + cursor.getString(cursor.getColumnIndex(Contacts.DISPLAY_NAME)));
        Cursor cursor1 = cr.query(Phone.
CONTENT_URI, new String[]{Phone.NUMBER, Phone.TYPE}, Phone.CONTACT_ID + " = " + id, null, null);
       
// 根据联系人 ID, 查询出联系人的电话号码
       
if(cursor1 != null){
           
while(cursor1.moveToNext()){
               
int type = cursor1.getInt(cursor1.getColumnIndex(Phone.TYPE));
               
if(type == Phone.TYPE_HOME){
                    Log.i(
"yimusanfendi", "家庭电话 = " + cursor1.getString(cursor1.getColumnIndex(Phone.NUMBER)));
                }
else if(type == Phone.TYPE_MOBILE){
                    Log.i(
"yimusanfendi", "手机号码 = " + cursor1.getString(cursor1.getColumnIndex(Phone.NUMBER)));
                }
            }
            cursor1.close();
        }
       
// 根据联系人的 ID 去查询联系人的邮箱地址
       
Cursor cursor2 = cr.query(Email.CONTENT_URI, new String[]{Email.DATA, Email.TYPE}, Email.CONTACT_ID + " = " + id, null, null);
       
if(cursor2 != null){
           
while(cursor2.moveToNext()){
               
int type = cursor2.getInt(cursor2.getColumnIndex(Email.TYPE));
                Log.i(
"yimusanfendi", "Email type = " + type);
               
if(type == Email.TYPE_HOME){
                    Log.i(
"yimusanfendi", "工作邮箱 = " + cursor2.getString(cursor2.getColumnIndex(Email.DATA)));
                }
            }
            cursor2.close();
        }
    }
    cursor.close();
}


// 向联系人中插入一条数据
ContentValues values = new ContentValues();
Uri uri = cr.insert(RawContacts.
CONTENT_URI,values);
Long raw_contact_id = ContentUris.parseId(uri);
values.clear();
values.put(StructuredName.
RAW_CONTACT_ID, raw_contact_id);
values.put(StructuredName.
DISPLAY_NAME, "张三");
values.put(StructuredName.
MIMETYPE, StructuredName.CONTENT_ITEM_TYPE);
uri = cr.insert(ContactsContract.Data.
CONTENT_URI, values);
// 插入电话信息
values.clear();
values.put(Phone.
RAW_CONTACT_ID, raw_contact_id);
values.put(Phone.
NUMBER, "13345678900");
values.put(Phone.
MIMETYPE, Phone.CONTENT_ITEM_TYPE);
uri = cr.insert(Data.
CONTENT_URI, values);

 

ContentProvider:

  1. 四大组件之一

android:name=""  相同属性
android:authorities="" 不同属性
android:exported=""    不同属性

  1. 接口的统一,并不能用于存储数据,只是为数据的存储或者添加等操作提供一个统一的接口
  2. 供多个应用程序共享数据,跨进程访问,自定服务端程序,系统通讯录等
  3. 设备存储数据,设备使用 ContentProvider 进行存储:通讯录,图片,音频,视频。
  4. 数据更新监听,

 

ContentProvider 优缺点:

  1. 数据访问统一接口。
  2. 跨进程访问
  3. 无法单独使用,需要结合数据库,sharedpreference 等

 

实现数据访问:

  1. ContentResolver
  • Context.getContentResolver()
  • 提供四个与 ContentProvider 相一致的方法
  • Transport implements IContentProvider

2, Uri

  • 协议: //用户名: 密码@主机名:端口号/路径/文件?参数1=值1&参数2=值2
  • 协议:content://
  • 域名:android:authorities
  • 路径:、contacts
  • 统一资源标示符,用于表示网络的图片。网址
  • 组成:协议 域名 路径
  • 作用:访问 ContentProvider
  • 不同:与其他组件(Intent)
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值