Android平台SQLite开发组件添加指南
1. 准备工作
在Android平台上使用SQLite进行开发,首先要确保开发环境的搭建是正确的。这包括安装最新版本的Android Studio,因为新版本通常会对SQLite的支持进行优化和更新。同时,Android SDK也需要保持最新,特别是与数据库操作相关的部分。
1.1 检查Android SDK
打开Android Studio,点击 File
-> Project Structure
。在弹出的窗口中,选择 SDK Location
。在这里,你可以看到当前安装的Android SDK的路径。确保 Android SDK Platform - Tools
和 Android SDK Build - Tools
都是最新版本。如果不是,可以点击 SDK Update Sites
中的 Sources
链接,前往SDK管理器进行更新。
1.2 项目配置
在你的Android项目的 build.gradle
文件中,确保 minSdkVersion
满足SQLite使用的基本要求。一般来说,SQLite在Android 1.0(API Level 1)及以上版本都有支持,但为了获取更好的兼容性和功能,建议将 minSdkVersion
设置为14(Android 4.0)及以上。
android {
compileSdkVersion 33
defaultConfig {
applicationId "com.example.yourpackage"
minSdkVersion 14
targetSdkVersion 33
versionCode 1
versionName "1.0"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
}
}
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
}
2. SQLiteOpenHelper类的使用
SQLiteOpenHelper
是Android中用于管理数据库创建和版本管理的一个重要类。通过继承这个类,我们可以方便地创建、升级和管理SQLite数据库。
2.1 创建SQLiteOpenHelper子类
创建一个新的Java类,例如 MyDatabaseHelper
,继承自 SQLiteOpenHelper
。在这个类中,需要实现两个重要的方法:onCreate
和 onUpgrade
。
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;
// 构造函数
public MyDatabaseHelper(Context context) {
super(context, DATABASE_NAME, null, DATABASE_VERSION);
}
@Override
public void onCreate(SQLiteDatabase db) {
// 创建表的SQL语句
String createTableQuery = "CREATE TABLE users (" +
"id INTEGER PRIMARY KEY AUTOINCREMENT," +
"name TEXT," +
"age INTEGER)";
db.execSQL(createTableQuery);
}
@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
// 当数据库版本更新时执行的操作,这里简单地删除旧表并创建新表
db.execSQL("DROP TABLE IF EXISTS users");
onCreate(db);
}
}
在上述代码中:
MyDatabaseHelper
类的构造函数接收一个Context
参数,并调用父类的构造函数,传入数据库名称、游标工厂(这里为null
,表示使用默认的游标工厂)和数据库版本。onCreate
方法在数据库首次创建时被调用。在这个方法中,我们执行了一条SQL语句来创建一个名为users
的表,该表包含id
(自增长的主键)、name
(文本类型)和age
(整数类型)三个字段。onUpgrade
方法在数据库版本发生变化时被调用。这里的示例代码只是简单地删除旧表并重新创建新表,实际应用中可能需要更复杂的数据迁移逻辑。
2.2 获取数据库实例
在需要使用数据库的地方,例如Activity或Service中,可以通过创建 MyDatabaseHelper
的实例来获取数据库对象。
import android.os.Bundle;
import android.widget.Toast;
import androidx.appcompat.app.AppCompatActivity;
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// 创建MyDatabaseHelper实例
MyDatabaseHelper dbHelper = new MyDatabaseHelper(this);
// 获取可写数据库
SQLiteDatabase db = dbHelper.getWritableDatabase();
if (db != null) {
Toast.makeText(this, "数据库已创建或打开", Toast.LENGTH_SHORT).show();
db.close();
}
}
}
在上述代码中,通过 MyDatabaseHelper
的实例调用 getWritableDatabase
方法来获取一个可写的数据库实例。如果数据库不存在,getWritableDatabase
方法会先调用 onCreate
方法创建数据库。获取数据库实例后,我们可以进行各种数据库操作,操作完成后记得关闭数据库以释放资源。
3. 基本的数据库操作
3.1 插入数据
向SQLite数据库中插入数据可以使用 SQLiteDatabase
的 insert
方法。
import android.content.ContentValues;
import android.database.sqlite.SQLiteDatabase;
public class DataInsertion {
public static void insertUser(SQLiteDatabase db, String name, int age) {
// 创建ContentValues对象
ContentValues values = new ContentValues();
values.put("name", name);
values.put("age", age);
// 插入数据
long newRowId = db.insert("users", null, values);
if (newRowId != -1) {
System.out.println("数据插入成功,新行ID:" + newRowId);
} else {
System.out.println("数据插入失败");
}
}
}
在上述代码中:
ContentValues
类用于存储要插入的数据,它类似于一个键值对的映射。db.insert
方法的第一个参数是表名,第二个参数是nullColumnHack
,当ContentValues
为空时,需要指定一个列名,这里因为ContentValues
不为空,所以设置为null
。第三个参数是ContentValues
对象。insert
方法返回插入行的ID,如果返回-1
,表示插入失败。
3.2 查询数据
查询数据可以使用 SQLiteDatabase
的 query
方法。
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
public class DataQuery {
public static void queryAllUsers(SQLiteDatabase db) {
// 选择所有列
String[] projection = {"id", "name", "age"};
// 不使用where子句
String selection = null;
String[] selectionArgs = null;
// 不进行分组
String groupBy = null;
String having = null;
// 按id升序排序
String orderBy = "id ASC";
Cursor cursor = db.query(
"users",
projection,
selection,
selectionArgs,
groupBy,
having,
orderBy
);
if (cursor != null) {
while (cursor.moveToNext()) {
int id = cursor.getInt(cursor.getColumnIndexOrThrow("id"));
String name = cursor.getString(cursor.getColumnIndexOrThrow("name"));
int age = cursor.getInt(cursor.getColumnIndexOrThrow("age"));
System.out.println("用户ID:" + id + ",姓名:" + name + ",年龄:" + age);
}
cursor.close();
}
}
}
在上述代码中:
projection
定义了要查询的列。selection
和selectionArgs
用于构建WHERE
子句,如果不使用WHERE
子句,可以设置为null
。groupBy
和having
用于分组查询,这里不使用,设置为null
。orderBy
用于指定排序方式。query
方法返回一个Cursor
对象,通过Cursor
可以遍历查询结果集。
3.3 更新数据
更新数据可以使用 SQLiteDatabase
的 update
方法。
import android.content.ContentValues;
import android.database.sqlite.SQLiteDatabase;
public class DataUpdate {
public static void updateUserAge(SQLiteDatabase db, int newAge, int userId) {
// 创建ContentValues对象
ContentValues values = new ContentValues();
values.put("age", newAge);
// 更新数据的条件
String selection = "id =?";
String[] selectionArgs = {String.valueOf(userId)};
int count = db.update(
"users",
values,
selection,
selectionArgs
);
if (count > 0) {
System.out.println("数据更新成功,更新行数:" + count);
} else {
System.out.println("数据更新失败");
}
}
}
在上述代码中:
ContentValues
对象用于存储要更新的数据。selection
和selectionArgs
定义了更新的条件,这里表示更新id
等于指定值的记录。update
方法返回更新的行数,如果返回0
,表示没有记录被更新。
3.4 删除数据
删除数据可以使用 SQLiteDatabase
的 delete
方法。
import android.database.sqlite.SQLiteDatabase;
public class DataDelete {
public static void deleteUser(SQLiteDatabase db, int userId) {
// 删除数据的条件
String selection = "id =?";
String[] selectionArgs = {String.valueOf(userId)};
int count = db.delete(
"users",
selection,
selectionArgs
);
if (count > 0) {
System.out.println("数据删除成功,删除行数:" + count);
} else {
System.out.println("数据删除失败");
}
}
}
在上述代码中:
selection
和selectionArgs
定义了删除的条件,这里表示删除id
等于指定值的记录。delete
方法返回删除的行数,如果返回0
,表示没有记录被删除。
4. 事务处理
在数据库操作中,事务是非常重要的概念。事务可以确保一组数据库操作要么全部成功,要么全部失败,从而保证数据的一致性。
4.1 手动事务处理
在Android中,可以通过 SQLiteDatabase
的方法手动管理事务。
import android.database.sqlite.SQLiteDatabase;
public class TransactionExample {
public static void performTransaction(SQLiteDatabase db) {
db.beginTransaction();
try {
// 插入一条数据
ContentValues values1 = new ContentValues();
values1.put("name", "张三");
values1.put("age", 25);
long newRowId1 = db.insert("users", null, values1);
// 更新一条数据
ContentValues values2 = new ContentValues();
values2.put("age", 26);
String selection = "id =?";
String[] selectionArgs = {String.valueOf(newRowId1)};
int count = db.update("users", values2, selection, selectionArgs);
db.setTransactionSuccessful();
} finally {
db.endTransaction();
}
}
}
在上述代码中:
beginTransaction
方法开始一个事务。- 在
try
块中执行一系列数据库操作。如果所有操作都成功,调用setTransactionSuccessful
方法标记事务成功。 - 无论
try
块中的操作是否成功,finally
块中的endTransaction
方法都会被执行。如果事务标记为成功,endTransaction
方法会提交事务;否则,会回滚事务。
4.2 使用事务提升性能
事务不仅可以保证数据一致性,还可以提升数据库操作的性能。例如,批量插入数据时,使用事务可以减少磁盘I/O操作次数。
import android.content.ContentValues;
import android.database.sqlite.SQLiteDatabase;
public class BatchInsertion {
public static void batchInsertUsers(SQLiteDatabase db, String[] names, int[] ages) {
db.beginTransaction();
try {
for (int i = 0; i < names.length; i++) {
ContentValues values = new ContentValues();
values.put("name", names[i]);
values.put("age", ages[i]);
db.insert("users", null, values);
}
db.setTransactionSuccessful();
} finally {
db.endTransaction();
}
}
}
在上述代码中,通过事务将批量插入操作作为一个整体,大大提高了插入效率。
5. SQLite数据库的优化
5.1 索引的使用
索引可以加快数据库查询的速度。在创建表时,可以同时创建索引。
CREATE TABLE users (
id INTEGER PRIMARY KEY AUTOINCREMENT,
name TEXT,
age INTEGER
);
CREATE INDEX idx_name ON users (name);
在上述代码中,通过 CREATE INDEX
语句创建了一个名为 idx_name
的索引,该索引基于 users
表的 name
列。这样在查询 name
列时,数据库可以利用索引快速定位数据,提高查询效率。
5.2 避免频繁的数据库操作
频繁地打开和关闭数据库会消耗大量的系统资源,降低应用性能。可以考虑在应用的生命周期中,尽量保持数据库连接的打开状态,并合理复用数据库操作对象。
public class DatabaseManager {
private static DatabaseManager instance;
private MyDatabaseHelper dbHelper;
private SQLiteDatabase db;
private DatabaseManager(Context context) {
dbHelper = new MyDatabaseHelper(context);
db = dbHelper.getWritableDatabase();
}
public static DatabaseManager getInstance(Context context) {
if (instance == null) {
instance = new DatabaseManager(context);
}
return instance;
}
public SQLiteDatabase getDatabase() {
return db;
}
public void closeDatabase() {
if (db != null) {
db.close();
}
if (dbHelper != null) {
dbHelper.close();
}
}
}
在上述代码中,通过单例模式创建一个 DatabaseManager
类,负责管理数据库的打开和关闭。应用中可以通过 DatabaseManager.getInstance(context)
获取数据库实例,这样可以避免频繁地创建和销毁数据库连接。
5.3 合理使用缓存
在某些情况下,对于一些不经常变化的数据,可以使用缓存来减少对数据库的查询次数。例如,可以使用Android的 SharedPreferences
或内存缓存(如 LruCache
)来缓存部分数据。
import android.content.Context;
import android.content.SharedPreferences;
public class DataCache {
private static final String PREF_NAME = "data_cache";
private static final String KEY_USER_NAME = "user_name";
public static void saveUserName(Context context, String name) {
SharedPreferences.Editor editor = context.getSharedPreferences(PREF_NAME, Context.MODE_PRIVATE).edit();
editor.putString(KEY_USER_NAME, name);
editor.apply();
}
public static String getUserName(Context context) {
SharedPreferences preferences = context.getSharedPreferences(PREF_NAME, Context.MODE_PRIVATE);
return preferences.getString(KEY_USER_NAME, null);
}
}
在上述代码中,通过 SharedPreferences
缓存了用户的姓名。在需要获取用户姓名时,先从缓存中获取,如果缓存中没有,则再从数据库中查询。
6. 多线程下的SQLite使用
6.1 线程安全问题
SQLite本身是线程安全的,但在Android应用中,由于UI线程和后台线程的存在,需要注意数据库操作的线程问题。如果在UI线程中执行长时间的数据库操作,会导致应用卡顿,影响用户体验。
6.2 使用AsyncTask进行数据库操作
AsyncTask
是Android提供的一个方便的异步任务类,可以在后台线程执行数据库操作,并在UI线程更新结果。
import android.os.AsyncTask;
import android.widget.TextView;
import java.util.ArrayList;
import java.util.List;
public class UserQueryTask extends AsyncTask<Void, Void, List<String>> {
private SQLiteDatabase db;
private TextView resultTextView;
public UserQueryTask(SQLiteDatabase db, TextView resultTextView) {
this.db = db;
this.resultTextView = resultTextView;
}
@Override
protected List<String> doInBackground(Void... voids) {
List<String> userList = new ArrayList<>();
String[] projection = {"name", "age"};
Cursor cursor = db.query("users", projection, null, null, null, null, null);
if (cursor != null) {
while (cursor.moveToNext()) {
String name = cursor.getString(cursor.getColumnIndexOrThrow("name"));
int age = cursor.getInt(cursor.getColumnIndexOrThrow("age"));
userList.add("姓名:" + name + ",年龄:" + age);
}
cursor.close();
}
return userList;
}
@Override
protected void onPostExecute(List<String> userList) {
StringBuilder result = new StringBuilder();
for (String user : userList) {
result.append(user).append("\n");
}
resultTextView.setText(result.toString());
}
}
在上述代码中:
UserQueryTask
继承自AsyncTask
,doInBackground
方法在后台线程执行数据库查询操作。onPostExecute
方法在UI线程执行,用于更新UI,将查询结果显示在TextView
中。
6.3 使用线程池
除了 AsyncTask
,还可以使用线程池来管理数据库操作的线程。ExecutorService
是Java提供的一个线程池框架。
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class DatabaseThreadPool {
private static final int THREAD_POOL_SIZE = 5;
private static ExecutorService executorService;
static {
executorService = Executors.newFixedThreadPool(THREAD_POOL_SIZE);
}
public static void executeTask(Runnable task) {
executorService.submit(task);
}
public static void shutdownThreadPool() {
if (executorService != null &&!executorService.isShutdown()) {
executorService.shutdown();
}
}
}
在上述代码中:
- 创建了一个固定大小为5的线程池。
executeTask
方法用于提交任务到线程池执行,shutdownThreadPool
方法用于关闭线程池。
7. SQLite与ContentProvider结合使用
7.1 ContentProvider简介
ContentProvider
是Android四大组件之一,它主要用于在不同的应用程序之间共享数据。通过 ContentProvider
,其他应用可以访问本应用的SQLite数据库中的数据。
7.2 创建ContentProvider
创建一个继承自 ContentProvider
的类,例如 MyContentProvider
。
import android.content.ContentProvider;
import android.content.ContentUris;
import android.content.ContentValues;
import android.content.UriMatcher;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.net.Uri;
public class MyContentProvider extends ContentProvider {
private static final String AUTHORITY = "com.example.mycontentprovider";
private static final String PATH_USERS = "users";
private static final int USERS = 1;
private static final int USER_ID = 2;
private static final UriMatcher uriMatcher;
static {
uriMatcher = new UriMatcher(UriMatcher.NO_MATCH);
uriMatcher.addURI(AUTHORITY, PATH_USERS, USERS);
uriMatcher.addURI(AUTHORITY, PATH_USERS + "/#", USER_ID);
}
private MyDatabaseHelper dbHelper;
@Override
public boolean onCreate() {
dbHelper = new MyDatabaseHelper(getContext());
return true;
}
@Override
public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) {
SQLiteDatabase db = dbHelper.getReadableDatabase();
int match = uriMatcher.match(uri);
switch (match) {
case USERS:
return db.query("users", projection, selection, selectionArgs, null, null, sortOrder);
case USER_ID:
long id = ContentUris.parseId(uri);
String idSelection = "_id =?";
String[] idSelectionArgs = {String.valueOf(id)};
return db.query("users", projection, idSelection, idSelectionArgs, null, null, sortOrder);
default:
throw new IllegalArgumentException("未知的URI:" + uri);
}
}
@Override
public Uri insert(Uri uri, ContentValues values) {
SQLiteDatabase db = dbHelper.getWritableDatabase();
int match = uriMatcher.match(uri);
if (match == USERS) {
long newRowId = db.insert("users", null, values);
if (newRowId > 0) {
Uri newUri = ContentUris.withAppendedId(Uri.parse("content://" + AUTHORITY + "/" + PATH_USERS), newRowId);
getContext().getContentResolver().notifyChange(newUri, null);
return newUri;
}
}
throw new IllegalArgumentException("未知的URI:" + uri);
}
@Override
public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) {
SQLiteDatabase db = dbHelper.getWritableDatabase();
int match = uriMatcher.match(uri);
switch (match) {
case USERS:
return db.update("users", values, selection, selectionArgs);
case USER_ID:
long id = ContentUris.parseId(uri);
String idSelection = "_id =?";
String[] idSelectionArgs = {String.valueOf(id)};
return db.update("users", values, idSelection, idSelectionArgs);
default:
throw new IllegalArgumentException("未知的URI:" + uri);
}
}
@Override
public int delete(Uri uri, String selection, String[] selectionArgs) {
SQLiteDatabase db = dbHelper.getWritableDatabase();
int match = uriMatcher.match(uri);
switch (match) {
case USERS:
return db.delete("users", selection, selectionArgs);
case USER_ID:
long id = ContentUris.parseId(uri);
String idSelection = "_id =?";
String[] idSelectionArgs = {String.valueOf(id)};
return db.delete("users", idSelection, idSelectionArgs);
default:
throw new IllegalArgumentException("未知的URI:" + uri);
}
}
@Override
public String getType(Uri uri) {
int match = uriMatcher.match(uri);
switch (match) {
case USERS:
return "vnd.android.cursor.dir/vnd.com.example.mycontentprovider.users";
case USER_ID:
return "vnd.android.cursor.item/vnd.com.example.mycontentprovider.users";
default:
throw new IllegalArgumentException("未知的URI:" + uri);
}
}
}
在上述代码中:
- 定义了
AUTHORITY
和PATH_USERS
等常量,用于构建ContentProvider
的URI。 UriMatcher
用于匹配不同的URI,根据不同的URI执行不同的数据库操作。- 实现了
ContentProvider
的各个抽象方法,如query
、insert
、update
和delete
,在这些方法中进行数据库操作,并处理不同的URI情况。
7.3 在AndroidManifest.xml中注册ContentProvider
在 AndroidManifest.xml
文件中注册 MyContentProvider
。
<provider
android:name=".MyContentProvider"
android:authorities="com.example.mycontentprovider"
android:exported="true" />
在上述代码中,android:name
指定了 ContentProvider
的类名,android:authorities
指定了 ContentProvider
的权限,android:exported
设置为 true
表示该 ContentProvider
可以被其他应用访问。
7.4 其他应用访问ContentProvider
其他应用可以通过 ContentResolver
来访问 MyContentProvider
提供的数据。
import android.content.ContentResolver;
import android.content.ContentValues;
import android.database.Cursor;
import android.net.Uri;
public class OtherAppAccess {
private static final Uri CONTENT_URI = Uri.parse("content://com.example.mycontentprovider/users");
public static void insertUser(ContentResolver resolver, String name, int age) {
ContentValues values = new ContentValues();
values.put("name", name);
values.put("age", age);
Uri newUri = resolver.insert(CONTENT_URI, values);
if (newUri != null) {
System.out.println("数据插入成功,新行URI:" + newUri);
} else {
System.out.println("数据插入失败");
}
}
public static void queryAllUsers(ContentResolver resolver) {
String[] projection = {"id", "name", "age"};
Cursor cursor = resolver.query(CONTENT_URI, projection, null, null, null);
if (cursor != null) {
while (cursor.moveToNext()) {
int id = cursor.getInt(cursor.getColumnIndexOrThrow("id"));
String name = cursor.getString(cursor.getColumnIndexOrThrow("name"));
int age = cursor.getInt(cursor.getColumnIndexOrThrow("age"));
System.out.println("用户ID:" + id + ",姓名:" + name + ",年龄:" + age);
}
cursor.close();
}
}
}
在上述代码中:
- 定义了
CONTENT_URI
作为访问MyContentProvider
的URI。 insertUser
方法通过ContentResolver
的insert
方法向MyContentProvider
插入数据。queryAllUsers
方法通过ContentResolver
的query
方法查询MyContentProvider
中的数据。
通过以上步骤,我们完成了在Android平台上添加SQLite开发组件,并进行了各种数据库操作、优化以及与 ContentProvider
的结合使用。希望这些内容能帮助你在Android开发中更好地利用SQLite数据库。