Android的SQLiteOpenHelper类 笔记241027

SQLiteOpenHelper

SQLiteOpenHelper是Android开发中用于管理SQLite数据库的一个非常重要的工具类。以下是对SQLiteOpenHelper的详细介绍:

一、基本概念

SQLiteOpenHelper是一个抽象类,它主要用于管理数据库的创建和版本管理。通过继承这个类,开发者可以重写一些方法以实现数据库的创建、升级和降级等功能。

二、主要方法

  1. 构造方法:用于创建SQLiteOpenHelper对象,需要传入数据库名称、版本号和一个可选的CursorFactory对象。
    public SQLiteOpenHelper(@Nullable Context context, @Nullable String name, @Nullable SQLiteDatabase.CursorFactory factory, int version)
    public SQLiteOpenHelper(@Nullable Context context, @Nullable String name, @Nullable SQLiteDatabase.CursorFactory factory, int version, @Nullable DatabaseErrorHandler errorHandler)
    public SQLiteOpenHelper(@Nullable Context context, @Nullable String name, int version, @NonNull SQLiteDatabase.OpenParams openParams)
  2. onCreate(SQLiteDatabase db):在数据库第一次创建时调用,用于执行创建表和初始化数据等操作。
  3. onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion):在数据库版本升级时调用,用于执行表结构的修改、数据迁移等操作。
  4. onDowngrade(SQLiteDatabase db, int oldVersion, int newVersion):在数据库版本降级时调用,但这个方法不是必须的,因为在实际开发中降级操作并不常见。
  5. getReadableDatabase():获取一个可读的数据库对象。如果数据库不存在,则会先调用onCreate()方法创建数据库。
  6. getWritableDatabase():获取一个可写的数据库对象。如果数据库不存在,也会先调用onCreate()方法创建数据库。



SQLiteOpenHelper的主要方法

方法名

作用

备注

SQLiteOpenHelper(
构造方法三个
创建SQLiteOpenHelper实例一般用 new SQLiteOpenHelper(Context context, String databaseName, CursorFactory factory, int version)
参数1可填MianActivity的实例,如this或MainActivity.this
参数2是数据库名称,如果不存在就会调用onCreate()方法
参数3指定CursorFactory , 可以为null(使用默认的CursorFactory)
参数4是版本号,如果变动就会执行onUpgrade()方法

抽象方法
onCreate()

创建数据库时做什么,
可以写入创建表结构的sql语句

没有对应的数据库时才调用
(构造函数的参数中的数据库名对应的数据库不存在时才调用)
构造获得实例,实例调用getReadableDatabase()
getWritableDatabase()时,发现构造时指定的数据库不存在,就会调用该方法

抽象方法
onUpgrade()

升级数据库版本时做什么

构造方法参数的版本号上升时才调用

非抽象,可选
onDowngrade()
降级数据库版本时做什么构造方法参数的版本号下降时才调用
非抽象,可选
onOpen()
打开数据库时做什么

close()

关闭所有打开的数据库对象

getWritableDatabase()

创建或打开可以读/写的数据库

通过返回的SQLiteDatabase对象对数据库进行操作

getReadableDatabase()

创建或打开可读的数据库

同上

SQLiteDatabase的主要方法

方法名

作用

备注

execSQL()

可进行增删改操作, 不能进行查询操作

query()、rawQuery()

查询数据库

insert()

插入数据

delete()

删除数据

SQLiteOpenHelper的构造方法

SQLiteOpenHelper 的构造方法通常看起来像这样(基于 Android SDK 的源代码):

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;
}

// 还有一个更简单的构造方法,它不接受 DatabaseErrorHandler
public SQLiteOpenHelper(Context context, String name, CursorFactory factory, int version) {
    this(context, name, factory, version, null);
}

在上面的构造方法中:

  • context 是一个 Context 对象,它允许访问应用的资源和类,以及调用应用级操作,如启动活动、广播和接收意图等。
  • name 是数据库文件的名称。如果不包括路径,则数据库文件将被存储在应用的私有文件目录中。
  • factory 是一个用于创建游标对象的 CursorFactory。如果传入 null,则使用默认的游标工厂。
  • version 是数据库的版本号。这是一个整数,用于跟踪数据库的结构变化。当版本号增加时,onUpgrade 方法将被调用。
  • errorHandler 是一个 DatabaseErrorHandler 对象,它允许在数据库遇到错误时执行自定义的错误处理逻辑。如果传入 null,则使用默认的错误处理器。

在创建 SQLiteOpenHelper 的子类时,我们需要调用其中一个构造方法来初始化父类。然后,我们可以实现 onCreateonUpgrade 等方法来定义数据库的结构和升级逻辑。

例如:

public class MyDatabaseHelper extends SQLiteOpenHelper {
    private static final String DATABASE_NAME = "mydatabase.db";
    private static final int DATABASE_VERSION = 1;

    public MyDatabaseHelper(Context context) {
        super(context, DATABASE_NAME, null, DATABASE_VERSION);
    }

    @Override
    public void onCreate(SQLiteDatabase db) {
        // 创建表的 SQL 语句
        String CREATE_TABLE = "CREATE TABLE mytable (" +
                "id INTEGER PRIMARY KEY AUTOINCREMENT," +
                "name TEXT" +
                ");";
        db.execSQL(CREATE_TABLE);
    }

    @Override
    public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
        // 版本升级时的逻辑
        db.execSQL("DROP TABLE IF EXISTS mytable");
        onCreate(db);
    }
}

在这个例子中,我们创建了一个名为 MyDatabaseHelper 的类,它继承自 SQLiteOpenHelper。我们在构造方法中调用了父类的构造方法,并传入了数据库名称、版本号和上下文对象。然后,我们实现了 onCreateonUpgrade 方法来定义数据库的结构和升级逻辑。

三、 SQLiteOpenHelper源代码

以下是一个简化的 SQLiteOpenHelper 源代码示例,并附有关键部分的解释:

package android.database.sqlite;

import android.content.Context;
import android.database.DatabaseErrorHandler;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteDatabase.CursorFactory;
import android.util.Log;

public abstract class SQLiteOpenHelper {
    // 调试标志,用于确定是否严格只读
    private static final boolean DEBUG_STRICT_READONLY = false;

    // 上下文对象,用于访问应用的资源和类
    private final Context mContext;

    // 数据库文件名
    private final String mName;

    // 用于创建游标对象的工厂,如果为 null,则使用默认工厂
    private final CursorFactory mFactory;

    // 数据库版本号
    private final int mNewVersion;

    // 数据库错误处理器,如果为 null,则使用默认处理器
    private final DatabaseErrorHandler mErrorHandler;

    // 数据库对象,可能为 null
    private SQLiteDatabase mDatabase;

    // 标记数据库是否正在初始化
    private boolean mIsInitializing;

