Android에서 SQLite를 사용할 때 동시성 문제를 방지하려면 어떻게 해야 합니까?
Android 앱 내에서 SQLite 데이터베이스에서 쿼리를 실행할 때 가장 좋은 방법은 무엇입니까?
비동기 작업의 DoInBackground에서 삽입, 삭제 및 선택 쿼리를 실행해도 안전합니까?아니면 UI 스레드를 사용해야 합니까?저는 데이터베이스 쿼리가 "무거운" 것일 수 있고 UI 스레드를 사용해서는 안 된다고 생각합니다. 이는 앱을 잠글 수 있으므로 ANR(Application Not Response)이 발생할 수 있기 때문입니다.
비동기 작업이 여러 개 있는 경우 연결을 공유해야 합니까, 아니면 각각 연결을 열어야 합니까?
이러한 시나리오에 대한 모범 사례가 있습니까?
여러 스레드에서 삽입, 업데이트, 삭제 및 읽기는 일반적으로 정상이지만 Brad의 대답은 올바르지 않습니다.연결을 만들고 사용하는 방법에 주의해야 합니다.데이터베이스가 손상되지 않은 경우에도 업데이트 호출이 실패할 수 있습니다.
기본적인 답은.
SqliteOpenHelper 개체는 하나의 데이터베이스 연결을 유지합니다.읽기 및 쓰기 연결을 제공하는 것처럼 보이지만 실제로는 그렇지 않습니다.읽기 전용으로 호출하면 쓰기 데이터베이스가 연결됩니다.
도우미 인스턴스 하나, DB 연결 하나.여러 스레드에서 사용하는 경우에도 한 번에 하나의 연결을 사용할 수 있습니다.SqliteDatabase 개체는 Java 잠금을 사용하여 액세스를 직렬화된 상태로 유지합니다.따라서 100개의 스레드에 하나의 DB 인스턴스가 있는 경우 실제 Disk 데이터베이스에 대한 호출이 직렬화됩니다.
즉, 도우미 하나, DB 연결 하나가 자바 코드로 직렬화되어 있습니다.하나의 스레드, 1000개의 스레드, 이들 간에 공유되는 하나의 도우미 인스턴스를 사용하는 경우 모든 DB 액세스 코드는 직렬입니다.그리고 인생은 좋습니다.
실제 고유한 연결에서 동시에 데이터베이스에 쓰려고 하면 연결이 실패합니다.첫 번째 작업이 완료될 때까지 기다렸다가 기록합니다.단순히 거스름돈을 쓰지 않습니다.더 나쁜 것은 SQLite 데이터베이스에서 올바른 버전의 삽입/업데이트를 호출하지 않으면 예외가 발생하지 않습니다.LogCat에 메시지가 표시됩니다. 이상입니다.
그럼, 다중 스레드?도우미 하나를 사용합니다.마침표.스레드가 하나만 작성된다는 것을 알고 있다면 여러 연결을 사용할 수 있으며 읽기 속도가 빨라질 수 있지만 구매자는 주의해야 합니다.저는 그렇게 많이 테스트해 본 적이 없습니다.
여기 훨씬 더 자세한 내용과 예제 앱이 있는 블로그 게시물이 있습니다.
- Android SQLite 잠금(2012년 6월 18일 업데이트된 링크)
- Android-데이터베이스-잠금-충돌-GitHub의 터치랩별 예
Gray와 저는 Android 데이터베이스 구현에서 기본적으로 작동하고 블로그 게시물에 설명한 안전한 생성/호출 구조를 따르는 ORM 툴을 실제로 마무리하고 있습니다.그것은 곧 나올 것입니다.한번 보세요.
한편, 후속 블로그 게시물이 있습니다.
또한 앞에서 언급한 잠금 예제의 2점 0으로 포크를 점검합니다.
동시 데이터베이스 액세스
내 블로그에 있는 같은 기사(포맷하는 것을 더 좋아합니다)
안드로이드 데이터베이스 스레드에 안전하게 액세스하는 방법을 설명하는 작은 기사를 작성했습니다.
자체 SQLiteOpenHelper가 있다고 가정합니다.
public class DatabaseHelper extends SQLiteOpenHelper { ... }
이제 별도의 스레드에서 데이터베이스에 데이터를 기록하려고 합니다.
// Thread 1
Context context = getApplicationContext();
DatabaseHelper helper = new DatabaseHelper(context);
SQLiteDatabase database = helper.getWritableDatabase();
database.insert(…);
database.close();
// Thread 2
Context context = getApplicationContext();
DatabaseHelper helper = new DatabaseHelper(context);
SQLiteDatabase database = helper.getWritableDatabase();
database.insert(…);
database.close();
로그캣에 다음 메시지가 표시되고 변경 내용 중 하나가 기록되지 않습니다.
android.database.sqlite.SQLiteDatabaseLockedException: database is locked (code 5)
이는 새 SQLiteOpenHelper 개체를 만들 때마다 실제로 새 데이터베이스를 연결하기 때문입니다.실제 개별 연결에서 동시에 데이터베이스에 쓰려고 하면 하나가 실패합니다. (위의 답변에서)
여러 스레드가 있는 데이터베이스를 사용하려면 하나의 데이터베이스 연결을 사용해야 합니다.
단일 SQLiteOpenHelper 개체를 보유하고 반환할 singleton 클래스 Database Manager를 만듭니다.
public class DatabaseManager {
private static DatabaseManager instance;
private static SQLiteOpenHelper mDatabaseHelper;
public static synchronized void initializeInstance(SQLiteOpenHelper helper) {
if (instance == null) {
instance = new DatabaseManager();
mDatabaseHelper = helper;
}
}
public static synchronized DatabaseManager getInstance() {
if (instance == null) {
throw new IllegalStateException(DatabaseManager.class.getSimpleName() +
" is not initialized, call initialize(..) method first.");
}
return instance;
}
public SQLiteDatabase getDatabase() {
return new mDatabaseHelper.getWritableDatabase();
}
}
별도의 스레드에서 데이터베이스에 데이터를 쓰는 업데이트된 코드는 다음과 같습니다.
// In your application class
DatabaseManager.initializeInstance(new MySQLiteOpenHelper());
// Thread 1
DatabaseManager manager = DatabaseManager.getInstance();
SQLiteDatabase database = manager.getDatabase()
database.insert(…);
database.close();
// Thread 2
DatabaseManager manager = DatabaseManager.getInstance();
SQLiteDatabase database = manager.getDatabase()
database.insert(…);
database.close();
이것은 당신에게 또 다른 충돌을 가져올 것입니다.
java.lang.IllegalStateException: attempt to re-open an already-closed object: SQLiteDatabase
데이터베이스 연결을 하나만 사용하고 있으므로 method getDatabase()는 Thread1 및 Thread2에 대해 동일한 SQLiteDatabase 개체 인스턴스를 반환합니다.Thread1이 데이터베이스를 닫는 동안 Thread2가 데이터베이스를 사용할 수 있습니다.그래서 불법 주 예외가 발생한 겁니다
아무도 데이터베이스를 사용하지 않는지 확인하고 종료해야 합니다.스택 오버플로우에 있는 일부 사용자는 SQLite 데이터베이스를 절대 닫지 말 것을 권장합니다.그러면 다음과 같은 로그캣 메시지가 표시됩니다.
Leak found
Caused by: java.lang.IllegalStateException: SQLiteDatabase created and never closed
작업샘플
public class DatabaseManager {
private int mOpenCounter;
private static DatabaseManager instance;
private static SQLiteOpenHelper mDatabaseHelper;
private SQLiteDatabase mDatabase;
public static synchronized void initializeInstance(SQLiteOpenHelper helper) {
if (instance == null) {
instance = new DatabaseManager();
mDatabaseHelper = helper;
}
}
public static synchronized DatabaseManager getInstance() {
if (instance == null) {
throw new IllegalStateException(DatabaseManager.class.getSimpleName() +
" is not initialized, call initializeInstance(..) method first.");
}
return instance;
}
public synchronized SQLiteDatabase openDatabase() {
mOpenCounter++;
if(mOpenCounter == 1) {
// Opening new database
mDatabase = mDatabaseHelper.getWritableDatabase();
}
return mDatabase;
}
public synchronized void closeDatabase() {
mOpenCounter--;
if(mOpenCounter == 0) {
// Closing database
mDatabase.close();
}
}
}
다음과 같이 사용합니다.
SQLiteDatabase database = DatabaseManager.getInstance().openDatabase();
database.insert(...);
// database.close(); Don't close it directly!
DatabaseManager.getInstance().closeDatabase(); // correct way
데이터베이스가 필요할 때마다 DatabaseManager 클래스의 openDatabase() 메서드를 호출해야 합니다.이 메소드 안에는 데이터베이스가 열리는 횟수를 나타내는 카운터가 있습니다.이 값이 1과 같으면 새 데이터베이스 연결을 만들어야 하고, 그렇지 않으면 데이터베이스 연결이 이미 작성되었음을 의미합니다.
closeDatabase() 메서드에서도 동일한 현상이 발생합니다.이 메서드를 호출할 때마다 카운터가 감소하고 0이 될 때마다 데이터베이스 연결을 종료합니다.
이제 데이터베이스를 사용하여 스레드가 안전한지 확인할 수 있습니다.
- 을 합니다.
Thread
또는AsyncTask
장기 실행 작업(50ms 이상)에 적합합니다.앱을 테스트하여 위치를 확인합니다.대부분의 작업에는 몇 개의 행만 포함되므로(아마도) 스레드가 필요하지 않습니다.대량 작업에는 스레드를 사용합니다. - 공유하기
SQLiteDatabase
스레드 사이의 디스크에 있는 각 DB에 대한 인스턴스(instance) 및 열린 연결을 추적하기 위한 카운팅 시스템(counting system)을 구현합니다.
이러한 시나리오에 대한 모범 사례가 있습니까?
모든 클래스 간에 정적 필드를 공유합니다.저는 그것과 다른 공유가 필요한 것들을 위해 싱글톤을 사용하곤 했습니다.또한 데이터베이스를 일찍 닫거나 열어 두지 않도록 카운트 체계(일반적으로 Atomic Integer 사용)를 사용해야 합니다.
내 솔루션:
제가 작성한 이전 버전은 https://github.com/Taeluf/dev/tree/main/archived/databasemanager 에서 사용할 수 있으며 유지보수가 되지 않습니다.제 솔루션을 이해하고 싶다면 코드를 보고 제 노트를 읽으세요.제 노트는 보통 꽤 도움이 됩니다.
- /제는로라는 이름의 새
DatabaseManager
github에서 다운로드) - 확장하다, 하다, 확대하다
DatabaseManager
을 구현합니다.onCreate
그리고.onUpgrade
당신이 평소에 그랬던 것처럼. 개 수 .DatabaseManager
클래스를 사용하여 디스크에 다른 데이터베이스를 사용할 수 있습니다. - 하고 하위클및호인화스스턴출스로 전화하세요.
getDb()
를 사용하기 위해SQLiteDatabase
학생들 - 러
close()
인스턴스화한 각 ▁for대
복사/붙여넣기할 코드:
import android.content.Context;
import android.database.sqlite.SQLiteDatabase;
import java.util.concurrent.ConcurrentHashMap;
/** Extend this class and use it as an SQLiteOpenHelper class
*
* DO NOT distribute, sell, or present this code as your own.
* for any distributing/selling, or whatever, see the info at the link below
*
* Distribution, attribution, legal stuff,
* See https://github.com/JakarCo/databasemanager
*
* If you ever need help with this code, contact me at support@androidsqlitelibrary.com (or support@jakar.co )
*
* Do not sell this. but use it as much as you want. There are no implied or express warranties with this code.
*
* This is a simple database manager class which makes threading/synchronization super easy.
*
* Extend this class and use it like an SQLiteOpenHelper, but use it as follows:
* Instantiate this class once in each thread that uses the database.
* Make sure to call {@link #close()} on every opened instance of this class
* If it is closed, then call {@link #open()} before using again.
*
* Call {@link #getDb()} to get an instance of the underlying SQLiteDatabse class (which is synchronized)
*
* I also implement this system (well, it's very similar) in my <a href="http://androidslitelibrary.com">Android SQLite Libray</a> at http://androidslitelibrary.com
*
*
*/
abstract public class DatabaseManager {
/**See SQLiteOpenHelper documentation
*/
abstract public void onCreate(SQLiteDatabase db);
/**See SQLiteOpenHelper documentation
*/
abstract public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion);
/**Optional.
* *
*/
public void onOpen(SQLiteDatabase db){}
/**Optional.
*
*/
public void onDowngrade(SQLiteDatabase db, int oldVersion, int newVersion) {}
/**Optional
*
*/
public void onConfigure(SQLiteDatabase db){}
/** The SQLiteOpenHelper class is not actually used by your application.
*
*/
static private class DBSQLiteOpenHelper extends SQLiteOpenHelper {
DatabaseManager databaseManager;
private AtomicInteger counter = new AtomicInteger(0);
public DBSQLiteOpenHelper(Context context, String name, int version, DatabaseManager databaseManager) {
super(context, name, null, version);
this.databaseManager = databaseManager;
}
public void addConnection(){
counter.incrementAndGet();
}
public void removeConnection(){
counter.decrementAndGet();
}
public int getCounter() {
return counter.get();
}
@Override
public void onCreate(SQLiteDatabase db) {
databaseManager.onCreate(db);
}
@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
databaseManager.onUpgrade(db, oldVersion, newVersion);
}
@Override
public void onOpen(SQLiteDatabase db) {
databaseManager.onOpen(db);
}
@Override
public void onDowngrade(SQLiteDatabase db, int oldVersion, int newVersion) {
databaseManager.onDowngrade(db, oldVersion, newVersion);
}
@Override
public void onConfigure(SQLiteDatabase db) {
databaseManager.onConfigure(db);
}
}
private static final ConcurrentHashMap<String,DBSQLiteOpenHelper> dbMap = new ConcurrentHashMap<String, DBSQLiteOpenHelper>();
private static final Object lockObject = new Object();
private DBSQLiteOpenHelper sqLiteOpenHelper;
private SQLiteDatabase db;
private Context context;
/** Instantiate a new DB Helper.
* <br> SQLiteOpenHelpers are statically cached so they (and their internally cached SQLiteDatabases) will be reused for concurrency
*
* @param context Any {@link android.content.Context} belonging to your package.
* @param name The database name. This may be anything you like. Adding a file extension is not required and any file extension you would like to use is fine.
* @param version the database version.
*/
public DatabaseManager(Context context, String name, int version) {
String dbPath = context.getApplicationContext().getDatabasePath(name).getAbsolutePath();
synchronized (lockObject) {
sqLiteOpenHelper = dbMap.get(dbPath);
if (sqLiteOpenHelper==null) {
sqLiteOpenHelper = new DBSQLiteOpenHelper(context, name, version, this);
dbMap.put(dbPath,sqLiteOpenHelper);
}
//SQLiteOpenHelper class caches the SQLiteDatabase, so this will be the same SQLiteDatabase object every time
db = sqLiteOpenHelper.getWritableDatabase();
}
this.context = context.getApplicationContext();
}
/**Get the writable SQLiteDatabase
*/
public SQLiteDatabase getDb(){
return db;
}
/** Check if the underlying SQLiteDatabase is open
*
* @return whether the DB is open or not
*/
public boolean isOpen(){
return (db!=null&&db.isOpen());
}
/** Lowers the DB counter by 1 for any {@link DatabaseManager}s referencing the same DB on disk
* <br />If the new counter is 0, then the database will be closed.
* <br /><br />This needs to be called before application exit.
* <br />If the counter is 0, then the underlying SQLiteDatabase is <b>null</b> until another DatabaseManager is instantiated or you call {@link #open()}
*
* @return true if the underlying {@link android.database.sqlite.SQLiteDatabase} is closed (counter is 0), and false otherwise (counter > 0)
*/
public boolean close(){
sqLiteOpenHelper.removeConnection();
if (sqLiteOpenHelper.getCounter()==0){
synchronized (lockObject){
if (db.inTransaction())db.endTransaction();
if (db.isOpen())db.close();
db = null;
}
return true;
}
return false;
}
/** Increments the internal db counter by one and opens the db if needed
*
*/
public void open(){
sqLiteOpenHelper.addConnection();
if (db==null||!db.isOpen()){
synchronized (lockObject){
db = sqLiteOpenHelper.getWritableDatabase();
}
}
}
}
데이터베이스는 멀티스레딩에 매우 유연합니다.제 앱은 여러 스레드에서 동시에 DB를 검색하는데 문제가 없습니다.경우에 따라 여러 프로세스가 동시에 DB에 연결되어 작동하기도 합니다.
비동기 작업 - 가능한 경우 동일한 연결을 사용하지만, 필요한 경우 다른 작업에서 DB에 액세스해도 됩니다.
몇 시간 동안 이것과 씨름한 결과, 저는 당신이 db 실행당 하나의 db 도우미 개체만 사용할 수 있다는 것을 발견했습니다.예를들면,
for(int x = 0; x < someMaxValue; x++)
{
db = new DBAdapter(this);
try
{
db.addRow
(
NamesStringArray[i].toString(),
StartTimeStringArray[i].toString(),
EndTimeStringArray[i].toString()
);
}
catch (Exception e)
{
Log.e("Add Error", e.toString());
e.printStackTrace();
}
db.close();
}
다음과 같은 경우:
db = new DBAdapter(this);
for(int x = 0; x < someMaxValue; x++)
{
try
{
// ask the database manager to add a row given the two strings
db.addRow
(
NamesStringArray[i].toString(),
StartTimeStringArray[i].toString(),
EndTimeStringArray[i].toString()
);
}
catch (Exception e)
{
Log.e("Add Error", e.toString());
e.printStackTrace();
}
}
db.close();
루프가 반복될 때마다 새 DBA 어댑터를 만드는 것이 도우미 클래스를 통해 문자열을 데이터베이스로 가져올 수 있는 유일한 방법이었습니다.
드미트로의 대답은 제 경우에 잘 맞습니다.기능이 동기화된 것으로 선언하는 것이 좋을 것 같습니다.적어도 나의 경우, 그것은 null 포인터 예외를 호출할 것입니다. 예를 들어 getWritableDatabase not returned in a thread and openDatabase called in aother thread.
public synchronized SQLiteDatabase openDatabase() {
if(mOpenCounter.incrementAndGet() == 1) {
// Opening new database
mDatabase = mDatabaseHelper.getWritableDatabase();
}
return mDatabase;
}
Google I/O 2017에서 발표된 새로운 아키텍처 접근 방식을 적용해 볼 수 있습니다.
또한 룸이라는 새로운 ORM 라이브러리도 포함되어 있습니다.
@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.
}
사용자 Dao.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);
}
앱 데이터베이스.자바
@Database(entities = {User.class}, version = 1)
public abstract class AppDatabase extends RoomDatabase {
public abstract UserDao userDao();
}
SQLiteDatabase API에 대한 제 이해는 다중 스레드 애플리케이션을 사용하는 경우 단일 데이터베이스를 가리키는 SQLiteDatabase 개체를 1개 이상 가질 수 없다는 것입니다.
개체는 확실히 만들 수 있지만 서로 다른 스레드/프로세스(예: JDBC Connection에서 사용하는 방법)가 서로 다른 SQLiteDatabase 개체를 사용하기 시작하면 삽입/업데이트가 실패합니다.
여기서 유일한 해결책은 SQLiteDatabase 개체 1개를 유지하는 것이며 startTransaction()이 두 개 이상의 스레드에서 사용될 때마다 Android는 서로 다른 스레드 간의 잠금을 관리하고 한 번에 한 스레드만 독점적인 업데이트 액세스를 허용합니다.
또한 데이터베이스에서 "읽기"를 수행하고 다른 스레드에서 동일한 SQLiteDatabase 개체를 사용할 수 있습니다(다른 스레드가 쓰는 동안). 즉, "읽기 스레드"가 동일한 SQLiteDatabase 개체를 사용하더라도 "쓰기 스레드"가 데이터를 커밋할 때까지 데이터베이스에서 데이터를 읽지 않습니다.
이는 JDBC에서 읽기 스레드와 쓰기 스레드 간에 연결 개체를 전달(동일하게 사용)하면 커밋되지 않은 데이터도 인쇄할 수 있는 방식과 다릅니다.
엔터프라이즈 응용 프로그램에서 저는 조건부 검사를 사용하여 UI 스레드가 기다릴 필요가 없도록 하고 BG 스레드는 SQLiteDatabase 개체(전용)를 보유합니다.UI Actions를 예측하고 BG 스레드가 'x'초 동안 실행되지 않도록 지연하려고 합니다.또한 우선순위를 유지할 수 있습니다.UI 스레드가 먼저 가져올 수 있도록 SQLite 데이터베이스 연결 개체를 전달하는 것을 관리하기 위한 대기열입니다.
몇 가지 문제가 있었기 때문에, 제가 왜 잘못되고 있는지 이해했다고 생각합니다.
나는 데이터베이스 래퍼 클래스를 작성했는데, 이 클래스에는 다음이 포함되어 있습니다.close()
그것은 도우미를 거울처럼 가까이 불렀다.open()
라는 이름을 가진 이 는 getWriteableDatabase로 .ContentProvider
ContentProvider
를 사용하지 .SQLiteDatabase.close()
코드가 사용하기 때문에 큰 단서라고 생각합니다.getWriteableDatabase
어떤 경우에는 직접 액세스(화면 유효성 검사 쿼리를 메인에서 수행하여 getWriteableDatabase/rawQuery 모델로 마이그레이션하기도 했습니다.
저는 싱글톤을 사용하는데, 가까운 문서에 약간 불길한 의견이 있습니다.
열려 있는 데이터베이스 개체 닫기
(나의 대담함).
그래서 저는 데이터베이스에 액세스하기 위해 백그라운드 스레드를 사용하고 포그라운드와 동시에 실행되는 간헐적인 충돌을 겪었습니다.
그래서 제 생각엔close()
참조를 보유하고 있는 다른 스레드에 관계없이 데이터베이스를 강제로 닫습니다.close()
그 자체가 단순히 매칭을 취소하는 것이 아닙니다.getWriteableDatabase
열려 있는 모든 요청을 강제로 닫습니다.대부분의 경우 코드가 단일 스레드이기 때문에 문제가 되지 않지만 멀티 스레드의 경우 항상 동기화되지 않고 열리고 닫힐 수 있습니다.
SqLite 데이터베이스를 설명하는 주석을 다른 곳에서 읽음도우미 코드 인스턴스가 카운트되면 종료할 때는 백업 복사본을 수행하고 모든 연결을 강제로 닫고 SqLite가 어슬렁거릴 수 있는 캐시된 모든 항목을 강제로 쓸 때뿐입니다. 즉, 모든 애플리케이션 데이터베이스 작업을 중지합니다.도우미가 트랙을 분실한 경우 닫기, 파일 수준 작업(백업/복원)을 수행한 후 처음부터 다시 시작합니다.
통제된 방식으로 종료를 시도하는 것이 좋은 방법처럼 들리지만, Android는 VM을 폐기할 수 있는 권한을 보유하고 있기 때문에 종료 시 캐시된 업데이트가 기록되지 않을 위험을 줄일 수 있지만 디바이스에 스트레스가 있을 경우에는 보장할 수 없습니다.커서와 데이터베이스에 대한 참조를 올바르게 해제한 경우(정적 구성원이 되어서는 안 됨) 도우미가 데이터베이스를 닫았습니다.
제 생각에 접근 방식은 다음과 같습니다.
싱글톤 래퍼에서 열려면 getWriteableDatabase를 사용합니다. (콘텍스트의 필요성을 해결하기 위해 정적에서 응용프로그램 컨텍스트를 제공하기 위해 파생된 응용프로그램 클래스를 사용했습니다.)
가까이에 직접 전화하지 마십시오.
명확한 범위가 없는 개체에는 결과 데이터베이스를 저장하지 말고 참조 카운트를 사용하여 암묵적인 닫기()를 트리거하십시오.
파일 수준 처리를 수행하는 경우 모든 데이터베이스 작업을 중지한 다음 적절한 트랜잭션을 작성하는 경우에 대비하여 적절한 트랜잭션을 작성하는 경우에 대비하여 종료를 호출합니다. 그러면 실행 중지된 스레드가 실패하고 닫힌 데이터베이스는 부분 트랜잭션의 파일 수준 복사본이 아닌 최소한 적절한 트랜잭션을 가지게 됩니다.
응답이 늦은 것은 알지만 안드로이드에서 sqlite 쿼리를 실행하는 가장 좋은 방법은 맞춤형 콘텐츠 공급자를 통해서입니다.이러한 방식으로 UI는 데이터베이스 클래스(SQLiteOpenHelper 클래스를 확장하는 클래스)와 분리됩니다.또한 쿼리는 백그라운드 스레드(커서 로더)에서 실행됩니다.
언급URL : https://stackoverflow.com/questions/2493331/how-can-i-avoid-concurrency-problems-when-using-sqlite-on-android
'programing' 카테고리의 다른 글
C Make로 C 수학 라이브러리에 링크하는 방법은 무엇입니까? (0) | 2023.06.11 |
---|---|
Excel 매크로 - 데이터와 형식을 표로 사용하는 모든 셀 선택 (0) | 2023.06.11 |
Visual Studio ASP.Net MVC 실행 취소를 시작 페이지 작업으로 설정 (0) | 2023.06.11 |
"mro()"는 무엇을 합니까? (0) | 2023.06.11 |
목록에 대한 모델 유효성 검사 보기 (0) | 2023.06.11 |