SQLite数据库学习小结(2)

3. SQLite的Frameworks层实现

3.1 Frameworks层架构

Android系统方便应用使用,在Frameworks层中封装了一套Content框架,之所以叫Content框架而不叫数据库框架之类的,是因为这里Content不一定是来自数据库的内容,也可以是来自其他数据源的内容,开发人员只需要知道如何使用ContentResovler和ContentProvider就可以在应用进程之间共享数据了。

  这里我们只讨论数据源是数据库的ContentProvider,开发人员需要实现一个SQLiteOpenHelper的派生类,它使用了一系列SQLite相关的类封装了native层的SQLite动态库的接口方法,那么SQLite在Frameworks层是如何封装的,我们在使用SQLite时又需要注意些什么呢?

我们先来看一看基于SQLite的Content框架的整体架构:

Android系统在Frameworks层

  1、ContentResolver:Content框架的客户端,对应用提供增、删、改、查的接口方法,通过Authority指定目标Content服务,并连接到服务端执行数据操作。

  2、ContentProvider:Content框架的服务端,Android应用四大组件之一,通过Authority注册到PMS,在运行时有客户端请求时由AMS调度来响应客户端的数据操作。

  3、SQLiteOpenHelper:管理SQLite的帮助类,提供获取SQLIteDatabase实例的方法,它会在第一次使用数据库时调用获取实例方法时创建SQLiteDatabase实例,并且处理数据库版本变化,开发人员在实现ContentProvider时都要实现一个自定义的SQLiteOpenHelper类,处理数据的创建、升级和降级。

  注意:

    (1)不管是调用getWritableDatabase方法还是getReadableDatabase方法,SQLIteOpenHelper都会以可读写模式打开数据库。

    (2)如果应用程序想以WAL模式打开数据库,可在自定义SQLiteOpenHelper类的构造方法中调用setWriteAheadLoggingEnabled(true)。

    SQLiteOpenHelper.java

    

  4、SQLiteDatabase:代表一个打开的SQLite数据库,提供了执行数据库操作的接口方法。如果不需要在进程之间共享数据,应用程序也可以自行创建这个类的实例来读写SQLite数据库。

  5、SQLiteSession:SQLiteSession负责管理数据库连接和事务的生命周期,通过SQLiteConnectionPool获取数据库连接来执行具体的数据库操作。

  6、SQLiteConnectionPool:数据库连接池,管理所有打开的数据库连接(Connection)。所有数据库连接都是通过它来打开,打开后会加入连接池,在读写数据库时需要从连接池中获取一个数据库连接来使用。

  7、SQLiteConnection:代表了数据库连接,每个Connection封装了一个native层的sqlite3实例,通过JNI调用SQLite动态库的接口方法操作数据库,Connection要么被Session持有,要么被连接池持有。

  8、CursorFactory:可选的Cursor工厂,可以提供自定义工厂来创建Cursor。

  9、DatabaseErrorHandler:可选的数据库异常处理器(目前仅处理数据库Corruption),如果不提供,将会使用默认的异常处理器。

  10、SQLiteDatabaseConfiguration:数据库配置,应用程序可以创建多个到SQLite数据库的连接,这个类用来保证每个连接的配置都是相同的。

  11、SQLiteQuery和SQLiteStatement:从抽象类SQLiteProgram派生,封装了SQL语句的执行过程,在执行时自动组装待执行的SQL语句,并调用SQLiteSession来执行数据库操作。这两个类的实现应用了设计模式中的命令模式。

3.2 关键模块实现

本节介绍几个关键模块的实现和使用时需要注意的事项。

3.2.1 SQLiteSession

  Android系统Frameworks层的数据库读写操作都是通过SQLiteSession完成的,SQLiteSession负责管理数据库连接和事务的生命周期。

  一个SQLiteDatabse实例可以同时持有多个活跃的Session(但是为防止死锁,每个线程只能持有一个DB的Session),每个Session在执行SQL语句时获取数据库连接,在SQL语句执行结束后释放数据库连接,Session只有在只执行SQL语句期间保持数据库连接,执行完后就释放了。这个特性也是连接池的实现基础。

SQLiteSession.java

  如果连接池中所有连接都已分配出去了,那么获取连接的SQLiteSession会阻塞直到有可用连接为止。

  所以,在使用SQLite时需要注意以下几点:

  (1)不要在UI线程中执行数据库操作。

  (2)执行的事务尽量短。

  (3)如果读写事务很长,可以考虑使用yieldTransaction()方法先提交部分事务,给其他事务执行的机会。

