Android的SQLiteOpenHelper类 笔记241027
SQLiteOpenHelper
SQLiteOpenHelper是Android开发中用于管理SQLite数据库的一个非常重要的工具类。以下是对SQLiteOpenHelper的详细介绍:
一、基本概念
SQLiteOpenHelper是一个抽象类,它主要用于管理数据库的创建和版本管理。通过继承这个类,开发者可以重写一些方法以实现数据库的创建、升级和降级等功能。
二、主要方法
- 构造方法:用于创建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)
- onCreate(SQLiteDatabase db):在数据库第一次创建时调用,用于执行创建表和初始化数据等操作。
- onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion):在数据库版本升级时调用,用于执行表结构的修改、数据迁移等操作。
- onDowngrade(SQLiteDatabase db, int oldVersion, int newVersion):在数据库版本降级时调用,但这个方法不是必须的,因为在实际开发中降级操作并不常见。
- getReadableDatabase():获取一个可读的数据库对象。如果数据库不存在,则会先调用onCreate()方法创建数据库。
- 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() 方法 |
抽象方法 | 创建数据库时做什么, | 没有对应的数据库时才调用 |
抽象方法 | 升级数据库版本时做什么 | 构造方法参数的版本号上升时才调用 |
非抽象,可选 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
的子类时,我们需要调用其中一个构造方法来初始化父类。然后,我们可以实现 onCreate
、onUpgrade
等方法来定义数据库的结构和升级逻辑。
例如:
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);}@Overridepublic void onCreate(SQLiteDatabase db) {// 创建表的 SQL 语句String CREATE_TABLE = "CREATE TABLE mytable (" +"id INTEGER PRIMARY KEY AUTOINCREMENT," +"name TEXT" +");";db.execSQL(CREATE_TABLE);}@Overridepublic void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {// 版本升级时的逻辑db.execSQL("DROP TABLE IF EXISTS mytable");onCreate(db);}
}
在这个例子中,我们创建了一个名为 MyDatabaseHelper
的类,它继承自 SQLiteOpenHelper
。我们在构造方法中调用了父类的构造方法,并传入了数据库名称、版本号和上下文对象。然后,我们实现了 onCreate
和 onUpgrade
方法来定义数据库的结构和升级逻辑。
三、 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;// 数据库对象,可能为 nullprivate 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) {}// ...(其他方法和内部类省略)
}
关键部分解释:
- 构造函数:
SQLiteOpenHelper
提供了两个构造函数,允许开发者指定数据库名称、版本号、游标工厂和错误处理器。版本号必须大于等于 1。 - getWritableDatabase():此方法用于创建或打开一个数据库,用于读写操作。如果数据库磁盘空间已满,则尝试以只读方式打开,但会抛出异常。
- getReadableDatabase():此方法用于创建或打开一个数据库,用于读取操作。如果数据库磁盘空间已满,则只能以只读方式打开。
- onCreate(SQLiteDatabase db):这是一个抽象方法,当数据库第一次创建时调用。开发者应在此方法中编写创建表和初始化数据的 SQL 语句。
- onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion):这也是一个抽象方法,当数据库版本升级时调用。开发者应在此方法中编写升级数据库的 SQL 语句,如添加新列、修改表结构等。
- onDowngrade(SQLiteDatabase db, int oldVersion, int newVersion):这是一个可选实现的方法,当数据库版本降级时调用。默认情况下,此方法会抛出异常,因为降级操作通常不被推荐。
- 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")@Overrideprotected 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抽象方法,必须实现.@Overridepublic 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抽象方法,必须实现. 在创建数据库时不会调用该方法@Overridepublic 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() 获取数据库时时, 如果数据库版本号发生变动,就会调用该方法,该方法非抽象,可选. 在创建数据库时不会调用该方法@Overridepublic 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() 获取数据库时时, 被调用@Overridepublic 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});}}@Overrideprotected void onStart() {super.onStart(); Log.i("onStart()","onStart()");}@Overrideprotected void onPause() {super.onPause(); Log.i(" onPause()"," onPause()");save();}@Overrideprotected void onStop() {super.onStop(); Log.i("onStop()","onStop()");save();}@Overrideprotected void onRestart() {super.onRestart(); Log.i("onRestart()","onRestart()");}@Overrideprotected void onPostResume() {super.onPostResume(); Log.i("onPostResume()","onPostResume()");}@Overrideprotected 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();}//创建数据库 只在没有数据库时执行@Overridepublic 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 对象,一个旧的版本号和一个新的版本号@Overridepublic 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"; // 创建数据库名叫 Usersprivate Context mContext;public DbContect(Context context){super(context,DBNAME,null,VERSION);mContext = context;}//创建数据库 只在没有数据库时执行public void onCreate(SQLiteDatabase db){//创建密码表 pwd_tbdb.execSQL("create table pwd_tb (pwd varchar(20) primary key)");//创建收入表 user_tbdb.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是一个抽象类,它主要用于管理数据库的创建和版本管理。通过继承这个类,开发者可以重写一些方法以实现数据库的创建、升级和降级等功能。
二、主要方法
- 构造方法:用于创建SQLiteOpenHelper对象,需要传入数据库名称、版本号和一个可选的CursorFactory对象。
- onCreate(SQLiteDatabase db):在数据库第一次创建时调用,用于执行创建表和初始化数据等操作。
- onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion):在数据库版本升级时调用,用于执行表结构的修改、数据迁移等操作。
- onDowngrade(SQLiteDatabase db, int oldVersion, int newVersion):在数据库版本降级时调用,但这个方法不是必须的,因为在实际开发中降级操作并不常见。
- getReadableDatabase():获取一个可读的数据库对象。如果数据库不存在,则会先调用onCreate()方法创建数据库。
- getWritableDatabase():获取一个可写的数据库对象。如果数据库不存在,也会先调用onCreate()方法创建数据库。
三、使用示例
以下是一个使用SQLiteOpenHelper创建数据库、表以及进行增删改查操作的简单示例:
- 创建数据库帮助类:
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);}@Overridepublic void onCreate(SQLiteDatabase db) {String createTable = "CREATE TABLE users (id INTEGER PRIMARY KEY AUTOINCREMENT, name TEXT)";db.execSQL(createTable);}@Overridepublic void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {db.execSQL("DROP TABLE IF EXISTS users");onCreate(db);}
}
- 使用数据库帮助类进行增删改查操作:
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();
四、注意事项
- 数据库版本管理:在开发过程中,随着应用程序的更新,数据库结构可能会发生变化。因此,合理使用onUpgrade()方法来进行数据库结构的升级是非常重要的。
- 异常处理:在进行数据库操作时,可能会遇到各种异常情况,如SQL语法错误、数据库文件损坏等。因此,在实际开发中,应该添加适当的异常处理代码来确保程序的健壮性。
- 性能优化:对于大规模的数据库操作,如批量插入、更新或删除数据,应该考虑使用事务(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);}@Overridepublic void onCreate(SQLiteDatabase db) {db.execSQL(TABLE_CREATE);}@Overridepublic 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;@Overrideprotected 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;@UnsupportedAppUsageprivate 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 WALmOpenParamsBuilder.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.*/@Deprecatedpublic 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 660setFilePermissionsForDb(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) {}
}
相关文章:
Android的SQLiteOpenHelper类 笔记241027
SQLiteOpenHelper SQLiteOpenHelper是Android开发中用于管理SQLite数据库的一个非常重要的工具类。以下是对SQLiteOpenHelper的详细介绍: 一、基本概念 SQLiteOpenHelper是一个抽象类,它主要用于管理数据库的创建和版本管理。通过继承这个类ÿ…...