    // SQLiteOpenHelper 的构造函数
    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;
    }

    // 简化构造函数,不指定错误处理器
    public SQLiteOpenHelper(Context context, String name, CursorFactory factory, int version) {
        this(context, name, factory, version, null);
    }

    // 创建或打开一个数据库,用于读写。如果数据库磁盘空间已满,则尝试以只读方式打开
    public synchronized SQLiteDatabase getWritableDatabase() {
        // ...(实现细节省略)
    }

    // 创建或打开一个数据库,用于读取。如果数据库磁盘空间已满,则只能以只读方式打开
    public synchronized SQLiteDatabase getReadableDatabase() {
        // ...(实现细节省略)
    }

    // 当数据库第一次创建时调用此方法
    public abstract void onCreate(SQLiteDatabase db);

    // 当数据库版本升级时调用此方法
    public abstract void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion);

    // 当数据库版本降级时调用此方法(可选实现)
    public void onDowngrade(SQLiteDatabase db, int oldVersion, int newVersion) {
        throw new SQLiteException("Can't downgrade database from version " + oldVersion + " to " + newVersion);
    }

    // 当数据库打开时调用此方法(可选实现)
    public void onOpen(SQLiteDatabase db) {
    }

    // ...(其他方法和内部类省略)
}
关键部分解释:
  1. 构造函数SQLiteOpenHelper 提供了两个构造函数,允许开发者指定数据库名称、版本号、游标工厂和错误处理器。版本号必须大于等于 1。
  2. getWritableDatabase():此方法用于创建或打开一个数据库,用于读写操作。如果数据库磁盘空间已满,则尝试以只读方式打开,但会抛出异常。
  3. getReadableDatabase():此方法用于创建或打开一个数据库,用于读取操作。如果数据库磁盘空间已满,则只能以只读方式打开。
  4. onCreate(SQLiteDatabase db):这是一个抽象方法,当数据库第一次创建时调用。开发者应在此方法中编写创建表和初始化数据的 SQL 语句。
  5. onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion):这也是一个抽象方法,当数据库版本升级时调用。开发者应在此方法中编写升级数据库的 SQL 语句,如添加新列、修改表结构等。
  6. onDowngrade(SQLiteDatabase db, int oldVersion, int newVersion):这是一个可选实现的方法,当数据库版本降级时调用。默认情况下,此方法会抛出异常,因为降级操作通常不被推荐。
  7. onOpen(SQLiteDatabase db):这是一个可选实现的方法,当数据库打开时调用。开发者可以在此方法中执行一些初始化操作。

请注意,上述代码是一个简化的示例,并省略了部分实现细节和内部类。在实际开发中,SQLiteOpenHelper 的实现可能会更加复杂,具体取决于应用的需求和数据库的结构。




用法实例

一个用 匿名内部类实例化SQLiteOpenHelper的Activity

package com.example.emptyviewsactivity2410261826;

import android.annotation.SuppressLint;
import android.content.ClipData;
import android.content.ClipboardManager;
import android.content.ContentValues;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;
import android.os.Bundle;
import android.util.Log;
import android.widget.Button;
import android.widget.EditText;
import android.widget.LinearLayout;
import android.widget.TableLayout;
import android.widget.TableRow;
import android.widget.TextView;

import androidx.activity.EdgeToEdge;
import androidx.appcompat.app.AppCompatActivity;
import androidx.appcompat.widget.AppCompatEditText;
import androidx.core.graphics.Insets;
import androidx.core.view.ViewCompat;
import androidx.core.view.WindowInsetsCompat;

public class MainActivity extends AppCompatActivity {

    final String DbName = "note1", TbName = "tableA1" , Col0="id" , Col1="content";
    final int RowQuantity = 666;
    LinearLayout linearLayout = null;



    TableLayout tableLayout = null;
    final TableRow[] TableRows = new TableRow[RowQuantity];
    final TextView[] TvAr = new TextView[RowQuantity];
    final EditText[] EtAr = new EditText[RowQuantity];
//    final AppCompatEditText EtAr[] = new AppCompatEditText[RowQuantity];
    final Button[] CopyBtnAr = new Button[RowQuantity];
    final Button[] PasteBtnAr = new Button[RowQuantity];
    final Button[] CutBtnAr = new Button[RowQuantity];
    final Button[] DelBtnAr = new Button[RowQuantity];



    SQLiteOpenHelper sqliteOpenHelper;

    SQLiteDatabase sqliteDatabase;





    @SuppressLint("MissingInflatedId")
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState); Log.i("onCreate()","onCreate()");
        EdgeToEdge.enable(this);
        setContentView(R.layout.activity_main);
        ViewCompat.setOnApplyWindowInsetsListener(findViewById(R.id.main), (v, insets) -> {
            Insets systemBars = insets.getInsets(WindowInsetsCompat.Type.systemBars());
            v.setPadding(systemBars.left, systemBars.top, systemBars.right, systemBars.bottom);
            return insets;
        });

        /// 👆上面部分是新建项目自动生成的



        linearLayout = findViewById(R.id.LinearLayoutA1);
        tableLayout = findViewById(R.id.tableLayoutA1);


//        EditText editText = new EditText(this);  linearLayout.addView(editText);
//
//        AppCompatEditText appCompatEditText = new AppCompatEditText(MainActivity.this); linearLayout.addView(appCompatEditText);
//
//        AutoCompleteTextView autoCompleteTextView = new AutoCompleteTextView(this); linearLayout.addView(autoCompleteTextView);
//
//        MultiAutoCompleteTextView multiAutoCompleteTextView = new MultiAutoCompleteTextView(MainActivity.this); linearLayout.addView(multiAutoCompleteTextView);



         /*
        删除数据库
         deleteDatabase(DbName)删除数据库, Activity,AppCompatActivity都自带删除Sqlite数据库的方法, 这是实现自最顶层 public abstract class Context 的抽象方法  public abstract boolean deleteDatabase(String name);
         AppCompatActivity extends FragmentActivity extends ComponentActivity extends androidx.core.app.ComponentActivity extends Activity extends ContextThemeWrapper extends ContextWrapper extends Context
         */
