Android内部数据库

Android内部数据库文件

Android中很多内部数据都是保存在数据库中,例如 短信,通讯录,文件信息等。这些数据可以通过它们的Uri来查询,
常用的数据类型及其对应的数据库文件路径如下表。

数据类型 数据库路径
通讯录 /data/data/com.android.providers.contacts/databases/contacts2.db
短信 /data/data/com.android.providers.telephony/databases/mmssms.db
文件信息 /data/data/com.android.providers.media/databases/external.db

数据库信息查询

查询方法为:

Context.getContentResolver().query(Uri uri, 
        String[] projection, String selection, 
        String[] selectionArgs, String sortOrder)

query方法包含五个参数,分别解析如下。

  • uri:要查询的表格Uri,对应SQL语句中的from子句
  • projection:要提取的数据库表格中的列名,对应SQL语句中的select子句
  • selection:查询条件,查询条件中可以使用?表示一个变量,其值会被替换为对应selectionArgs参数中对应的值。selection对应SQL语句中的where子句。
  • selectionArgs:查询条件中用到的变量。
  • sortOrder:查询结果排序方式。对应SQL语句中的ORDER BY子句。
    例如:
Uri uri = MediaStore.Images.Media.EXTERNAL_CONTENT_URI;
String columns[] = new String[] {MediaStore.Images.Media._ID, MediaStore.Images.Media.DATA};
String selection = IMAGE_BUCKET_DISPLAY_NAME + " IN (?,?,?)";
String[] selectionArgs = {"Screenshots", "Camera", "Download"};
String sortOrder = MediaStore.Images.Media.DATE_ADDED + " DESC";
mContext.getContentResolver().query(uri, columns, selection, selectionArgs, sortOrder);

其对应的SQL语句为

SELECT MediaStore.Images.Media._ID, MediaStore.Images.Media.DATA
        FROM MediaStore.Images.Media.EXTERNAL_CONTENT_URI 
        WHERE IMAGE_BUCKET_DISPLAY_NAME IN ("Screenshots", "Camera", "Download") 
        ORDER BY MediaStore.Images.Media.DATE_ADDED DESC

数据库查询Uri

在查询数据库中信息时需要指定查询的Uri。
常用的数据类型及其查询Uri如下表。

数据类型 查询Uri
通讯录 content://com.android.contacts/data
短信 content://sms/ 全部信息
文件信息 content://media/external/images/media 图片数据

使用ContentObserver监听数据库变化

ContentObserver使用方法

虽然可以通过query方法来查询数据库获取信息,但如果数据库中的信息发生了改变,那么获取到的信息也就随之失效。例如某次使用query方法查询到在Download目录中有一个图片,文件名为photo.jpg。但过段时间后可能这张图片被删除或重命名了。如果这时再去使用原先查询到的信息去读取这张图片,就会找不到文件。
因此,需要能够在数据库内容发生改变时通知应用去更新数据。
在Android中可以通过ContentObserver来监听数据库变化。
其使用方法为:

1、定义一个类继承自ContentObserver类

派生类中必须重写其构造方法和onChange()方法。
构造方法的原型为

public ContentObserver(Handler handler) { mHandler = handler;
}

参数handle用来向创建handler的线程发送消息。当ContentObserver对象收到数据库变化的消息后,如果handler为null,会直接调用onChange()方法,这时onChange()是在非UI线程中执行的。如果handler不为null,那么ContentObserver对象会调用handler的post方法将消息发送到创建handler的线程去执行onChange()方法。所以如果需要在onChange()中去执行查询数据库之类比较耗时的操作,则可以传入null handler。如果需要在onChange中更新UI界面,则必须在UI线程中new一个handler,并作为参数传入。

onChange()有两个重载的方法,其原型为

public void onChange(boolean selfChange) {}
public void onChange(boolean selfChange, Uri uri) {}

在低于API level 16 的系统中,会调用第一种不带Uri参数的onChange函数。在大于等于API level 16 的系统中,会调用带Uri参数的onChange函数。为了兼容性,两种onChange函数都需要重写。
Android SDK给出的建议实现方式为

