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. 【RF库Collections测试】Dictionary Should Contain Value

    Name:Dictionary Should Contain ValueSource:Collections <test library>Arguments:[ dictionary | ...

  2. sql语句如何删除最后一条和第一条信息

    这是先前建好的SQL数据库中的test表, sql语句: delete a from test a,(select max(id) id from test) b where a.id = b.id ...

  3. MQTT的学习研究(四)moquette-mqtt 的使用之mqtt Blocking API客户端订阅并接收主题信息

    在上面两篇关于mqtt的broker的启动和mqtt的服务端发布主题信息之后,我们客户端需要订阅相关的信息并接收相关的主题信息. package com.etrip.mqtt; import java ...

  4. Docker源码分析(六):Docker Daemon网络

    1. 前言 Docker作为一个开源的轻量级虚拟化容器引擎技术,已然给云计算领域带来了新的发展模式.Docker借助容器技术彻底释放了轻量级虚拟化技术的威力,让容器的伸缩.应用的运行都变得前所未有的方 ...

  5. Linux下TCP延迟确认(Delayed Ack)机制导致的时延问题分析

    版权声明:本文由潘安群原创文章,转载请注明出处: 文章原文链接:https://www.qcloud.com/community/article/105 来源:腾云阁 https://www.qclo ...

  6. 【BZOJ1500】[NOI2005]维修数列 Splay

    [BZOJ1500][NOI2005]维修数列 Description Input 输入的第1 行包含两个数N 和M(M ≤20 000),N 表示初始时数列中数的个数,M表示要进行的操作数目.第2行 ...

  7. 【BZOJ1529】[POI2005]ska Piggy banks Tarjan

    [BZOJ1529][POI2005]ska Piggy banks Description Byteazar 有 N 个小猪存钱罐. 每个存钱罐只能用钥匙打开或者砸开. Byteazar 已经把每个 ...

  8. Mac - iPhone屏幕录制

    1. Mac电脑屏幕录制 1.1 文件->新建屏幕录制   1.2 点击红色按钮   1.3 截取需要录制的屏幕部分,点击开始录制   1.4 点击工具栏的停止按钮,停止录制   1.5 然后会 ...

  9. mysql 把表中某一列的内容合并为一行

    1,把表中某一列的内容合并为一行 select province,CONCAT('[\"全部\",\"',GROUP_CONCAT(city ORDER BY cityI ...

  10. less常见的操作

    less初体验:     mixin : 混合,将一堆属性从一个规则集到另外一个规则集: (如果有公用的样式,可以做提取:)     清除浮动经典代码:     .clearfix {   displ ...