在Android应用程序中对SQLite数据库执行查询时,什么被认为是最佳实践?
从AsyncTask的doInBackground中运行插入、删除和选择查询是否安全?或者我应该使用UI线程?我认为数据库查询可能是“沉重的”,不应该使用UI线程,因为它可以锁定应用程序-导致应用程序无响应(ANR)。
如果我有几个asynctask,他们应该共享一个连接还是应该各自打开一个连接?
对于这些场景有什么最佳实践吗?
在Android应用程序中对SQLite数据库执行查询时,什么被认为是最佳实践?
从AsyncTask的doInBackground中运行插入、删除和选择查询是否安全?或者我应该使用UI线程?我认为数据库查询可能是“沉重的”,不应该使用UI线程,因为它可以锁定应用程序-导致应用程序无响应(ANR)。
如果我有几个asynctask,他们应该共享一个连接还是应该各自打开一个连接?
对于这些场景有什么最佳实践吗?
当前回答
您可以尝试应用谷歌I/O 2017上宣布的新架构方法。
它还包括新的ORM库Room
它包含三个主要组件:@Entity, @Dao和@Database
User.java
@Entity
public class User {
@PrimaryKey
private int uid;
@ColumnInfo(name = "first_name")
private String firstName;
@ColumnInfo(name = "last_name")
private String lastName;
// Getters and setters are ignored for brevity,
// but they're required for Room to work.
}
UserDao.java
@Dao
public interface UserDao {
@Query("SELECT * FROM user")
List<User> getAll();
@Query("SELECT * FROM user WHERE uid IN (:userIds)")
List<User> loadAllByIds(int[] userIds);
@Query("SELECT * FROM user WHERE first_name LIKE :first AND "
+ "last_name LIKE :last LIMIT 1")
User findByName(String first, String last);
@Insert
void insertAll(User... users);
@Delete
void delete(User user);
}
AppDatabase.java
@Database(entities = {User.class}, version = 1)
public abstract class AppDatabase extends RoomDatabase {
public abstract UserDao userDao();
}
其他回答
数据库在多线程方面非常灵活。我的应用程序从许多不同的线程同时击中它们的db,它做得很好。在某些情况下,我有多个进程同时访问数据库,这也很好。
你的异步任务-当你可以使用相同的连接,但如果你必须,它可以从不同的任务访问数据库。
在多线程中插入、更新、删除和读取通常是可以的,但是Brad的答案是不正确的。你必须小心如何创建和使用你的联系。在某些情况下,即使数据库没有损坏,更新调用也会失败。
最基本的答案。
SqliteOpenHelper对象保留一个数据库连接。它看似为您提供了读写连接,但实际上并非如此。调用read-only,无论如何都将获得写数据库连接。
一个helper实例,一个db连接。即使您在多个线程中使用它,也只能一次使用一个连接。SqliteDatabase对象使用java锁来保持访问序列化。因此,如果100个线程拥有一个db实例,则对实际磁盘上数据库的调用将被序列化。
一个helper,一个db连接,在java代码中序列化。一个线程,1000个线程,如果你使用一个helper实例在它们之间共享,你所有的db访问代码都是串行的。生活是美好的。
如果您试图同时从不同的实际连接写入数据库,其中一个将失败。它不会等到第一个完成后再写。它将不编写您的更改。更糟糕的是,如果您没有在SQLiteDatabase上调用正确的insert/update版本,则不会出现异常。您只会在LogCat中收到一条消息,仅此而已。
多线程?使用一个帮手。时期。如果你知道只有一个线程在写,你可以使用多个连接,你的读取速度会更快,但买家要小心。我还没测试那么多。
这里有一篇博客文章提供了更多的细节和一个示例应用程序。
Android Sqlite锁定(更新链接6/18/2012) android - database - lock - collisions - example by touchlab on GitHub
Gray和我实际上正在包装一个ORM工具,基于他的Ormlite,它与Android数据库实现原生工作,并遵循我在博客文章中描述的安全创建/调用结构。应该很快就能出来了。来看看。
与此同时,还有一篇后续博文:
单个SQLite连接
同样签出前面提到的锁定示例的2point0的fork:
Android-Database-Locking-Collisions-Example by 2point0 on GitHub
德米特罗的回答很适合我。 我认为最好将函数声明为同步的。至少在我的情况下,它会调用空指针异常,否则,例如getWritableDatabase尚未返回在一个线程和opendatabase在另一个线程同时调用。
public synchronized SQLiteDatabase openDatabase() {
if(mOpenCounter.incrementAndGet() == 1) {
// Opening new database
mDatabase = mDatabaseHelper.getWritableDatabase();
}
return mDatabase;
}
我对SQLiteDatabase api的理解是,如果你有一个多线程应用程序,你不能有超过1个SQLiteDatabase对象指向单个数据库。
对象当然可以创建,但是如果不同的线程/进程(也)开始使用不同的SQLiteDatabase对象(就像我们在JDBC Connection中使用的那样),则插入/更新会失败。
这里唯一的解决方案是坚持使用1个SQLiteDatabase对象,无论何时startTransaction()在多个线程中使用,Android管理跨不同线程的锁定,并且一次只允许一个线程具有独占更新访问。
你也可以从数据库中“读取”,并在不同的线程中使用相同的SQLiteDatabase对象(而另一个线程写入),永远不会有数据库损坏,即“读线程”不会从数据库中读取数据,直到“写线程”提交数据,尽管两者都使用相同的SQLiteDatabase对象。
这与JDBC中的连接对象不同,在JDBC中,如果在读写线程之间传递连接对象(使用相同的方法),那么我们也可能打印未提交的数据。
在我的企业应用程序中,我尝试使用条件检查,以便UI线程永远不必等待,而BG线程持有SQLiteDatabase对象(独占)。我试图预测UI动作和推迟BG线程从运行'x'秒。还可以维护PriorityQueue来管理分发SQLiteDatabase Connection对象,以便UI线程首先获得它。
您可以尝试应用谷歌I/O 2017上宣布的新架构方法。
它还包括新的ORM库Room
它包含三个主要组件:@Entity, @Dao和@Database
User.java
@Entity
public class User {
@PrimaryKey
private int uid;
@ColumnInfo(name = "first_name")
private String firstName;
@ColumnInfo(name = "last_name")
private String lastName;
// Getters and setters are ignored for brevity,
// but they're required for Room to work.
}
UserDao.java
@Dao
public interface UserDao {
@Query("SELECT * FROM user")
List<User> getAll();
@Query("SELECT * FROM user WHERE uid IN (:userIds)")
List<User> loadAllByIds(int[] userIds);
@Query("SELECT * FROM user WHERE first_name LIKE :first AND "
+ "last_name LIKE :last LIMIT 1")
User findByName(String first, String last);
@Insert
void insertAll(User... users);
@Delete
void delete(User user);
}
AppDatabase.java
@Database(entities = {User.class}, version = 1)
public abstract class AppDatabase extends RoomDatabase {
public abstract UserDao userDao();
}