public void onChange(boolean selfChange) {
    onChange(selfChange,null); 
}
public void onChange(boolean selfChange, Uri uri) {
    ....
}

即不带Uri参数的onChange方法直接调用带参数的onChange方法,并传入null作为Uri。onChange在处理时需要对Uri为null的情况做判断。

2、 注册监听某个数据库表格的变化
注册监听的方法为:

Context.getContentResolver().registerContentObserver(Uri uri,
            boolean notifyForDescendents,
            ContentObserver observer)

registerContentObserver方法包含三个参数。

  • uri:要监听的数据库表格Uri
  • notifyForDescendents:当要监听的uri相关联的uri发生改变时是否通知,如果true,表示需要通知。如果为false,表示只关心要监听的uri,对其关联的uri不关心,发生数据改变时不通知。
  • observer:当监听的uri发生改变时调用的回调对象。

3、解除监听
必须确保registerContentObserver和unregisterContentObserver的成对调用。在不需要监听时,调用unregisterContentObserver来解除监听。方法为:

mContext.getContentResolver().unregisterContentObserver(observer);

通常的做法是在Activity的onResume中调用registerContentObserver,在Activity的onPause中调用unregisterContentObserver。如果没有调用unregisterContentObserver,则会造成内存泄漏,相关的资源无法被回收。

使用ContentObserver监听图片数据库变化示例

使用ContentObserver监听图片数据库变化代码如下

    public class ImageDataChangeObserver extends ContentObserver {
        public ImageDataChangeObserver(Handler handler) {
            super(handler);
        }
        @Override
        public void onChange(boolean selfChange) {
            onChange(selfChange, null);
        }
        @Override
        public void onChange(boolean selfChange, Uri uri) {
            Log.i("listview1", uri.toString());
        }
    }
    private ImageDataChangeObserver mObserver;
    public void registerContentObserver(Context context) {
        mObserver = new ImageDataChangeObserver(null);
        context.getContentResolver().registerContentObserver(
            MediaStore.Images.Media.EXTERNAL_CONTENT_URI, false, observer);
    }

通过以上代码,可以监听图片数据库发生的改变。当图片数据库发生改变时会自动调用observer的onChange方法,通知监听对象更新数据和界面。

ContentObserver的缺陷

  1. 通过ContentObserver可以知道某个数据库发生了变化,但是并不知道是数据库中哪个记录发生了改变。也不知道该变化的具体操作是什么。
  2. 在监听图片数据库变化时,由于ContentObserver没有提供变化的任何具体信息,必须在应用中自行决定何时去读取变化后数据。例如一次性向手机中拷贝10张图片,则会陆续收到10次数据库改变的通知,也就是说onChange()会被执行10次,中间间隔时间很短。在onChange()中必须合并这些更新,不能每次都去读取一次数据库。
  3. 不同手机在数据库的更新机制和onChange触发时机上存在差异。同样是在监听图片数据库变化时,
    对华为3X手机
    当添加一张图片时,onChange会被调用一次,Uri为content://media/external/images/media
    当重命名一张图片时,onChange会被调用两次,第一次Uri为content://media/external,第二次Uri为content://media/external/images/media
    当删除一张图片时,onChange会被调用一次,Uri为content://media/external
    不仅图片,任何文件的变化都会导致onChange被调用
    对魅族MX3手机
    当添加一张图片时,onChange会被调用一次,Uri为content://media/external/images/media?forbid_generate=true
    当重命名一张图片时,onChange会被调用一次,Uri为content://media/external/images/media?forbid_generate=true
    当删除一张图片时,onChange会被调用一次,Uri为content://media/external
    除了图片,其他文件的变化都不会导致onChange被调用,将图片重命名为非图片扩展名,不会调用。但是将非图片重命名为图片扩展名,会调用。

你可能感兴趣的:(android,数据库查询,内容监听,图片数据库)