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//databse/目录下确认。

Android 创建SQLite数据库(一)_第1张图片

  用于创建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.     *     * 

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.

* *

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. * *

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//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//database/,并且权限为-rx-rx--,再贴下之前新建数据库得到的结果:

Android 创建SQLite数据库(一)_第2张图片

 

  其中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数据库了。

更多相关文章

  1. Android 中数据库查询方法 query() 中的 selectionArgs 的用法
  2. Android Wifi子系统源代码View
  3. 代码:android崩溃日志收集和处理
  4. android中的sqlit3数据库进行手机应用软件开发(自写的一个财务管
  5. Button代码小写,运行时大写
  6. android 365手机秘书源代码
  7. Android系统配置数据库注释(settings.db)
  8. android studio调试c/c++代码
  9. Android开发者实用代码片段 与大家分享

随机推荐

  1. Android进度条总结
  2. Android进度条总结
  3. android 自动更新apk版本
  4. Android使用代码模拟HOME键的功能
  5. android返回HOME界面
  6. 垂直跑马灯
  7. android自带图片资源
  8. Android植物大战僵尸小游戏
  9. Android(安卓)调用系统相册选择图片并显
  10. Android(安卓)发送短信 sms