Android内置了轻量级的数据库SQLite,这里将自己理解作个记录,方便自己复习。

  一.首先,创建SQLite数据库比较常见的方式是通过Android提供的SQLiteOpenHelper来实现,先贴一段代码:

import android.Manifest;
import android.content.Context;
import android.content.pm.PackageManager;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.database.sqlite.SQLiteDatabase.CursorFactory;
import java.io.File; public class MainActivity extends AppCompatActivity implements View.OnClickListener{
private static final String TAG = "CustomSQLiteOpenHelper";
private Button mButton;
private CustomSQLiteOpenHelper customSQLiteOpenHelper;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mButton = (Button)findViewById(R.id.btn1);
mButton.setOnClickListener(this);
customSQLiteOpenHelper = new CustomSQLiteOpenHelper(this);//创建SQLIteOpenHelper对象(1)
} @Override
public void onClick(View v) {
switch (v.getId()) {
case R.id.btn1:
customSQLiteOpenHelper.getWritableDatabase();//通过getWritableDatabase()方式来新建SQLite数据库(2)
break;
default:
break;
}
} class CustomSQLiteOpenHelper extends SQLiteOpenHelper {
private static final String DATABASE_NAME = "book_store.db";//数据库名字
private static final int DATABASE_VERSION = 1;//数据库版本号
private static final String CREATE_TABLE = "create table bookStore ("
+ "id integer primary key autoincrement,"
+ "book_name text, "
+ "author text, "
+ "price real)";//数据库里的表 public CustomSQLiteOpenHelper(Context context) {
this(context, DATABASE_NAME, null, DATABASE_VERSION);
} private CustomSQLiteOpenHelper(Context context, String name, CursorFactory factory, int version) {
super(context, name, factory, version);//调用到SQLiteOpenHelper中
Log.d(TAG,"New CustomSQLiteOpenHelper");
} @Override
public void onCreate(SQLiteDatabase db) {
Log.d(TAG,"onCreate");
db.execSQL(CREATE_TABLE);
} @Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { }
}
}

  在MainActivity布局文件activity_main.xml里,只有一个Button控件,点击该Button,就会通过调用SQLiteOpenHelper.getWritableDatabase()的方式来创建名为DATABASE_NAME的数据库。数据库是否新建成功,可以在/data/data/<Package_Name>/databse/目录下确认。

  用于创建SQLite数据库比较重要的代码:(1)建立SQLiteOpenHelper对象;(2)调用getWritableDatabase()来建立SQLite数据库。下面来看看这两句代码主要做了什么。

  二.代码分析

  (1)建立SQLiteOpenHelper对象

    /**
* Create a helper object to create, open, and/or manage a database.
* The database is not actually created or opened until one of
* {@link #getWritableDatabase} or {@link #getReadableDatabase} is called.*/
public SQLiteOpenHelper(Context context, String name, CursorFactory factory, int version,
DatabaseErrorHandler errorHandler) {
if (version < 1) throw new IllegalArgumentException("Version must be >= 1, was " + version); mContext = context;
mName = name;
mFactory = factory;
mNewVersion = version;
mErrorHandler = errorHandler;
}

  从注释中就可以看出,该构造函数只是建立SQLiteOpenHelper对象,以及进行了一些变量的初始化动作,所以正真创建SQLite数据库地方不是在这里,而是在getWritableDatabase() 或者 getReadableDatabase()中实现的。接下来看这两个函数是如何创建SQLite数据库的。

  (2)调用getWritableDatabase()函数

    /**
* Create and/or open a database that will be used for reading and writing.
* The first time this is called, the database will be opened and
* {@link #onCreate}, {@link #onUpgrade} and/or {@link #onOpen} will be
* called.
*
* <p>Once opened successfully, the database is cached, so you can
* call this method every time you need to write to the database.
* (Make sure to call {@link #close} when you no longer need the database.)
* Errors such as bad permissions or a full disk may cause this method
* to fail, but future attempts may succeed if the problem is fixed.</p>
*
* <p class="caution">Database upgrade may take a long time, you
* should not call this method from the application main thread, including
* from {@link android.content.ContentProvider#onCreate ContentProvider.onCreate()}.
**/
public SQLiteDatabase getWritableDatabase() {
synchronized (this) {
return getDatabaseLocked(true);
}
} /**
* Create and/or open a database. This will be the same object returned by
* {@link #getWritableDatabase} unless some problem, such as a full disk,
* requires the database to be opened read-only. In that case, a read-only
* database object will be returned. If the problem is fixed, a future call
* to {@link #getWritableDatabase} may succeed, in which case the read-only
* database object will be closed and the read/write object will be returned
* in the future.
*
* <p class="caution">Like {@link #getWritableDatabase}, this method may
* take a long time to return, so you should not call it from the
* application main thread, including from
* {@link android.content.ContentProvider#onCreate ContentProvider.onCreate()}.
**/
public SQLiteDatabase getReadableDatabase() {
synchronized (this) {
return getDatabaseLocked(false);
}
}

  getWritableDatabase() & getReadableDatabase()函数中都是调用getDatabaseLocked()函数,仅仅是传的参数不同,这两个函数的功能基本上是相同的。注释中也举例说明了当磁盘满了是两者间的区别。下面接着看getDatabaseLocked()中的内容。

    private SQLiteDatabase getDatabaseLocked(boolean writable) {
if (mDatabase != null) {//第一次进来时,mDatabase为空
if (!mDatabase.isOpen()) {
// Darn! The user closed the database by calling mDatabase.close().
mDatabase = null;//当mDatabase被close后,再次打开时需要重新创建
} else if (!writable || !mDatabase.isReadOnly()) {
// The database is already open for business.
return mDatabase;//直接返回当前mDatabase
}
} if (mIsInitializing) {
throw new IllegalStateException("getDatabase called recursively");
} SQLiteDatabase db = mDatabase;
try {
mIsInitializing = true; if (db != null) {
if (writable && db.isReadOnly()) {
//数据库从ReadOnly变为ReadWrite时调用
db.reopenReadWrite();
}
} else if (mName == null) {
db = SQLiteDatabase.create(null);
} else {
try {
//DEBUG_STRICT_READONLY 默认为FALSE,因为实际应用过程中,只读数据库不实用,自己Debug可以设置为TRUE测试。
if (DEBUG_STRICT_READONLY && !writable) {
final String path = mContext.getDatabasePath(mName).getPath();
db = SQLiteDatabase.openDatabase(path, mFactory,
SQLiteDatabase.OPEN_READONLY, mErrorHandler);
} else {
db = mContext.openOrCreateDatabase(mName, mEnableWriteAheadLogging ?
Context.MODE_ENABLE_WRITE_AHEAD_LOGGING : 0,
mFactory, mErrorHandler);
}
} catch (SQLiteException ex) {
if (writable) {
throw ex;
}
Log.e(TAG, "Couldn't open " + mName
+ " for writing (will try read-only):", ex);
final String path = mContext.getDatabasePath(mName).getPath();
db = SQLiteDatabase.openDatabase(path, mFactory,
SQLiteDatabase.OPEN_READONLY, mErrorHandler);
}
} onConfigure(db);
//下面数据库版本信息发生变化时调用
final int version = db.getVersion();//第一次创建,version值为0
if (version != mNewVersion) {
if (db.isReadOnly()) {
throw new SQLiteException("Can't upgrade read-only database from version " +
db.getVersion() + " to " + mNewVersion + ": " + mName);
} db.beginTransaction();
try {
if (version == 0) {
onCreate(db);//该函数为抽象函数,可以在重载该函数时执行创建数据库命令
} else {
if (version > mNewVersion) {
//版本降低时调用,但是默认不支持该操作,会报“SQLiteException:Can't downgrade database from version”
onDowngrade(db, version, mNewVersion);
} else {
//该函数为抽象函数,可以添加自己想实现的效果代码
onUpgrade(db, version, mNewVersion);
}
}
db.setVersion(mNewVersion);
db.setTransactionSuccessful();
} finally {
db.endTransaction();
}
} onOpen(db); if (db.isReadOnly()) {
Log.w(TAG, "Opened " + mName + " in read-only mode");
} mDatabase = db;
return db;
} finally {
mIsInitializing = false;
if (db != null && db != mDatabase) {
db.close();
}
}
}

  其中DEBUG_STRICT_READONLY变量默认为False,所以正常建立的数据库都是Readable & Writable。所以上面新建数据库代码主要可以分为三个部分:

  1.如果mDataBase不为空,并且处于打开状态时,直接返回,所以多次调用getWritableDatabase()或getReadableDatabase()和只调用一次的效果是一样的。

  2.如果mDataBase为空,则调用mContext.openOrCreateDatabase()来创建数据库。

  3.当数据库版本信息发生变化时,做相应的升/降版本处理。

  那么mContext.openOrCreateDatabase()函数里又做了哪些事情呢?进入到ContextImpl.java文件中一探究竟。

    @Override