//        deleteDatabase(DbName);

        /*
        通过匿名内部类实现SQLiteOpenHelper, 也可用继承类实现.
        实例化SQLiteOpenHelper时虽然指定了数据库名称和版本,但还不会创建或打开数据库,直到实例执行 getReadableDatabase() 或 getWritableDatabase() 获取数据库时时,才会打开 或 创建再打开 数据库
         */
       sqliteOpenHelper = new SQLiteOpenHelper(MainActivity.this, DbName, null, 1) {


            //必须
            //onCreate(SQLiteDatabase sqLiteDatabase数据库实例)
            ///在执行 getReadableDatabase() 或 getWritableDatabase() 获取数据库时时, 如果数据库名称对应的数据库不存在,就会调用该方法,该方法为abstract抽象方法,必须实现.
            @Override
            public void onCreate(SQLiteDatabase sqLiteDatabase) {
                System.out.println("SQLiteOpenHelper 的 onCreate(SQLiteDatabase sqLiteDatabase)被调用    //在执行 getReadableDatabase() 或 getWritableDatabase() 获取数据库时时, 如果数据库名称对应的数据库不存在,就会调用该方法,该方法为abstract抽象方法,必须实现");

                final String CreateTableSql = "CREATE TABLE "+TbName+" ( id INTEGER PRIMARY KEY , content TEXT )";

                sqLiteDatabase.execSQL(CreateTableSql);

                for(int r=0; r<RowQuantity; r++){
                    ContentValues cvs = new ContentValues(2);
                    cvs.put(Col0, r);
                    cvs.put(Col1, "");
                    sqLiteDatabase.insert(TbName, null, cvs);
                }

            }

            //必须
            //onUpgrade(SQLiteDatabase sqLiteDatabase数据库实例, int oldVersion旧版本号, int newVersion新版本号)
            ///在执行 getReadableDatabase() 或 getWritableDatabase() 获取数据库时时, 如果数据库版本号升高,就会调用该方法,该方法为abstract抽象方法,必须实现.    在创建数据库时不会调用该方法
            @Override
            public void onUpgrade(SQLiteDatabase sqLiteDatabase, int oldVersion, int newVersion) {
                System.out.println("SQLiteOpenHelper 的 onUpgrade(SQLiteDatabase sqLiteDatabase, int oldVersion, int newVersion)被调用    //在执行 getReadableDatabase() 或 getWritableDatabase() 获取数据库时时, 如果数据库版本号升高,就会调用该方法,该方法为abstract抽象方法,必须实现.    在创建数据库时不会调用该方法");

            }

            //非抽象,可选
            //onDowngrade(SQLiteDatabase sqLiteDatabase数据库实例, int oldVersion旧版本号, int newVersion新版本号)
            ///在执行 getReadableDatabase() 或 getWritableDatabase() 获取数据库时时, 如果数据库版本号发生变动,就会调用该方法,该方法非抽象,可选.    在创建数据库时不会调用该方法
            @Override
            public void onDowngrade(SQLiteDatabase sqLiteDatabase, int oldVersion, int newVersion) {
                System.out.println("SQLiteOpenHelper 的 onDowngrade(SQLiteDatabase sqLiteDatabase, int oldVersion, int newVersion)被调用    //在执行 getReadableDatabase() 或 getWritableDatabase() 获取数据库时时, 如果数据库版本号降低,就会调用该方法,该方法非抽象,可选.    在创建数据库时不会调用该方法");

            }

            //非抽象,可选
            //onOpen(SQLiteDatabase sqLiteDatabase数据库实例)
            //在执行 getReadableDatabase() 或 getWritableDatabase() 获取数据库时时, 被调用
            @Override
            public void onOpen(SQLiteDatabase sqLiteDatabase) {
                Log.i("onOpen(SQLiteDatabase sqLiteDatabase)","SQLiteOpenHelper 的 onOpen(SQLiteDatabase sqLiteDatabase)被调用    //在执行 getReadableDatabase() 或 getWritableDatabase() 获取数据库时时, 会调用onOpen(SQLiteDatabase db)方法");
                @SuppressLint("Recycle") Cursor cursor = sqLiteDatabase.rawQuery("SELECT COUNT(*) FROM "+TbName, null);
                cursor.moveToFirst(); if(cursor.getInt(0) != RowQuantity){ 
                    Log.i("库表均已存在,但行数不对应","库表均已存在,但行数不对应, 将 DELETE TABLE FROM table-name 然后重新 INSERT INTO");
                    sqLiteDatabase.delete(TbName,null,null);
                    for(int r=0; r<RowQuantity; r++){
                       sqLiteDatabase.execSQL("INSERT INTO " + TbName + " VALUES (?,?) " , new Object[]{r,""});
                    }
                }
            }

        };

       sqliteDatabase = sqliteOpenHelper.getWritableDatabase();

        @SuppressLint("Recycle") Cursor cursor = sqliteDatabase.rawQuery("SELECT * FROM "+TbName, null);


        for(int r=0; cursor.moveToNext(); r++){
            TableRow row = TableRows[r] = new TableRow(this); tableLayout.addView(row);
            TextView tv = TvAr[r] = new TextView(this); row.addView(tv); tv.setText(cursor.getString(0));
            EditText et = EtAr[r] = new EditText(this); row.addView(et); et.setText(cursor.getString(1));
            Button copyBtn = CopyBtnAr[r] = new Button(this); row.addView(copyBtn, 100, 100); copyBtn.setText("复");
                copyBtn.setOnClickListener((view)->{
                    ClipboardManager clipboardManager = (ClipboardManager)getSystemService(CLIPBOARD_SERVICE);
                    ClipData clipData = ClipData.newPlainText("text", et.getEditableText().toString());
                    clipboardManager.setPrimaryClip(clipData);
                });
            Button pBtn = PasteBtnAr[r] = new Button(this); row.addView(pBtn, 100, 100); pBtn.setText("粘"); pBtn.setOnClickListener((view)->{ et.setText(((ClipboardManager)getSystemService(CLIPBOARD_SERVICE)).getPrimaryClip().getItemAt(0).getText()); });
            Button cutBtn = CutBtnAr[r] = new Button(this); row.addView(cutBtn, 100, 100); cutBtn.setText("剪"); cutBtn.setOnClickListener((view)->{
                copyBtn.callOnClick(); et.setText("");
            });
            Button dBtn = DelBtnAr[r] = new Button(this); row.addView(dBtn, 100, 100); dBtn.setText("删"); dBtn.setOnClickListener((view)->{ et.setText(""); });
        }



//       for(int r=0; r<RowQuantity; r++){
//           TableRow row = tableRows[r] = new TableRow(this); tableLayout.addView(row);
//           EditText et = etAr[r] = new EditText(this); row.addView(et);
//
//       }


        /*
        测试多次调用  getReadableDatabase() 和 getWritableDatabase() 获取SQLiteDatabase数据库实例
        多次调用  getReadableDatabase() 和 getWritableDatabase() 返回同一个SQLiteDatabase实例
         */
        SQLiteDatabase dbw , dbr;
        dbr = sqliteOpenHelper.getReadableDatabase();
        Log.i("dbw.isReadOnly()",""+dbr.isReadOnly());
        dbw = sqliteOpenHelper.getWritableDatabase();
        Log.i("dbw.isReadOnly()",""+dbw.isReadOnly());
//        dbr = sqliteOpenHelper.getReadableDatabase();
        System.out.println("dbw==dbr 结果 "+(dbw==dbr));
        Log.i("dbw.isReadOnly()",""+dbw.isReadOnly());


        /*
        删除数据库
         deleteDatabase(DbName)删除数据库, Activity,AppCompatActivity都自带删除Sqlite数据库的方法, 这是实现自最顶层 public abstract class Context 的抽象方法  public abstract boolean deleteDatabase(String name);
         AppCompatActivity extends FragmentActivity extends ComponentActivity extends androidx.core.app.ComponentActivity extends Activity extends ContextThemeWrapper extends ContextWrapper extends Context
         */
//        deleteDatabase(DbName);




    }

    void save(){
        for(int r=0; r<RowQuantity; r++){
            ContentValues cvs = new ContentValues();
            cvs.put(Col1, EtAr[r].getText().toString());
            sqliteDatabase.update(TbName, cvs, "id=?", new String[]{""+r});
        }
    }

    @Override
    protected void onStart() {
        super.onStart(); Log.i("onStart()","onStart()");
    }

    @Override
    protected void onPause() {
        super.onPause(); Log.i(" onPause()"," onPause()");
        save();
    }


    @Override
    protected void onStop() {
        super.onStop(); Log.i("onStop()","onStop()");
        save();
    }

    @Override
    protected void onRestart() {
        super.onRestart(); Log.i("onRestart()","onRestart()");
    }

    @Override
    protected void onPostResume() {
        super.onPostResume(); Log.i("onPostResume()","onPostResume()");
    }

    @Override
    protected void onDestroy() {
        super.onDestroy(); Log.i("onDestroy()","onDestroy()");
        save();
    }

}










一些用法收集参考

public class SQLiteHelper extends SQLiteOpenHelper {
    private SQLiteDatabase sqLiteDatabase;

    //调用父类 SQLiteOpenHelper 的构造函数
    public SQLiteHelper(Context context) {
        //context上下文环境(例如,一个 Activity),数据库名字,一个可选的游标工厂(通常是 Null),一个代表你正在使用的数据库模型版本的整数。
        super(context, DBUtils.DATABASE_NAME, null, DBUtils.DATABASE_VERSION);
        sqLiteDatabase = this.getWritableDatabase();
    }

    //创建数据库 只在没有数据库时执行
    @Override
    public void onCreate(SQLiteDatabase db) {
        //execSQL() 方法适用于所有不返回结果的 SQL 语句
        db.execSQL("CREATE TABLE " + DBUtils.DATABASE_TABLE + "(" + DBUtils.NOTE_ID +
                " INTEGER PRIMARY KEY AUTOINCREMENT," + DBUtils.NOTE_CONTENT +
                " TEXT," + DBUtils.NOTE_TIME + " TEXT)");
    }

    //把一个数据库从旧的模型转变到新的模型。
    //它需要三个参数,一个 SQLiteDatabase 对象,一个旧的版本号和一个新的版本号
    @Override
    public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {}

    //添加数据
    public boolean insertData(String userContent, String userTime) {
    	//ContentValues 储存数据,只能存储基本类型的数据,不能存储对象
        ContentValues values = new ContentValues();
        values.put(DBUtils.NOTE_CONTENT, userContent);
        values.put(DBUtils.NOTE_TIME, userTime);
        //插入数据
        //第一个参数是表的名称
        //第二个参数为空值字段,就是如果第三个参数为空(null)的时候就会用到第二个参数的值。用第二个参数代替第三个参数组拼成SQL语句
        //比如:insert into person(name) values(null)   这里的person字段使用了第二个参数的name
        //第三个参数不为空就不会用到第二个参数
        return sqLiteDatabase.insert(DBUtils.DATABASE_TABLE, null, values) > 0;
    }