「Mac畅玩鸿蒙与硬件10」鸿蒙开发环境配置篇10 - 项目实战:计数器应用
本篇将通过一个简单的计数器应用,带你体验鸿蒙开发环境的实际操作流程。本项目主要练习组件的使用、事件响应和状态管理,帮助开发者熟悉基本的应用构建流程。 关键词 计数器应用组件操作事件响应状态管理HarmonyOS 应用开发一、创建计数器项目 1.1 在 DevEco Studio 中新建项…...
安卓逆向之ARM汇编寻址,汇编指令
一:ARM汇编寻址 1. 立即数寻址 (Immediate Addressing) 指令中直接给出一个常数值(立即数),并对其进行操作。 MOV R0, #5 ; 将立即数5载入寄存器R02. 直接寻址 (Direct Addressing) 指令中给出的地址直接指定了内存中的一…...

Idea常见插件(超级实用)
文章目录 Idea好用的插件推荐Idea插件安装Chinese(中文版)Alibaba Java Coding Guidelines(代码规范)Auto Filling Java Arguments(自动补全参数)CamelCase(变量名称格式转换)CodeGeeX(智能&…...
C++中如何获取时间并格式化为字符串?
在C中,你可以使用标准库中的 <chrono> 和 <iomanip> 头文件来获取当前时间并将其格式化为字符串。以下是一个简单的示例,展示了如何获取当前时间并将其格式化为一个可读的字符串(例如:YYYY-MM-DD HH:MM:SS)…...

项目1 yolov5鱼苗检测计数
yolov5鱼苗检测 1. yolov5鱼苗检测1.1. 环境配置1.2 Predict1.3 Validate1.4 Train1.5 生成 ONNX 2 代码解析2.1 模型2.2 数据集2.3 损失函数2.4 训练2.5 预测 之前做的项目,再回顾一下 环境:GPU1卡,CPU4核,每显卡12GB,…...

GPU 学习笔记三:GPU多机多卡组网和拓扑结构分析(基于数据中心分析)
文章目录 一、概述二、数据中心(DC)2.1 数据中心简介2.2 传统数据中心的网络模型2.3 脊叶网络模型(Spine-Leaf)2.4 Facebook的Fabric网络架构 三、基于数据中心的多机多卡拓扑3.1 Spine-Leaf 架构网络规模测算方法3.2 NVIDIA多机多…...

各编程语言处理HTTP状态码的库推荐
Http 状态码用那个库 备注 Spring 的状态码库为 org.springframework.http.HttpStatus Apache 的状态码库为: org.apache.http.HttpStatus 通常这 2 个库都差不多。 如你的项目中已经用了 Spring 的代码的话,那么就用 Spring 的库吧。 不管是那个库…...
【Mac】Python 环境管理工具
一、pyenv 1、安装 (1)安装 brew install pyenv(2)环境配置 查看系统使用 shell 是 bash 还是 zsh bash 配置文件:~/.bash_profile zsh 配置文件:~/.zshrc userMac ~ % echo $SHELL /bin/zsh userMa…...

大语言模型数据流程源码解读(基于llama3模型)
文章目录 前言一、数据进入LlamaForCausalLM(LlamaPreTrainedModel)类二、数据进入LlamaModel(LlamaPreTrainedModel)类1、input_ids的embedding编码2、position_ids位置获取3、causal_mask因果mask构建1、causal_mask调用2、因果mask代码解读(_update_causal_mask)4、hidden_s…...
[蓝桥杯 2015 省 A] 饮料换购
题目描述 乐羊羊饮料厂正在举办一次促销优惠活动。乐羊羊 C 型饮料,凭 3 个瓶盖可以再换一瓶 C 型饮料,并且可以一直循环下去(但不允许暂借或赊账)。 请你计算一下,如果小明不浪费瓶盖,尽量地参加活动,那么ÿ…...

K8S测试pod内存和CPU资源不足
只设置requests参数 mysql主从pod启动后监控 读压测之后 同时设置limits和requests,只调低内存值 监控 压力测试 同时设置limits和requests,只调低CPU值 初始状态 开始压测 结论 对于CPU,如果pod中服务使用CPU超过设置的limits&…...

rabbitmq 使用注意事项
1,注意开启的端口号,一共四个端口号,1883是mqtt连接的端口号,如果没开,是连接不上的需要手动起mqtt插件。 //开始mqtt插件服务 rabbitmq-plugins enable rabbitmq_mqtt 2,15672端口是http网页登录的管理后…...

<项目代码>YOLOv8 夜间车辆识别<目标检测>
YOLOv8是一种单阶段(one-stage)检测算法,它将目标检测问题转化为一个回归问题,能够在一次前向传播过程中同时完成目标的分类和定位任务。相较于两阶段检测算法(如Faster R-CNN),YOLOv8具有更高的…...
xterm.js 库作用
前言:xterm.js 是一个用于在网页上模拟终端的强大 JavaScript 库。 一、在网页中实现终端模拟 1. 提供类似终端的界面 xterm.js可以在浏览器中创建一个看起来和行为都类似于传统终端的界面。这包括显示命令行提示符、接受用户输入、显示命令输出等。 例如&#…...

在Excel中如何快速筛选非特定颜色
Excel中的自动筛选是个非常强大的工具,不仅可以筛选内容,而且可以筛选颜色,例如筛选A列红色单元格。但是有时希望筛选除了红色之外的单元格(下图右侧所示),其他单元格的填充色不固定,有几种颜色…...
kotlin定时器和主线程定时器
场景 最近要用kotlin写一个每隔一段时间切视频并截图 刷刷的就写出来了,很快啊 timerTask object : TimerTask() {override fun run() {captureWindow()if ((group 1) * 4 > urls.size) {showDialog()timerTask.cancel()timer.cancel()}groupupdatePlayers(…...

vscode不能执行vue命令/ vue : 无法加载文件
问题: 解决: 1. 在Windows应用中找到Windows PowerShell,以管理员运行: 2. 在命令框输入: set-ExecutionPolicy RemoteSigned, 然后输入A即可解决...
1.4 STL C++面试问题
1.4.1 说说STL的基本组成部分 总结 STL 的基本组成部分包括容器、算法、迭代器、函数对象和仿函数和适配器。通过这些组件,STL 提供了高效、灵活和可复用的代码结构,极大地提高了 C 的开发效率和程序的可维护性。STL 的设计思想使得算法和数据结构的使…...
Bash、sh 和 Shell都弄混了?
在Linux和Unix系统中,Bash、sh 和 Shell 都与命令行解释器相关,但它们各自的含义和作用略有不同。以下是它们之间的关系和区别: Shell Shell 是一个通用术语,指的是操作系统中负责解释和执行用户命令的程序。它是用户与操作系统…...

Prompt Tuning、P-Tuning、Prefix Tuning的区别
一、Prompt Tuning、P-Tuning、Prefix Tuning的区别 1. Prompt Tuning(提示调优) 核心思想:固定预训练模型参数,仅学习额外的连续提示向量(通常是嵌入层的一部分)。实现方式:在输入文本前添加可训练的连续向量(软提示),模型只更新这些提示参数。优势:参数量少(仅提…...
Admin.Net中的消息通信SignalR解释
定义集线器接口 IOnlineUserHub public interface IOnlineUserHub {/// 在线用户列表Task OnlineUserList(OnlineUserList context);/// 强制下线Task ForceOffline(object context);/// 发布站内消息Task PublicNotice(SysNotice context);/// 接收消息Task ReceiveMessage(…...

云启出海,智联未来|阿里云网络「企业出海」系列客户沙龙上海站圆满落地
借阿里云中企出海大会的东风,以**「云启出海,智联未来|打造安全可靠的出海云网络引擎」为主题的阿里云企业出海客户沙龙云网络&安全专场于5.28日下午在上海顺利举办,现场吸引了来自携程、小红书、米哈游、哔哩哔哩、波克城市、…...

《通信之道——从微积分到 5G》读书总结
第1章 绪 论 1.1 这是一本什么样的书 通信技术,说到底就是数学。 那些最基础、最本质的部分。 1.2 什么是通信 通信 发送方 接收方 承载信息的信号 解调出其中承载的信息 信息在发送方那里被加工成信号(调制) 把信息从信号中抽取出来&am…...
Angular微前端架构:Module Federation + ngx-build-plus (Webpack)
以下是一个完整的 Angular 微前端示例,其中使用的是 Module Federation 和 npx-build-plus 实现了主应用(Shell)与子应用(Remote)的集成。 🛠️ 项目结构 angular-mf/ ├── shell-app/ # 主应用&…...

SiFli 52把Imagie图片,Font字体资源放在指定位置,编译成指定img.bin和font.bin的问题
分区配置 (ptab.json) img 属性介绍: img 属性指定分区存放的 image 名称,指定的 image 名称必须是当前工程生成的 binary 。 如果 binary 有多个文件,则以 proj_name:binary_name 格式指定文件名, proj_name 为工程 名&…...
A2A JS SDK 完整教程:快速入门指南
目录 什么是 A2A JS SDK?A2A JS 安装与设置A2A JS 核心概念创建你的第一个 A2A JS 代理A2A JS 服务端开发A2A JS 客户端使用A2A JS 高级特性A2A JS 最佳实践A2A JS 故障排除 什么是 A2A JS SDK? A2A JS SDK 是一个专为 JavaScript/TypeScript 开发者设计的强大库ÿ…...

Linux nano命令的基本使用
参考资料 GNU nanoを使いこなすnano基础 目录 一. 简介二. 文件打开2.1 普通方式打开文件2.2 只读方式打开文件 三. 文件查看3.1 打开文件时,显示行号3.2 翻页查看 四. 文件编辑4.1 Ctrl K 复制 和 Ctrl U 粘贴4.2 Alt/Esc U 撤回 五. 文件保存与退出5.1 Ctrl …...

如何应对敏捷转型中的团队阻力
应对敏捷转型中的团队阻力需要明确沟通敏捷转型目的、提升团队参与感、提供充分的培训与支持、逐步推进敏捷实践、建立清晰的奖励和反馈机制。其中,明确沟通敏捷转型目的尤为关键,团队成员只有清晰理解转型背后的原因和利益,才能降低对变化的…...
LangChain 中的文档加载器(Loader)与文本切分器(Splitter)详解《二》
🧠 LangChain 中 TextSplitter 的使用详解:从基础到进阶(附代码) 一、前言 在处理大规模文本数据时,特别是在构建知识库或进行大模型训练与推理时,文本切分(Text Splitting) 是一个…...