public SQLiteDatabase openOrCreateDatabase(String name, int mode, CursorFactory factory,
DatabaseErrorHandler errorHandler) {
File f = validateFilePath(name, true);//判断数据库name是否有效
int flags = SQLiteDatabase.CREATE_IF_NECESSARY;
if ((mode & MODE_ENABLE_WRITE_AHEAD_LOGGING) != 0) {
flags |= SQLiteDatabase.ENABLE_WRITE_AHEAD_LOGGING;
}
     //openDatabase
SQLiteDatabase db = SQLiteDatabase.openDatabase(f.getPath(), factory, flags, errorHandler);
setFilePermissionsFromMode(f.getPath(), mode, 0);//设置文件读写权限Mode
return db;
}     private File validateFilePath(String name, boolean createDirectory) {
        File dir;
        File f;         if (name.charAt(0) == File.separatorChar) {
       //可以自己定义数据库所在目录,但是要注意目录权限,比如以User身份往System所属的目录里添加文件
            String dirPath = name.substring(0, name.lastIndexOf(File.separatorChar));
            dir = new File(dirPath);
            name = name.substring(name.lastIndexOf(File.separatorChar));
            f = new File(dir, name);
        } else {
            dir = getDatabasesDir();
            f = makeFilename(dir, name);
        }         if (createDirectory && !dir.isDirectory() && dir.mkdir()) {
            FileUtils.setPermissions(dir.getPath(),
                FileUtils.S_IRWXU|FileUtils.S_IRWXG|FileUtils.S_IXOTH,
                -1, -1);
        }         return f;
    }     private File getDatabasesDir() {
        synchronized (mSync) {
            if (mDatabasesDir == null) {
//这个得到的结果就是/data/data/<PackageName>/database
                mDatabasesDir = new File(getDataDirFile(), "databases");
            }
            if (mDatabasesDir.getPath().equals("databases")) {
                mDatabasesDir = new File("/data/system");
            }
            return mDatabasesDir;
        }
    }     @SuppressWarnings("deprecation")
    static void setFilePermissionsFromMode(String name, int mode,
            int extraPermissions) {
        int perms = FileUtils.S_IRUSR|FileUtils.S_IWUSR
            |FileUtils.S_IRGRP|FileUtils.S_IWGRP
            |extraPermissions;//默认权限为-rx-rx---
        if ((mode&MODE_WORLD_READABLE) != 0) {
            perms |= FileUtils.S_IROTH;
        }
        if ((mode&MODE_WORLD_WRITEABLE) != 0) {
            perms |= FileUtils.S_IWOTH;
        }
        if (DEBUG) {
            Log.i(TAG, "File " + name + ": mode=0x" + Integer.toHexString(mode)
                  + ", perms=0x" + Integer.toHexString(perms));
        }
        FileUtils.setPermissions(name, perms, -1, -1);
    }

  从上面代码可以知道为何新建的数据库的目录为/data/data/<PackageName>/database/,并且权限为-rx-rx--,再贴下之前新建数据库得到的结果:

  其中SQLiteDatabase db = SQLiteDatabase.openDatabase(f.getPath(), factory, flags, errorHandler);应该是正真建立数据库的地方。

    public static SQLiteDatabase openDatabase(String path, CursorFactory factory, int flags,
DatabaseErrorHandler errorHandler) {
SQLiteDatabase db = new SQLiteDatabase(path, flags, factory, errorHandler);
db.open();
return db;
}

  这样最终就可以得到SQLite数据库了。