    //删除数据
    public boolean deleteData(String id) {
        String sql = DBUtils.NOTE_ID + "=?";
        String[] contentValuesArrary = new String[]{String.valueOf(id)};
        //1表名、2字段名、3占位符的数据
        return sqLiteDatabase.delete(DBUtils.DATABASE_TABLE, sql, contentValuesArrary) > 0;
    }

    //修改数据
    public boolean updateData(String id, String content, String userYear) {
        ContentValues contentValues = new ContentValues();
        contentValues.put(DBUtils.NOTE_CONTENT, content);
        contentValues.put(DBUtils.NOTE_TIME, userYear);
        String sql = DBUtils.NOTE_ID + "=?";
        String[] strings = new String[]{id};
        //1表名、2需要更新值、3以什么条件字段更新、4条件字段的数据值(占位符的值)
        return sqLiteDatabase.update(DBUtils.DATABASE_TABLE, contentValues, sql, strings) > 0;
    }

    //查询数据
    public List<NotepadBean> qurry() {
        List<NotepadBean> list = new ArrayList<NotepadBean>();
        //1 表名、   2 需要查询的字段列表,用字符串数组形式传入,null为所有的字段、   3 以什么条件字段查询、   4 条件字段的数据值(占位符的值)、
        // 5 groupBy相当于select语句的groupby后面的部分、   6 having相当于select语句的having后面的部分、  7 order是我们想要的排序方式。
        Cursor cursor = sqLiteDatabase.query(DBUtils.DATABASE_TABLE, null, null, null,
                null, null, DBUtils.NOTE_ID + " desc");
        if (cursor != null) {
            while (cursor.moveToNext()) {
                NotepadBean noteInfo = new NotepadBean();
                String id = String.valueOf(cursor.getInt
                        (cursor.getColumnIndex(DBUtils.NOTE_ID)));
                String content = cursor.getString(cursor.getColumnIndex
                        (DBUtils.NOTE_CONTENT));
                String time = cursor.getString(cursor.getColumnIndex(DBUtils.NOTE_TIME));
                noteInfo.setId(id);
                noteInfo.setNotepadContent(content);
                noteInfo.setNotepadTime(time);
                list.add(noteInfo);
            }
            cursor.close();
        }
        return list;
    }
}

package com.example.dbproject;
import android.content.Context;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;
import android.widget.Toast;
public class DbContect extends SQLiteOpenHelper {
    private static final int VERSION=1;
    private static final String DBNAME="Users.db";   //  创建数据库名叫 Users
    private Context mContext;

    public DbContect(Context context){
        super(context,DBNAME,null,VERSION);
        mContext = context;
    }
    //创建数据库 只在没有数据库时执行
    public void onCreate(SQLiteDatabase db){
        //创建密码表  pwd_tb
        db.execSQL("create table pwd_tb (pwd varchar(20) primary key)");
        //创建收入表    user_tb
        db.execSQL("create table user_tb(_id integer primary key autoincrement, money decimal," +
                " time varchar(10),type varchar(10),handler varchar(100),mark varchar(200))");
    }
    //数据库版本更新时执行
    public void onUpgrade(SQLiteDatabase db,int oldVersion,int newVersion)
    {
        db.execSQL("drop table if exists pwd_tb");
        db.execSQL("drop table if exists user_tb");
        onCreate(db);
    }


}





SQLiteOpenHelper是Android开发中用于管理SQLite数据库的一个非常重要的工具类。以下是对SQLiteOpenHelper的详细介绍:

一、基本概念

SQLiteOpenHelper是一个抽象类,它主要用于管理数据库的创建和版本管理。通过继承这个类,开发者可以重写一些方法以实现数据库的创建、升级和降级等功能。

二、主要方法

  1. 构造方法:用于创建SQLiteOpenHelper对象,需要传入数据库名称、版本号和一个可选的CursorFactory对象。
  2. onCreate(SQLiteDatabase db):在数据库第一次创建时调用,用于执行创建表和初始化数据等操作。
  3. onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion):在数据库版本升级时调用,用于执行表结构的修改、数据迁移等操作。
  4. onDowngrade(SQLiteDatabase db, int oldVersion, int newVersion):在数据库版本降级时调用,但这个方法不是必须的,因为在实际开发中降级操作并不常见。
  5. getReadableDatabase():获取一个可读的数据库对象。如果数据库不存在,则会先调用onCreate()方法创建数据库。
  6. getWritableDatabase():获取一个可写的数据库对象。如果数据库不存在,也会先调用onCreate()方法创建数据库。

三、使用示例

以下是一个使用SQLiteOpenHelper创建数据库、表以及进行增删改查操作的简单示例:

  1. 创建数据库帮助类
public class MyDatabaseHelper extends SQLiteOpenHelper {
    private static final String DATABASE_NAME = "mydatabase.db";
    private static final int DATABASE_VERSION = 1;

    public MyDatabaseHelper(Context context) {
        super(context, DATABASE_NAME, null, DATABASE_VERSION);
    }

    @Override
    public void onCreate(SQLiteDatabase db) {
        String createTable = "CREATE TABLE users (id INTEGER PRIMARY KEY AUTOINCREMENT, name TEXT)";
        db.execSQL(createTable);
    }

    @Override
    public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
        db.execSQL("DROP TABLE IF EXISTS users");
        onCreate(db);
    }
}
  1. 使用数据库帮助类进行增删改查操作
MyDatabaseHelper dbHelper = new MyDatabaseHelper(context);
SQLiteDatabase db = dbHelper.getWritableDatabase();

// 插入数据
ContentValues values = new ContentValues();
values.put("name", "John Doe");
long id = db.insert("users", null, values);

// 查询数据
Cursor cursor = db.query("users", null, null, null, null, null, null);
while (cursor.moveToNext()) {
    int userId = cursor.getInt(cursor.getColumnIndex("id"));
    String userName = cursor.getString(cursor.getColumnIndex("name"));
    // 处理查询到的数据
}
cursor.close();

// 更新数据
ContentValues updateValues = new ContentValues();
updateValues.put("name", "Jane Smith");
db.update("users", updateValues, "id=?", new String[]{String.valueOf(id)});

// 删除数据
db.delete("users", "id=?", new String[]{String.valueOf(id)});

// 关闭数据库
db.close();

四、注意事项

  1. 数据库版本管理:在开发过程中,随着应用程序的更新,数据库结构可能会发生变化。因此,合理使用onUpgrade()方法来进行数据库结构的升级是非常重要的。
  2. 异常处理:在进行数据库操作时,可能会遇到各种异常情况,如SQL语法错误、数据库文件损坏等。因此,在实际开发中,应该添加适当的异常处理代码来确保程序的健壮性。
  3. 性能优化:对于大规模的数据库操作,如批量插入、更新或删除数据,应该考虑使用事务(Transaction)来提高性能并减少数据库锁定的时间。

综上所述,SQLiteOpenHelper是Android开发中管理SQLite数据库的重要工具类。通过合理使用该类,开发者可以实现高效的数据管理,从而提升应用程序的性能和用户体验。







如何使用SQLiteOpenHelper

SQLiteOpenHelper 是 Android 平台上用于管理 SQLite 数据库的一个辅助类。它简化了数据库的创建和版本管理。以下是使用 SQLiteOpenHelper 的基本步骤:

1. 创建一个继承自 SQLiteOpenHelper 的类

首先,你需要创建一个类,这个类继承自 SQLiteOpenHelper 并实现其两个抽象方法:onCreate(SQLiteDatabase db)onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion)

import android.content.Context;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;

public class MyDatabaseHelper extends SQLiteOpenHelper {

