Sqlite in Android
在Android上保存本地数据有三种方式,SharedPreferences、Files和Sqlite。SharedPreferences主要是用来保存键值对形式的程序配置信息,与ini、properties操作类似。Files主要是用来保存图片、音视频等二进制数据。Sqlite是一种轻量级的关系型数据库,运行在android系统中为应用程序提供按照表结构存储数据的功能。Sqlite并不想与传统的大型的关系型数据库竞争,它的目标很明确,在硬件资源有限的情况下,为应用程序提供基本的数据库操作功能。因此,Sqlite在嵌入式系统、智能移动设备上广泛应用。同样,因为轻量的原因,Sqlite在处理并发性的能力上比起大型的数据库就要欠缺很多。这个问题不是Sqlite的缺陷而是它的特点,作为开发人员需要掌握它。Sqlite提供的是文件锁,即在同一时间内,只能有一个程序对它进行写操作,可以有多个程序对它进行读操作。这对于在嵌入式系统中运行的程序来说,应该足够了,并且程序的结构也可以很清晰,实现一个全局的写操作连接由整个程序共享,并且通过代码控制其并发操作,同时允许多个读操作连接并发执行。但是这种想法在Android上完全是错误的。
在Android的官网上有一个操作Sqlite的例子,正因为是官方发布的例子,那么可以想象有多少开发人员会在实际的项目中用到它。
public class FeedReaderDbHelper extends SQLiteOpenHelper {
// If you change the database schema, you must increment the database version.
public static final int DATABASE_VERSION = 1;
public static final String DATABASE_NAME = "FeedReader.db";
public FeedReaderDbHelper(Context context) {
super(context, DATABASE_NAME, null, DATABASE_VERSION);
}
public void onCreate(SQLiteDatabase db) {
db.execSQL(SQL_CREATE_ENTRIES);
}
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
// This database is only a cache for online data, so its upgrade policy is
// to simply to discard the data and start over
db.execSQL(SQL_DELETE_ENTRIES);
onCreate(db);
}
public void onDowngrade(SQLiteDatabase db, int oldVersion, int newVersion) {
onUpgrade(db, oldVersion, newVersion);
}
}
Google为Android开发出了一个SQLiteOpenHelper来管理Sqlite,包括创建数据库文件、创建表和数据库更新。在使用Sqlite时,程序实例化SQLiteOpenHelper的子类,然后再进行相关操作。
FeedReaderDbHelper mDbHelper = new FeedReaderDbHelper(getContext()); SQLiteDatabase db = mDbHelper.getWritableDatabase(); // Create a new map of values, where column names are the keys
ContentValues values = new ContentValues();
values.put(FeedEntry.COLUMN_NAME_ENTRY_ID, id);
values.put(FeedEntry.COLUMN_NAME_TITLE, title);
values.put(FeedEntry.COLUMN_NAME_CONTENT, content); // Insert the new row, returning the primary key value of the new row
long newRowId;
newRowId = db.insert(
FeedEntry.TABLE_NAME,
FeedEntry.COLUMN_NAME_NULLABLE,
values);
以上代码的标题赫然写着:Put Information into a Database。紧接着,下面代码的标题是:Read Information from a Database。
SQLiteDatabase db = mDbHelper.getReadableDatabase(); // Define a projection that specifies which columns from the database
// you will actually use after this query.
String[] projection = {
FeedEntry._ID,
FeedEntry.COLUMN_NAME_TITLE,
FeedEntry.COLUMN_NAME_UPDATED,
...
}; // How you want the results sorted in the resulting Cursor
String sortOrder =
FeedEntry.COLUMN_NAME_UPDATED + " DESC"; Cursor c = db.query(
FeedEntry.TABLE_NAME, // The table to query
projection, // The columns to return
selection, // The columns for the WHERE clause
selectionArgs, // The values for the WHERE clause
null, // don't group the rows
null, // don't filter by row groups
sortOrder // The sort order
);
看到这里,自信的开发人员可能已经感到在Android上操作Sqlite已经没有问题了,一个getWritableDatabase()和一个getReadableDatabase()。再加上Sqlite文件锁这个特点,那么程序理所当然是将所有涉及写操作的函数用synchronized保护起来,而读操作可以任意执行,强大的Google已经分离了WritableDatabase和ReadableDatabase。但事实并非如此,官网上的这个例子以及这些API的命名产生了很大的混淆。Kevin Galligan在他的博客《Android Sqlite Locking》中讨论过这个问题,并专门写了一个测试程序证明多线程操作Sqlite时会导致数据库操作失败。为了突出问题便于测试,在该测试程序的基础上做了一些修改以表明:在正常情况下,同一个SQLiteOpenHelper的getWritableDatabase()和一个getReadableDatabase()返回的是同一个SQLiteDatabase并且是线程安全的。因此,数据库操作会串行执行,而并不能实现一个写、多个读的结构。下面代码故意使用getReadableDatabase()实现写操作,getWritableDatabase()实现读操作,同时在LogCat中打印出不同线程中SQLiteDatabase对象的Hash Code。
public void insert(String desc) {
SQLiteDatabase readableDatabase = getReadableDatabase();
Log.i(Thread.currentThread().toString(), "insert: " + readableDatabase.hashCode());
ContentValues contentValues = new ContentValues();
contentValues.put("description", desc);
readableDatabase.insertOrThrow("session", null, contentValues);
}
public int count() {
SQLiteDatabase writableDatabase = getWritableDatabase();
Log.i(Thread.currentThread().toString(), "count: " + writableDatabase.hashCode());
Cursor cursor = writableDatabase.rawQuery(
"select count(*) from session", null);
cursor.moveToFirst();
return cursor.getInt(0);
}
通过上面的测试可以看到,一个SQLiteOpenHelper实际代表着一个数据库连接,对于一个数据库(在Sqlite中相当于一个.db文件)同时只能建立一个连接执行数据库操作,所以在程序中操作同一个数据库时SQLiteOpenHelper应该是唯一的,SQLiteDatabase对象可以有多个,并且能在多线程中执行操作。在Android自带的Samples里有一个Notepad例子,它采用了ContentProvider作为操作Sqlite的接口。这种方式就是让系统初始化一个SQLiteOpenHelper对象,供其它程序使用。它有一个明显的缺陷,针对每个表都需要实现一个专门的ContentProvider从而使得表间操作相当繁琐。
OrmLite这个持久层的框架解决了上述问题。在OrmLite里是使用OpenHelperManager来管理SQLiteOpenHelper,通过查看源代码可以看到OpenHelperManager使用了单例模式来确保SQLiteOpenHelper的唯一性。在下面的工厂方法中有两个小技巧:第一,Context实际上是获取的整个应用程序的上下文;第二,设置了一个引用计数器,instanceCount,确保程序通过releaseHelper()能够正确关闭SQLiteOpenHelper。
private static <T extends OrmLiteSqliteOpenHelper> T loadHelper(Context context, Class<T> openHelperClass) {
if (helper == null) {
if (wasClosed) {
logger.info("helper was already closed and is being re-opened");
}
if (context == null) {
throw new IllegalArgumentException("context argument is null");
}
Context appContext = context.getApplicationContext();
helper = constructHelper(appContext, helperClass);
logger.trace("zero instances, created helper {}", helper);
DaoManager.clearDaoCache();
instanceCount = 0;
}
instanceCount++;
logger.trace("returning helper {}, instance count = {} ", helper, instanceCount);
@SuppressWarnings("unchecked")
T castHelper = (T) helper;
return castHelper;
}
Sqlite in Android的更多相关文章
- sqlite在Android上的一个bug:SQLiteCantOpenDatabaseException when nativeExecuteForCursorWindow
更多内容在这里查看 https://ahangchen.gitbooks.io/windy-afternoon/content/ ::-/com.company.product W/System.er ...
- 利用SQLite在android上实现增删改查
利用SQLite在android上实现增删改查 方法: 一.直接利用database.execSQL()方法输入完整sql语句进行操作 这种方法适用于复杂的sql语句,比如多表查询等等 这里适合于增删 ...
- 利用SQLite在android上创建数据库
利用SQLite在android上创建数据库 方法: 1.创建我们的数据库类继承SQLiteOpenHelper类 完成相关函数的重写和数据库对象的初始化 public MySQLiteOpenHel ...
- sqlite与android交互 (封装)
学android已经有大概一周时间了吧 ,总感觉自己基础不怎么好,只能通过一点一点积累着敲来巩固平常的知识,有的时候就先不封装的敲一遍,再封装上,有些语句真的记不住,虽然知道他是什么意思,于是乎就反复 ...
- SQLite在Android程序中的使用方法,SQLite的增删查改方法
Sqlite: 1.一款用来实现本地数据存储的轻量级数据管理工具,是众多用来实现数据库管理的工具之一. 2.Android已经将SQLite的代码功能吸收在它的系统中,我们可以直接在Android程序 ...
- 关于SQLite在Android开发中的知识点总结
一.存放位置 1.内部存储-放在 data/data 底下, 也就是内部存储, 里面的文件以及文件夹是私有的, 其他 app 不能访问, 也随着 app 卸载而删除; 2.放在 sd 卡里面, 如果设 ...
- SQLite 在 Android 的应用
Android提供了创建和使用SQLite数据库的API(Application Programming Interface,应用程序编程接口). 在Android系统中,主要由类SQLiteData ...
- what are Datatypes in SQLite supporting android
As said at Datatypes In SQLite Version 3: Datatypes In SQLite Version 3 Most SQL database engines (e ...
- android安卓Sqlite数据库实现用户登录注册
看了很多别人写的安卓SQlite数据的操作代码,一点也不通俗易懂,我觉得我写的不错,而且安卓项目也用上了,所以在博客园里保存分享一下!建立一个类 并继承SQLiteOpenHelper public ...
随机推荐
- linux内核源码目录(转)
Linux用来支持各种体系结构的源代码包含大约4500个C语言程序,存放在270个左右的子目录下,总共大约包含200万行代码,大概占用58MB磁盘空间. 源代码所有在目录:/usr/src/linux ...
- 【phpMyAdmin】更改配置文件连接到其他server
默认phpMyAdmin安装完毕后对机器的访问mysql,但有时我们需要访问其它server的mysql数据库,所以我们需要配置. 真,phpMyAdmin已经为我们做了配置的选项.可是须要我们进行一 ...
- Wowza流媒体Live直播和VOD点播配置实战-attach
Wowza是当今可以说最流行的流媒体服务器之一,近来因为需要搭建相应的服务器,但又不想用camera等作真实的直播,所以想办法用媒体文件转换成直播流再提供给Wowza进行直播.这里把该设置步骤以及设计 ...
- 快速构建Windows 8风格应用9-竖直视图
原文:快速构建Windows 8风格应用9-竖直视图 本篇博文主要介绍竖直视图概览.关于竖直视图设计.如何构建竖直视图 竖直视图概览 Windows 8为了支持旋转的设备提供了竖屏视图,我们开发的应用 ...
- String 的intern() 方法说明
1.说明 Java中string.intern()方法调用会先去字符串常量池中查找相应的字符串,如果字符串不存在,就会在字符串常量池中创建该字符串然后再返回. 2.源码说明 public native ...
- NCache实现Oracle数据与分布式缓存数据同步的3个步骤
多层次结构的应用程序是目前发展的趋势,这种程序都需要庞大的数据库支持.而数据传输的能力直接影响程序性能,成为程序可扩展性的瓶颈.因此很多开发者开始在程序中使用内存分布式缓存来提高程序性能. 同时,内存 ...
- 如何通过js获取iframe框架中的内容
在父窗口中获取iframe中的元素 IE下:格式:window.frames["iframe的name值"].document.getElementById("ifram ...
- PHP:Cannot modify header information - headers already sent by错误的解决方案
<?php ob_start();setcookie("username","test",time()+3600);echo "the user ...
- [置顶] EasyMock的简单使用
EasyMock总览 下面,我将讲述如何使用JUnit和EasyMock框架来进行单元测试. 在现实情况下,我们通常是在一些类里使用另外的一些类.在进行真正的测试之前,你可能需要做很多的工作,比喻说安 ...
- 【转】Android 系统菜单与自定义菜单
Android 系统菜单与自定义菜单实现方法如下:系统菜单显示DefaultMenu.java package com.wxz.menu; import com.wxz.menu.R; import ...