Android 创建SQLite数据库(一)的更多相关文章

  1. Android实现SQLite数据库联系人列表

    Android实现SQLite数据库联系人列表 开发工具:Andorid Studio 1.3 运行环境:Android 4.4 KitKat 工程内容 实现一个通讯录查看程序: 要求使用SQLite ...

  2. android基础---->SQLite数据库的使用

    SQLite 一个非常流行的嵌入式数据库,它支持 SQL 语言,并且只利用很少的内存就有很好的性能.此外它还是开源的,任何人都可以使用它.许多开源项目((Mozilla, PHP, Python)都使 ...

  3. Android中SQLite数据库操作(1)——使用SQL语句操作SQLite数据库

    下面是最原始的方法,用SQL语句操作数据库.后面的"Android中SQLite数据库操作(2)--SQLiteOpenHelper类"将介绍一种常用的android封装操作SQL ...

  4. android中sqlite数据库的基本使用和添加多张表

    看了很多关于android使用sqlite数据库的文章,很多都是介绍了数据库的建立和表的建立,而表通常都是只建立一张,而实际情况我们用到的表可能不止一张,那这种情况下我们又该怎么办呢,好了,下面我教大 ...

  5. android 一个SQLite数据库多个数据表的基本使用框架 (带demo)

    android 一个SQLite数据库多个数据表(带demo) 前言        demo演示        一.搭建        二.建立实体类        三.建立数据库操作类        ...

  6. Qt for Android 打包 SQLite 数据库

    Qt for Android 调用 SQLite 数据库时, 怎样将已经存在的数据库附加到 APK 中? 直接在你项目里面的Android源码的根目录下新建一个文件夹assets, 数据库就可以放里面 ...

  7. Android SQLiteOpenHelper Sqlite数据库的创建与打开

    Android Sqlite数据库是一个怎样的数据库? 答:是一种嵌入式小型设备,移动设备,的数据库,应用在穿戴设备(例如:智能手表,计算手环 等等),移动设备(例如:Android系统类型的手机 等 ...

  8. Android开发SQLite数据库的创建

    package com.example.db; import android.content.Context; import android.database.sqlite.SQLiteDatabas ...

  9. Android之SQLite数据库篇

    一.SQLite简介 Google为Andriod的较大的数据处理提供了SQLite,他在数据存储.管理.维护等各方面都相当出色,功能也非常的强大. 二.SQLite的特点 1.轻量级使用 SQLit ...

随机推荐

  1. surfaceView和View的区别

    概念:view在UI线程去更新自己:而SurfaceView则在一个子线程中去更新自己 surfaceView是在一个新起的单独线程中可以重新绘制画面,而View必须在UI的主线程中更新画面 在UI的 ...

  2. mqtt 服务器与客户端通讯

    mqtt 服务器与客户端通讯. 服务器端 ? 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 ...

  3. excel——之锁定表头不可编辑

    前言: 设置excel表头不可编辑功能.效果如下: 这就意味着,不可随意更改表头数据,除非你有密码. 过程: 1.先随意右键打开一个单元格,查看其中的“设置单元格属性”,即 2.在选中想要编辑的那些数 ...

  4. UITextView 实现placeholder的方法

    本文转载至 http://www.cnblogs.com/easonoutlook/archive/2012/12/28/2837665.html 在UITextField中自带placeholder ...

  5. HTML基础做出属于自己的完美网页

    HTML HTML解释: HTML是英文Hyper Text Mark-up Language(超文本标记语言)的缩写,他是一种制作万维网页面标准语言(标记).相当于定义统一的规则(W3C),大家都来 ...

  6. 学习Ruby你需要了解的相关知识(rvm, gem, bundle, rake, rails等)

    这篇文章主要介绍了学习Ruby你需要了解的相关知识(rvm, gem, bundle, rake, rails等),需要的朋友可以参考下     Ruby 这个就不用多说了 RVM 用于帮你安装Rub ...

  7. android 仿微信聊天界面,以及语音录制功能

    extends:http://104zz.iteye.com/blog/1709840 本例为模仿微信聊天界面UI设计,文字发送以及语言录制UI. 1先看效果图:     第一:chat.xml设计 ...

  8. 试验性的Numpy教程(译)

    Python中Numpy模块学习: 转自:http://my.oschina.net/u/175377/blog/74406

  9. 使用Yii2中dropdownlist实现地区三级联动的例子

    原文:http://www.yiichina.com/code/636 <?php use yii\helpers\Url; use yii\widgets\ActiveForm; use yi ...

  10. .net配置404错误页面

    如果你的网站出现一堆让人看不懂的报错,那么你就不是一个合格的程序员,也不是一个合格的站长. 下面的方面可以帮助你的网站远离让人头大的页面. 第一步:配置web.config 打开web.config, ...