    // 数据库名称
    private static final String DATABASE_NAME = "my_database.db";

    // 数据库版本
    private static final int DATABASE_VERSION = 1;

    // 创建一个表
    private static final String TABLE_CREATE =
            "CREATE TABLE my_table (" +
            "id INTEGER PRIMARY KEY AUTOINCREMENT, " +
            "name TEXT NOT NULL, " +
            "age INTEGER NOT NULL);";

    public MyDatabaseHelper(Context context) {
        super(context, DATABASE_NAME, null, DATABASE_VERSION);
    }

    @Override
    public void onCreate(SQLiteDatabase db) {
        db.execSQL(TABLE_CREATE);
    }

    @Override
    public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
        // 如果表已经存在,删除表
        db.execSQL("DROP TABLE IF EXISTS my_table");
        // 重新创建表
        onCreate(db);
    }
}

2. 在你的 Activity 或其他组件中使用 SQLiteOpenHelper

在你的 Activity 或其他组件中,你可以通过实例化 MyDatabaseHelper 类来获取 SQLiteDatabase 对象,然后执行 CRUD(创建、读取、更新、删除)操作。

import android.database.sqlite.SQLiteDatabase;
import android.os.Bundle;
import androidx.appcompat.app.AppCompatActivity;
import android.widget.Toast;

public class MainActivity extends AppCompatActivity {

    private MyDatabaseHelper dbHelper;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        dbHelper = new MyDatabaseHelper(this);

        // 插入数据
        insertData();

        // 查询数据
        queryData();
    }

    private void insertData() {
        SQLiteDatabase db = dbHelper.getWritableDatabase();

        ContentValues values = new ContentValues();
        values.put("name", "Alice");
        values.put("age", 30);

        long newRowId = db.insert("my_table", null, values);
        Toast.makeText(this, "Inserted Row ID: " + newRowId, Toast.LENGTH_SHORT).show();
    }

    private void queryData() {
        SQLiteDatabase db = dbHelper.getReadableDatabase();

        String[] projection = {
                "id",
                "name",
                "age"
        };

        Cursor cursor = db.query(
                "my_table",   // 表名
                projection,   // 要返回的列
                null,         // WHERE 子句的选择条件
                null,         // WHERE 子句的选择参数
                null,         // GROUP BY 子句
                null,         // HAVING 子句
                null          // ORDER BY 子句
        );

        while (cursor.moveToNext()) {
            int id = cursor.getInt(cursor.getColumnIndexOrThrow("id"));
            String name = cursor.getString(cursor.getColumnIndexOrThrow("name"));
            int age = cursor.getInt(cursor.getColumnIndexOrThrow("age"));

            // 处理查询结果
            Toast.makeText(this, "ID: " + id + ", Name: " + name + ", Age: " + age, Toast.LENGTH_SHORT).show();
        }

        cursor.close();
    }
}

3. 注意事项

  • 线程安全:SQLite 数据库不是线程安全的,因此你应该确保所有的数据库操作都在一个单独的线程中执行,或者使用 AsyncTask 或其他异步方法。
  • 关闭游标:每次使用完 Cursor 后,都应该调用 cursor.close() 方法来释放资源。
  • 错误处理:在实际应用中,你应该添加适当的错误处理逻辑,比如捕获 SQLException

通过以上步骤,你就可以在 Android 应用中使用 SQLiteOpenHelper 来管理 SQLite 数据库了。

























Android API34 的 SQLiteOpenHelper 的源码

/*
 * Copyright (C) 2007 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package android.database.sqlite;

import android.annotation.IntRange;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.compat.annotation.UnsupportedAppUsage;
import android.content.Context;
import android.database.DatabaseErrorHandler;
import android.database.SQLException;
import android.database.sqlite.SQLiteDatabase.CursorFactory;
import android.os.FileUtils;
import android.util.Log;

import java.io.File;
import java.util.Objects;

/**
 * A helper class to manage database creation and version management.
 *
 * <p>You create a subclass implementing {@link #onCreate}, {@link #onUpgrade} and
 * optionally {@link #onOpen}, and this class takes care of opening the database
 * if it exists, creating it if it does not, and upgrading it as necessary.
 * Transactions are used to make sure the database is always in a sensible state.
 *
 * <p>This class makes it easy for {@link android.content.ContentProvider}
 * implementations to defer opening and upgrading the database until first use,
 * to avoid blocking application startup with long-running database upgrades.
 *
 * <p>For an example, see the NotePadProvider class in the NotePad sample application,
 * in the <em>samples/</em> directory of the SDK.</p>
 *
 * <p class="note"><strong>Note:</strong> this class assumes
 * monotonically increasing version numbers for upgrades.</p>
 *
 * <p class="note"><strong>Note:</strong> the {@link AutoCloseable} interface was
 * first added in the {@link android.os.Build.VERSION_CODES#Q} release.</p>
 */
public abstract class SQLiteOpenHelper implements AutoCloseable {
    private static final String TAG = SQLiteOpenHelper.class.getSimpleName();

    private final Context mContext;
    @UnsupportedAppUsage
    private final String mName;
    private final int mNewVersion;
    private final int mMinimumSupportedVersion;

    private SQLiteDatabase mDatabase;
    private boolean mIsInitializing;
    private SQLiteDatabase.OpenParams.Builder mOpenParamsBuilder;

    /**
     * Create a helper object to create, open, and/or manage a database.
     * This method always returns very quickly.  The database is not actually
     * created or opened until one of {@link #getWritableDatabase} or
     * {@link #getReadableDatabase} is called.
     *
     * @param context to use for locating paths to the the database
     * @param name of the database file, or null for an in-memory database
     * @param factory to use for creating cursor objects, or null for the default
     * @param version number of the database (starting at 1); if the database is older,
     *     {@link #onUpgrade} will be used to upgrade the database; if the database is
     *     newer, {@link #onDowngrade} will be used to downgrade the database
     */
    public SQLiteOpenHelper(@Nullable Context context, @Nullable String name,
            @Nullable CursorFactory factory, int version) {
        this(context, name, factory, version, null);
    }

    /**
     * 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.
     *
     * <p>Accepts input param: a concrete instance of {@link DatabaseErrorHandler} to be
     * used to handle corruption when sqlite reports database corruption.</p>
     *
     * @param context to use for locating paths to the the database
     * @param name of the database file, or null for an in-memory database
     * @param factory to use for creating cursor objects, or null for the default
     * @param version number of the database (starting at 1); if the database is older,
     *     {@link #onUpgrade} will be used to upgrade the database; if the database is
     *     newer, {@link #onDowngrade} will be used to downgrade the database
     * @param errorHandler the {@link DatabaseErrorHandler} to be used when sqlite reports database
     * corruption, or null to use the default error handler.
     */
    public SQLiteOpenHelper(@Nullable Context context, @Nullable String name,
            @Nullable CursorFactory factory, int version,
            @Nullable DatabaseErrorHandler errorHandler) {
        this(context, name, factory, version, 0, errorHandler);
    }

    /**
     * Create a helper object to create, open, and/or manage a database.
     * This method always returns very quickly.  The database is not actually
     * created or opened until one of {@link #getWritableDatabase} or
     * {@link #getReadableDatabase} is called.
     *
     * @param context to use for locating paths to the the database
     * @param name of the database file, or null for an in-memory database
     * @param version number of the database (starting at 1); if the database is older,
     *     {@link #onUpgrade} will be used to upgrade the database; if the database is
     *     newer, {@link #onDowngrade} will be used to downgrade the database
     * @param openParams configuration parameters that are used for opening {@link SQLiteDatabase}.
     *        Please note that {@link SQLiteDatabase#CREATE_IF_NECESSARY} flag will always be
     *        set when the helper opens the database
     */
    public SQLiteOpenHelper(@Nullable Context context, @Nullable String name, int version,
            @NonNull SQLiteDatabase.OpenParams openParams) {
        this(context, name, version, 0, openParams.toBuilder());
    }