3.2.2 SQLiteConnectionPool

  数据库连接池保持所有打开的数据库连接,在任何时候,一个数据库连接要么被连接池持有,要么被一个SQLiteSession持有,如果SQLiteSession使用完数据库连接,必须把它还给连接池。如果连接池中所有的连接都已被占用,则待执行的事务要等待有空闲的连接才能执行。

  目前Android系统的实现中,如果以非WAL模式打开数据库,连接池中只会保持一个数据库连接,如果以WAL模式打开数据库,连接池中的最大连接数量则根据系统配置决定,默认配置是两个。

SQLiteConnectionPool.java:

  虽然名为连接池,但是从源码来看,目前实现的池中只有一个数据库连接(以后的Android版本可能会扩展),所以如果应用程序中有大量的并发数据库读和写操作的话,每个操作的时长都可能受到影响,所以数据库操作应放在工作线程中执行,以免影响UI响应。

3.2.4 Cursor

  从ContentProvider查询的数据结果是放在Cursor中返回给客户端的,在客户端看来Cursor就是一个数据容器,但隐藏在Cursor后面的实现方式很灵活,它的数据既可以不是从数据库返回的,也可以是在使用时才真正加载的,很好的体现了面向对象编程的特性和优点。

  在Frameworks中把Cursor定义为了一个接口,它的定位是可以随机访问的数据集,在接口中定义了访问数据集的通用方法。业务可以根据自己的需要实现一个Cursor,具体怎么实现接口的方法由具体实现决定,所以Cursor有很多子类来满足不同场景的需要。

  通过SQLiteDatabase返回的就是其中的SQLiteCursor,如果数据不是从数据库返回的,开发人员也可以在ContentProvider中动态创建一个MatrixCursor,然后填充数据并返回给客户端。

  从Cursor接口和其派生类的定义来看它们都没有实现Parcelable接口,那么它是怎么跨进程传递的呢?这需要Cursor首先要解决两个问题。

  第一个问题:Cursor没有实现Parcelable接口,一个Cursor实例怎么跨进称传递呢?答案是传递的不是具体数据,而是Binder引用,即在ContentProvider端创建Cursor的Binder服务端实例,然后把Binder应用传递给客户端,在客户端通过这个Binder引用跨进程获取查询到的数据的。这里Frameworks定义了一个接口:IBulkCursor。

  IBulkCursor定义了跨进程的Cursor需要实现的接口方法,其中getWindow()用来获得数据窗口,onMove()用来移动Cursor的位置。

  那么第二个问来了:我们知道通过Binder传递的数据大小有限(1MB),而查询到的数据大小可能超出限制,那么怎么跨进程传递数据呢?既然数据大小不定,那么我们就不通过Binder传递数据了,而是通过共享内存传递数据,这块共享内存是封装在CursorWindow中的。

  CursorWindow就是数据窗口,它在服务端分配(窗口大小有Android系统配置决定)并传递到客户端,客户端再映射到自己的进程空间中,这样,服务端填充的数据就可以被客户端读取到了。上面IBulkCursor接口中定义的getWindow()方法就是获取CursorWindow的。

  CursorWindow在初始化时是空的,在调用Cursor的moveToXXX方法时会通过IBulkCursor的onMove()方法调用服务端的Cursor去填充数据窗口的内容。

CursorWindow.java

frameworks\base\libs\androidfw\CursorWindow.cpp

  服务端创建共享内存。

CursorWindow.java

frameworks\base\libs\androidfw\CursorWindow.cpp

  客户端映射共享内存到进程内存空间。

  上面知道了Frameworks解决跨进程传递Cursor数据的思路,我们再来看下具体执行跨进程传递数据的类:CursorToBulkCursorAdapter(服务端)和BulkCursorToCursorAdapter(客户端)。

服务端:

客户端:

  在ContentProviderNatvie类中可以看到Cursor的专递过程。

服务端:

ContentProviderNative.onTransact()

CursorToBulkCursorAdaptor.java

  服务端在通过ContentProvider得到Cursor后,用它创建一个CursorToBulkCursorAdaptor实例,然后把adaptor封装在一个实现了Parcelable接口的BulkCursorDescriptor实例中返回给客户端。

  虽说是在使用时才填充数据窗口,但是实际上传递Cursor的过程中,从上面代码可以看到服务端已经替应用程序填充过一次数据了:mCursor.getCount()。

SQLiteCursor.java

客户端:

ContentProviderProxy.query()

BulkCursorToCursorAdaptor.java

在得到服务端返回的数据后创建一个BulkCursorDescriptor实例,在用它初始化一个BulkCursorToCursorAdapter实例返回给应用程序使用。

时间: 2024-10-11 06:40:56

SQLite数据库学习小结(2)的相关文章

SQLite数据库学习小结(1)

