具体讲解看博主的另一篇文章。
java
package com.mySQLiteTest.SQLiteTest;
import android.content.Context;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
/**
* @File Name: mySurnameDatabase .java
* @Date: April 22, 2026
* @Version: 1.0
* @Author: 施棠海
* @描述: 数据库安全初始化、安全获取数据。
*/
public class mySurnameDatabase {
private static final String TAG = "mySurnameDatabase";
private static final String DB_NAME = "SurnameInitialDb.db" ; // 数据库名
private static final String TABLE_NAME = "HundredFamilySurnames"; // 表名
private static mySurnameDatabase instance;
private Context mContext;
private SQLiteDatabase database;
private String dbPath;
private boolean initialized = false;
public mySurnameDatabase(Context context){
mContext = context.getApplicationContext();
// 获取数据库文件路径(指的是如果db文件拷贝,设备会默认放在哪个路径下,不需要这个文件当前真的在这个目录下存在)
dbPath = mContext.getDatabasePath(DB_NAME).getAbsolutePath();
}
public static synchronized mySurnameDatabase getInstance(Context context){
if (instance == null){
// 第一次创建单例时调用构造函数
instance = new mySurnameDatabase(context);
}
return instance;
}
public void initDB() {
try {
File dbFile = new File(dbPath);
// 开始拷贝文件
File dbFilePath = dbFile.getParentFile();
if (!dbFilePath.exists()){ // 如果父目录不存在就递归创建所有父目录
dbFilePath.mkdirs();
}
// 如果数据库文件已经存在,则删除
if (dbFile.exists()){
dbFile.delete();
}
// 创建数据库文件
// InputStream 打开一条读取管道,程序可以从中一个字节一个字节地取数据
// FileOutputStream 打开一条写入管道,程序通过它创建/覆盖文件并写入字节。创建一个文件输出流,向指定的文件写入数据。
try (InputStream input = mContext.getAssets().open(DB_NAME);FileOutputStream output = new FileOutputStream(dbFile)){
// 获取数据库文件大小
int dbAvailable = input.available();
Debug.i(TAG,"已发现数据库文件,大小:"+dbAvailable+" bytes");
if (dbAvailable<=0){
// 发送错误信息
throw new IOException(DB_NAME+" 文件为空或不存在");
}
// 缓冲区buffer,用于存储数据库文件
byte[] buffer = new byte[1024];
int byteSizeAll = 0;
int byteSize = 0;
// 读取数据库文件
// input.read(byte[] b)方法返回读取的字节数,如果返回0,则说明文件读取完毕。读取时会把读取的文件内容缓存到buffer中
while ((byteSize = input.read(buffer)) > 0){
// 向指定的目录下写入指定的文件
output.write(buffer,0,byteSize);
byteSizeAll += byteSize;
}
// 刷新缓冲区,确保数据写入。刷新此输出流并强制将任何缓冲的输出字节写出
output.flush();
Debug.i(TAG,DB_NAME+" 文件复制完成。大小: "+byteSizeAll+" bytes。 路径: "+dbPath);
}
// 打开数据库并设置只读模式
database = SQLiteDatabase.openDatabase(dbPath, null, SQLiteDatabase.OPEN_READONLY);
initialized = true;
Debug.i(TAG,"数据库初始化完成");
} catch (IOException e) {
Debug.e(TAG,"数据库初始化失败 "+ e.getMessage(),e);
initialized = false;
}
}
public String getInitial(String Surname){
try {
// 若未初始化,则初始化
if(!initialized && database == null){
Debug.i(TAG,"未初始化,执行初始化程序");
initDB();
}
// 若初始化失败,则返回空值
if(!initialized && database == null){
Debug.i(TAG,"初始化失败,返回空值");
return "";
}
// 若数据库未打开,则打开数据库
if(!database.isOpen()){
database = SQLiteDatabase.openDatabase(dbPath, null, SQLiteDatabase.OPEN_READONLY);
}
// 查询数据库并获取结果集
Cursor cursor = database.rawQuery(
// SELECT 字段 FROM 表名 WHERE 条件
"SELECT Initial FROM " + TABLE_NAME + " WHERE Name = ?",
// 把参数放入数组中,SQLite会自动将问号替换为参数,防止SQL注入攻击。
new String[]{Surname}
);
String initial = null;
// 使光标移到结果集的第一行,若当前行存在结果则返回 true,否则返回 false
if(cursor.moveToFirst()){
// 获取当前行指定列的值
initial = cursor.getString(cursor.getColumnIndexOrThrow("Initial"));
}
// 关闭结果集,否则会内存泄露。
cursor.close();
Debug.i(TAG,"找到的首字母为 "+initial);
return initial == null?"":initial;
} catch (Exception e) {
Debug.e(TAG,"获取首字母失败 "+e.getMessage(),e);
return "";
}
}
public void close(){
try {
if (database != null && database.isOpen()) {
// 关闭数据库
database.close();
}
database = null;
instance = null;
initialized = false;
Debug.i(TAG,"成功关闭数据库");
} catch (Exception e) {
Debug.e(TAG,"关闭数据库异常 "+e.getMessage(),e);
}
}
}