    /**
     * Same as {@link #SQLiteOpenHelper(Context, String, CursorFactory, int, DatabaseErrorHandler)}
     * but also accepts an integer minimumSupportedVersion as a convenience for upgrading very old
     * versions of this database that are no longer supported. If a database with older version that
     * minimumSupportedVersion is found, it is simply deleted and a new database is created with the
     * given name and version
     *
     * @param context to use for locating paths to the the database
     * @param name the name of the database file, null for a temporary in-memory database
     * @param factory to use for creating cursor objects, null for default
     * @param version the required version of the database
     * @param minimumSupportedVersion the minimum version that is supported to be upgraded to
     *            {@code version} via {@link #onUpgrade}. If the current database version is lower
     *            than this, database is simply deleted and recreated with the version passed in
     *            {@code version}. {@link #onBeforeDelete} is called before deleting the database
     *            when this happens. This is 0 by default.
     * @param errorHandler the {@link DatabaseErrorHandler} to be used when sqlite reports database
     *            corruption, or null to use the default error handler.
     * @see #onBeforeDelete(SQLiteDatabase)
     * @see #SQLiteOpenHelper(Context, String, CursorFactory, int, DatabaseErrorHandler)
     * @see #onUpgrade(SQLiteDatabase, int, int)
     * @hide
     */
    public SQLiteOpenHelper(@Nullable Context context, @Nullable String name,
            @Nullable CursorFactory factory, int version,
            int minimumSupportedVersion, @Nullable DatabaseErrorHandler errorHandler) {
        this(context, name, version, minimumSupportedVersion,
                new SQLiteDatabase.OpenParams.Builder());
        mOpenParamsBuilder.setCursorFactory(factory);
        mOpenParamsBuilder.setErrorHandler(errorHandler);
    }

    private SQLiteOpenHelper(@Nullable Context context, @Nullable String name, int version,
            int minimumSupportedVersion,
            @NonNull SQLiteDatabase.OpenParams.Builder openParamsBuilder) {
        Objects.requireNonNull(openParamsBuilder);
        if (version < 1) throw new IllegalArgumentException("Version must be >= 1, was " + version);

        mContext = context;
        mName = name;
        mNewVersion = version;
        mMinimumSupportedVersion = Math.max(0, minimumSupportedVersion);
        setOpenParamsBuilder(openParamsBuilder);
    }

    /**
     * Return the name of the SQLite database being opened, as given to
     * the constructor.
     */
    public String getDatabaseName() {
        return mName;
    }

    /**
     * Enables or disables the use of write-ahead logging for the database.
     *
     * Write-ahead logging cannot be used with read-only databases so the value of
     * this flag is ignored if the database is opened read-only.
     *
     * @param enabled True if write-ahead logging should be enabled, false if it
     * should be disabled.
     *
     * @see SQLiteDatabase#enableWriteAheadLogging()
     */
    public void setWriteAheadLoggingEnabled(boolean enabled) {
        synchronized (this) {
            if (mOpenParamsBuilder.isWriteAheadLoggingEnabled() != enabled) {
                if (mDatabase != null && mDatabase.isOpen() && !mDatabase.isReadOnly()) {
                    if (enabled) {
                        mDatabase.enableWriteAheadLogging();
                    } else {
                        mDatabase.disableWriteAheadLogging();
                    }
                }
                mOpenParamsBuilder.setWriteAheadLoggingEnabled(enabled);
            }

            // Compatibility WAL is disabled if an app disables or enables WAL
            mOpenParamsBuilder.removeOpenFlags(SQLiteDatabase.ENABLE_LEGACY_COMPATIBILITY_WAL);
        }
    }

    /**
     * Configures <a href="https://sqlite.org/malloc.html#lookaside">lookaside memory allocator</a>
     *
     * <p>This method should be called from the constructor of the subclass,
     * before opening the database, since lookaside memory configuration can only be changed
     * when no connection is using it
     *
     * <p>SQLite default settings will be used, if this method isn't called.
     * Use {@code setLookasideConfig(0,0)} to disable lookaside
     *
     * <p><strong>Note:</strong> Provided slotSize/slotCount configuration is just a recommendation.
     * The system may choose different values depending on a device, e.g. lookaside allocations
     * can be disabled on low-RAM devices
     *
     * @param slotSize The size in bytes of each lookaside slot.
     * @param slotCount The total number of lookaside memory slots per database connection.
     */
    public void setLookasideConfig(@IntRange(from = 0) final int slotSize,
            @IntRange(from = 0) final int slotCount) {
        synchronized (this) {
            if (mDatabase != null && mDatabase.isOpen()) {
                throw new IllegalStateException(
                        "Lookaside memory config cannot be changed after opening the database");
            }
            mOpenParamsBuilder.setLookasideConfig(slotSize, slotCount);
        }
    }

    /**
     * Sets configuration parameters that are used for opening {@link SQLiteDatabase}.
     * <p>Please note that {@link SQLiteDatabase#CREATE_IF_NECESSARY} flag will always be set when
     * opening the database
     *
     * @param openParams configuration parameters that are used for opening {@link SQLiteDatabase}.
     * @throws IllegalStateException if the database is already open
     */
    public void setOpenParams(@NonNull SQLiteDatabase.OpenParams openParams) {
        Objects.requireNonNull(openParams);
        synchronized (this) {
            if (mDatabase != null && mDatabase.isOpen()) {
                throw new IllegalStateException(
                        "OpenParams cannot be set after opening the database");
            }
            setOpenParamsBuilder(new SQLiteDatabase.OpenParams.Builder(openParams));
        }
    }

    private void setOpenParamsBuilder(SQLiteDatabase.OpenParams.Builder openParamsBuilder) {
        mOpenParamsBuilder = openParamsBuilder;
        mOpenParamsBuilder.addOpenFlags(SQLiteDatabase.CREATE_IF_NECESSARY);
    }

    /**
     * Sets the maximum number of milliseconds that SQLite connection is allowed to be idle
     * before it is closed and removed from the pool.
     *
     * <p>This method should be called from the constructor of the subclass,
     * before opening the database
     *
     * <p><b>DO NOT USE</b> this method.
     * This feature has negative side effects that are very hard to foresee.
     * See the javadoc of
     * {@link SQLiteDatabase.OpenParams.Builder#setIdleConnectionTimeout(long)}
     * for the details.
     *
     * @param idleConnectionTimeoutMs timeout in milliseconds. Use {@link Long#MAX_VALUE} value
     * to allow unlimited idle connections.
     *
     * @see SQLiteDatabase.OpenParams.Builder#setIdleConnectionTimeout(long)
     *
     * @deprecated DO NOT USE this method. See the javadoc of
     * {@link SQLiteDatabase.OpenParams.Builder#setIdleConnectionTimeout(long)}
     * for the details.
     */
    @Deprecated
    public void setIdleConnectionTimeout(@IntRange(from = 0) final long idleConnectionTimeoutMs) {
        synchronized (this) {
            if (mDatabase != null && mDatabase.isOpen()) {
                throw new IllegalStateException(
                        "Connection timeout setting cannot be changed after opening the database");
            }
            mOpenParamsBuilder.setIdleConnectionTimeout(idleConnectionTimeoutMs);
        }
    }