1. SQlite概述 SQLite是一款轻量.快速.跨平台的嵌入式数据库,是遵守ACID(注:ACID指数据库事务正确执行的四个基本要素的缩写.包含:原子性(Atomicity).一致性(Consistency).隔离性(Isolation).持久性(Durability))的关系型数据库管理系统,它包含在一个相对小的C库中. SQLite 的设计目标是简单,从这种意义上说,SQLite 和其他很多现代的 SQL 数据库都不相同.SQLite 力求简单,即使这导致了它的某些特征偶尔执行效率比较

Android数据存储之SQLite 数据库学习

Android提供了五种存取数据的方式 (1)SharedPreference,存放较少的五种类型的数据,只能在同一个包内使用,生成XML的格式存放在设备中 (2) SQLite数据库,存放各种数据,是一个轻量级的嵌入式数据库 (3) File文件,通过读取写入方式生成文件存放数据 (4) ContentProvider,主要用于让其他应用程序使用保存的数据 (5)通过网络获取数据和写入数据到网络存储空间 SQLite 数据库介绍 SQLite 是一款轻量级的关系型数据库,它的运算速度非常快,占

sqlite数据库学习

1.0版代码: package com.swust.sqlitedatabase.test; import com.swust.sqlitedatabase.myOpenHelper; import android.database.sqlite.SQLiteDatabase; import android.test.AndroidTestCase; public class TestCase extends AndroidTestCase { public void test(){ //第一个

android简单登陆和注册功能实现+SQLite数据库学习

最近初学android,做了实验室老师给的基本任务,就是简单的登陆和注册,并能通过SQLite实现登陆,SQlLite是嵌入在安卓设备中的 好了下面是主要代码: 数据库的建立: 这里我只是建立了一个用简单的存储用户名和密码的表单 MyDBHelper.java public class MyDBHelper extends SQLiteOpenHelper { public static final String CREATE_USERDATA="create table userData(&q

Oracle数据库学习小结(一)

今天要跟大家来聊一聊俺在使用Oracle数据库的这段时间里面学到的一些知识. 俺最近出来工作了,来到的公司是使用的Oracle数据库,之前一直使用的是SQL的数据库,在性质上二者前者是收费的,后者是开源的:在使用上主要还是靠自己探索,下面说说俺的一些收获.在前段时间俺一直在鼓捣组长分下来的一个任务,定时同步数据的一个小程序,俺的Oracle之旅也是从这里才算是正式开始的. 近期的学习中俺了解到Oracle数据库本身是自带三个默认用户的:sys(网络管理员).system(本地普通管理员).sco

Android:Sqlitedatabase学习小结

今天刚刚学习完Sqlite数据库的基础知识,随即把学到的东西记录下来,以便随后查阅,以下是自己对Sqlite数据库的小结:1.Sqlite简介       Sqlite是一款轻型的数据库,它包含在一个相对小的C库中,它的设计目标是嵌入式的,由于它占用资源非常少,可能只需要几百K的内存就可以了,并且支持Windows/Linux/Unix等等主流的操作系统,同时可以和很多种程序语言相结合,比如:C#/Java/php等,所以在嵌入式设备中特别受欢迎,这一点也正好符合android的开发要求,所以在

android学习十一(android的SQLite数据库的使用)

SQLite是一款轻量级的关系型数据库,它运算速度快,占用资源少,通常只需要几百k的内存就够了,支持标准的sql语法和数据库的ACID事务.在android中为了能够更加方便的管理数据库,专门提供了一个SQLiteOpenHelper帮助类,借助这个类就可以非常简单的对数据库进行创建和升级. SQLiteOpenHelper是一个抽象类,如果我们要使用的话,就需要创建一个自己的类去继承它.SQLiteOpenHelper中有2个抽象方法,分别是onCreate和onUpgrade方法,我们必须在

Android学习---SQLite数据库的增删改查和事务(transaction)调用

上一篇文章中介绍了手工拼写sql语句进行数据库的CRUD操作,本文将介绍调用sqlite内置的方法实现CRUD操作,其实质也是通过拼写sql语句. 首先,创建一个新的android项目: 其次,查看代码实现增删查改: 1.创建DB工具类 MyDBHelper.java(创建数据库的操作) package com.amos.android_db; import android.content.Context; import android.database.sqlite.SQLiteDatabas

Android学习笔记--Sqlite数据库

前几天学习了Android中的数据存储,包括文件存储,SharedPreferences存储,还有就是Acndroid中的特色:SQLite数据库存储了.让我比较惊讶的是Android中竟然内嵌了一个轻量型的数据库SQLite数据库,使得本地的数据持久化有了一个质的飞跃. 通过前两天上课听老师讲解,和课下自己看书总结,想在博客上把自己对于SQLite数据库的理解做一下总结,也是方便之后的复习.Android为了让我们能够更加方便的管理数据库,专门提供了SQLiteOpenHelper帮助类,这个