    /**
     * 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()}.
     *
     * @throws SQLiteException if the database cannot be opened for writing
     * @return a read/write database object valid until {@link #close} is called
     */
    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()}.
     *
     * @throws SQLiteException if the database cannot be opened
     * @return a database object valid until {@link #getWritableDatabase}
     *     or {@link #close} is called.
     */
    public SQLiteDatabase getReadableDatabase() {
        synchronized (this) {
            return getDatabaseLocked(false);
        }
    }

    private SQLiteDatabase getDatabaseLocked(boolean writable) {
        if (mDatabase != null) {
            if (!mDatabase.isOpen()) {
                // Darn!  The user closed the database by calling mDatabase.close().
                mDatabase = null;
            } else if (!writable || !mDatabase.isReadOnly()) {
                // The database is already open for business.
                return mDatabase;
            }
        }

        if (mIsInitializing) {
            throw new IllegalStateException("getDatabase called recursively");
        }

        SQLiteDatabase db = mDatabase;
        try {
            mIsInitializing = true;

            if (db != null) {
                if (writable && db.isReadOnly()) {
                    db.reopenReadWrite();
                }
            } else if (mName == null) {
                db = SQLiteDatabase.createInMemory(mOpenParamsBuilder.build());
            } else {
                final File filePath = mContext.getDatabasePath(mName);
                SQLiteDatabase.OpenParams params = mOpenParamsBuilder.build();
                try {
                    db = SQLiteDatabase.openDatabase(filePath, params);
                    // Keep pre-O-MR1 behavior by resetting file permissions to 660
                    setFilePermissionsForDb(filePath.getPath());
                } catch (SQLException ex) {
                    if (writable) {
                        throw ex;
                    }
                    Log.e(TAG, "Couldn't open " + mName
                            + " for writing (will try read-only):", ex);
                    params = params.toBuilder().addOpenFlags(SQLiteDatabase.OPEN_READONLY).build();
                    db = SQLiteDatabase.openDatabase(filePath, params);
                }
            }

            onConfigure(db);

            final int version = db.getVersion();
            if (version != mNewVersion) {
                if (db.isReadOnly()) {
                    throw new SQLiteException("Can't upgrade read-only database from version " +
                            db.getVersion() + " to " + mNewVersion + ": " + mName);
                }

                if (version > 0 && version < mMinimumSupportedVersion) {
                    File databaseFile = new File(db.getPath());
                    onBeforeDelete(db);
                    db.close();
                    if (SQLiteDatabase.deleteDatabase(databaseFile)) {
                        mIsInitializing = false;
                        return getDatabaseLocked(writable);
                    } else {
                        throw new IllegalStateException("Unable to delete obsolete database "
                                + mName + " with version " + version);
                    }
                } else {
                    db.beginTransaction();
                    try {
                        if (version == 0) {
                            onCreate(db);
                        } else {
                            if (version > mNewVersion) {
                                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();
            }
        }
    }

    private static void setFilePermissionsForDb(String dbPath) {
        int perms = FileUtils.S_IRUSR | FileUtils.S_IWUSR | FileUtils.S_IRGRP | FileUtils.S_IWGRP;
        FileUtils.setPermissions(dbPath, perms, -1, -1);
    }

    /**
     * Close any open database object.
     */
    public synchronized void close() {
        if (mIsInitializing) throw new IllegalStateException("Closed during initialization");

        if (mDatabase != null && mDatabase.isOpen()) {
            mDatabase.close();
            mDatabase = null;
        }
    }

    /**
     * Called when the database connection is being configured, to enable features such as
     * write-ahead logging or foreign key support.
     * <p>
     * This method is called before {@link #onCreate}, {@link #onUpgrade}, {@link #onDowngrade}, or
     * {@link #onOpen} are called. It should not modify the database except to configure the
     * database connection as required.
     * </p>
     * <p>
     * This method should only call methods that configure the parameters of the database
     * connection, such as {@link SQLiteDatabase#enableWriteAheadLogging}
     * {@link SQLiteDatabase#setForeignKeyConstraintsEnabled}, {@link SQLiteDatabase#setLocale},
     * {@link SQLiteDatabase#setMaximumSize}, or executing PRAGMA statements.
     * </p>
     *
     * @param db The database.
     */
    public void onConfigure(SQLiteDatabase db) {}

    /**
     * Called before the database is deleted when the version returned by
     * {@link SQLiteDatabase#getVersion()} is lower than the minimum supported version passed (if at
     * all) while creating this helper. After the database is deleted, a fresh database with the
     * given version is created. This will be followed by {@link #onConfigure(SQLiteDatabase)} and
     * {@link #onCreate(SQLiteDatabase)} being called with a new SQLiteDatabase object
     *
     * @param db the database opened with this helper
     * @see #SQLiteOpenHelper(Context, String, CursorFactory, int, int, DatabaseErrorHandler)
     * @hide
     */
    public void onBeforeDelete(SQLiteDatabase db) {
    }

    /**
     * Called when the database is created for the first time. This is where the
     * creation of tables and the initial population of the tables should happen.
     *
     * @param db The database.
     */
    public abstract void onCreate(SQLiteDatabase db);

    /**
     * Called when the database needs to be upgraded. The implementation
     * should use this method to drop tables, add tables, or do anything else it
     * needs to upgrade to the new schema version.
     *
     * <p>
     * The SQLite ALTER TABLE documentation can be found
     * <a href="http://sqlite.org/lang_altertable.html">here</a>. If you add new columns
     * you can use ALTER TABLE to insert them into a live table. If you rename or remove columns
     * you can use ALTER TABLE to rename the old table, then create the new table and then
     * populate the new table with the contents of the old table.
     * </p><p>
     * This method executes within a transaction.  If an exception is thrown, all changes
     * will automatically be rolled back.
     * </p>
     * <p>
     * <em>Important:</em> You should NOT modify an existing migration step from version X to X+1
     * once a build has been released containing that migration step.  If a migration step has an
     * error and it runs on a device, the step will NOT re-run itself in the future if a fix is made
     * to the migration step.</p>
     * <p>For example, suppose a migration step renames a database column from {@code foo} to
     * {@code bar} when the name should have been {@code baz}.  If that migration step is released
     * in a build and runs on a user's device, the column will be renamed to {@code bar}.  If the
     * developer subsequently edits this same migration step to change the name to {@code baz} as
     * intended, the user devices which have already run this step will still have the name
     * {@code bar}.  Instead, a NEW migration step should be created to correct the error and rename
     * {@code bar} to {@code baz}, ensuring the error is corrected on devices which have already run
     * the migration step with the error.</p>
     *
     * @param db The database.
     * @param oldVersion The old database version.
     * @param newVersion The new database version.
     */
    public abstract void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion);

    /**
     * Called when the database needs to be downgraded. This is strictly similar to
     * {@link #onUpgrade} method, but is called whenever current version is newer than requested one.
     * However, this method is not abstract, so it is not mandatory for a customer to
     * implement it. If not overridden, default implementation will reject downgrade and
     * throws SQLiteException
     *
     * <p>
     * This method executes within a transaction.  If an exception is thrown, all changes
     * will automatically be rolled back.
     * </p>
     *
     * @param db The database.
     * @param oldVersion The old database version.
     * @param newVersion The new database version.
     */
    public void onDowngrade(SQLiteDatabase db, int oldVersion, int newVersion) {
        throw new SQLiteException("Can't downgrade database from version " +
                oldVersion + " to " + newVersion);
    }

    /**
     * Called when the database has been opened.  The implementation
     * should check {@link SQLiteDatabase#isReadOnly} before updating the
     * database.
     * <p>
     * This method is called after the database connection has been configured
     * and after the database schema has been created, upgraded or downgraded as necessary.
     * If the database connection must be configured in some way before the schema
     * is created, upgraded, or downgraded, do it in {@link #onConfigure} instead.
     * </p>
     *
     * @param db The database.
     */
    public void onOpen(SQLiteDatabase db) {}
}













本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/906816.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Manus在虚拟现实仿真模拟中的应用案例分享

Manus虚拟现实手套作为一种高精度的人机交互设备&#xff0c;在仿真模拟领域展现出了巨大的应用潜力。通过提供实时、准确的手指动作捕捉数据&#xff0c;Manus手套为多个行业带来了前所未有的仿真体验&#xff0c;推动了技术发展和应用创新。 技术特点 1. 高精度手指跟踪 Ma…

ensp中acl的使用

拓扑图及其要求如下 基础配置 检查此上R2配置错误&#xff0c;undo重新写 检查手写配置无误 按要求写配置 要求1完成 因为一个接口的入或者出方向上 只能调用一张acl表格&#xff0c;所以要求二照样在R1上面写 要求3

5. STM32之TIM实验--输出比较(PWM输出,电机,四轴飞行器,智能车,机器人)--(实验5:PWM驱动直流电机)

作者:Whappy,日期:2024.10.29,决战STM32 直流电机的控制就比较简单了,只有数据线和地线,正接正转,反接反转,为了方便,本实验采用H桥电路来控制电机的正反转,H桥电路也很简单,就是4个MOS管构成的2路推挽输出电路. 注:基本上大功率器件,单片机基本上是无法驱动的,都是要靠一部分…

Python基础知识汇总(建议收藏再观看)!

1.执行脚本的两种方式 Python a.py 直接调用Python解释器执行文件 chomd x a.py ./a.py #修改a.py文件的属性&#xff0c;为可执行&#xff0c;在用 ./ 执行a.py 文件 2、简述位、字节的关系 1bytes8bit ,2**8256,可以代表256中变化&#xff0c; 3、简述 ascii、unicode、…

Java中IO的高级操作

目录 缓冲流 缓冲字节输入流&#xff1a; 缓冲字节输出流&#xff1a; 缓冲字符输入流&#xff1a; 缓冲字符输出流&#xff1a; 转换流 转换流字符输入&#xff1a; 转换流字符输出&#xff1a; 练习案例&#xff1a; 打印流 字节打印流&#xff1a; 字符打印流&a…

Matlab高光谱遥感

原文链接&#xff1a;Matlab高光谱遥感https://mp.weixin.qq.com/s?__bizMzUzNTczMDMxMg&mid2247623643&idx5&sne4557ed43728f851140b100f42286988&chksmfa8da23ccdfa2b2a4d795bf4087f672faaa7082d1f52e046616ab7bf196a6eef89ea553d06b1&token1392391660&…

ssm+jsp663数学课程评价系统的设计与开发

博主介绍&#xff1a;专注于Java&#xff08;springboot ssm 等开发框架&#xff09; vue .net php phython node.js uniapp 微信小程序 等诸多技术领域和毕业项目实战、企业信息化系统建设&#xff0c;从业十五余年开发设计教学工作 ☆☆☆ 精彩专栏推荐订阅☆☆☆☆☆不…

openssl-ec-chn命令手册

openssl-ec命令处理EC&#xff08;Elliptic Curve&#xff0c;椭圆曲线&#xff09;密钥。使密钥可以在各种形式之间转换&#xff0c;并打印出其组件。注意&#xff1a;OpenSSL使用“SEC 1:椭圆曲线密码学&#xff08;Elliptic Curve Cryptography&#xff09;”中指定的私钥格…

(JVM)深入JAVA底层 JVM(Java 虚拟机)!带你认识JVM、程序计数器、JVM栈和方法栈还有堆内存!看看JAVA针对这些内存空间都做了什么吧!

1. 什么是JVM java 二进制字节码的运行环境&#xff0c;简称&#xff1a;java 虚拟机&#xff08;Java Virtual Machine&#xff09; 2. 好处是什么 一次编写&#xff0c;到处运行自动内存管理&#xff0c;GC垃圾回收功能数组下标越界检查多态… 3. jdk、jre、jvm 4. 学习J…

OLAP平台架构演化历程

OLAP平台架构演化历程 0 导读 随着大数据的持续发展及数字化转型的兴起&#xff0c;大数据OLAP分析需求越来越迫切&#xff0c;不论是大型互联网企业&#xff0c;还是中小型传统企业&#xff0c;都在积极探索及实践OLAP引擎选型及平台架构建设&#xff0c;大数据技术的蓬勃发展…

Kaggle入门指南(Kaggle竞赛)

文章目录 Kaggle 入门指南1. Kaggle 的功能概述1.1 竞赛1.2 数据集1.3 学习与教程1.4 社区 2. 注册与设置2.1 创建账户2.2 完善个人资料 3. 探索数据集3.1 查找数据集3.2 下载数据集示例代码&#xff1a;加载数据集 3.3 数据预处理示例代码&#xff1a;数据预处理 4. 参与竞赛4…

docker 可用镜像服务地址(2024.10.31亲测可用)

1.错误 Error response from daemon: Get “https://registry-1.docker.io/v2/” 原因&#xff1a;镜像服务器地址不可用。 2.可用地址 编辑daemon.json&#xff1a; vi /etc/docker/daemon.json内容修改如下&#xff1a; {"registry-mirrors": ["https://…

TortoiseSVN小乌龟下载安装(Windows11)

目录 TortoiseSVN 1.14.7工具下载安装 TortoiseSVN 1.14.7 工具 系统&#xff1a;Windows 11 下载 官网&#xff1a;https://tortoisesvn.subversion.org.cn/downloads.html如图选 TortoiseSVN 1.14.7 - 64 位 下载完成 安装 打开 next&#xff0c;next Browse&#xf…

CAD图纸防泄密|哪些措施可以加密公司图纸?五个宝藏方法分享,2024必读!

在工程设计领域&#xff0c;CAD图纸作为企业的核心资产&#xff0c;其安全性至关重要。一旦图纸泄露&#xff0c;不仅可能给企业带来重大的经济损失&#xff0c;还可能损害企业的声誉和竞争力。 那么&#xff0c;怎么实现CAD图纸防泄密呢&#xff1f; 以下是五个CAD图纸防泄密…

Centos7安装最新版EMQX(v5.8.1)

引言 由于从centos停止维护&#xff0c;导致yum源不可用&#xff0c;好多方法安装都有问题&#xff0c;各种缺依赖&#xff1b;其实最简单就是换操作系统&#xff0c;Ubuntu&#xff0c;centos停止维护之后我们现在服务器基本上都是Ubuntu;而我的环境又不想再折腾&#xff0c;…

网络应用技术 实验二:交换机VLAN 应用(华为ensp)

目录 一、实验简介 二、实验目的 三、实验需求 四、实验拓扑 五、实验任务及要求 1、任务 1&#xff1a;在交换机上创建VLAN 并测试通信 2、任务 2&#xff1a;路由交换机实现VLAN 之间通信 六、实验步骤 1、完成任务 1 2、完成任务 2 一、实验简介 在交换机上配置 VLAN&#x…

数学期望和联合概率密度

数学期望的定义 数学期望是描述随机变量平均趋势的一个重要统计量。根据随机变量的类型&#xff08;离散或连续&#xff09;&#xff0c;数学期望的定义有所不同。 离散型随机变量的数学期望&#xff1a; 若离散型随机变量 X X X取值为 x 1 , x 2 , … , x n , … x_1,x_2,\do…

Python基础学习(十)面向对象编程(基础)

代码获取&#xff1a;https://github.com/qingxuly/hsp_python_course 完结版&#xff1a;Python基础学习&#xff08;完结版&#xff09; 面向对象编程&#xff08;基础&#xff09; 类与对象 类与实例的关系 类与实例的关系示意图 快速入门—面向对象的方式解决养猫问题 …

.bixi勒索病毒来袭:如何防止文件加密与数据丢失?

导言 在网络威胁剧烈的今天&#xff0c;勒索病毒已成为企业和个人面临的重大安全挑战&#xff0c;其中虫洞勒索病毒习得高强度的加密手段和急剧传播的特性引起关注。一旦感染&#xff0c;就会加密关键数据并索要赎金&#xff0c;导致数据无法访问并带来巨大的财务损失。更为严…

OpenCV基础02_图像预处理

图像预处理 在计算机视觉和图像处理领域&#xff0c;图像预处理是一个重要的步骤&#xff0c;它能够提高后续处理&#xff08;如特征提取、目标检测等&#xff09;的准确性和效率。 OpenCV 提供了许多图像预处理的函数和方法&#xff0c;一些常见的图像预处理操作&#xff1a…