your programing

Android에서 SQLite의 모범 사례는 무엇입니까?

lovepro 2020. 9. 30. 11:12
반응형

Android에서 SQLite의 모범 사례는 무엇입니까?


Android 앱 내에서 SQLite 데이터베이스에 대한 쿼리를 실행할 때 모범 사례로 간주되는 것은 무엇입니까?

AsyncTask의 doInBackground에서 삽입, 삭제 및 선택 쿼리를 실행하는 것이 안전합니까? 아니면 UI 스레드를 사용해야합니까? 데이터베이스 쿼리가 "무거울 수 있고"앱을 잠글 수 있으므로 UI ​​스레드를 사용해서는 안된다고 가정합니다. 결과적으로 ANR ( 응용 프로그램 없음 )이 발생합니다.

AsyncTask가 여러 개인 경우 연결을 공유해야합니까, 아니면 각각 연결을 열어야합니까?

이러한 시나리오에 대한 모범 사례가 있습니까?


삽입, 업데이트, 삭제 및 읽기는 일반적으로 여러 스레드에서 괜찮지 만 Brad의 대답 은 올바르지 않습니다. 연결을 만들고 사용하는 방법에주의해야합니다. 데이터베이스가 손상되지 않은 경우에도 업데이트 호출이 실패하는 상황이 있습니다.

기본적인 대답입니다.

SqliteOpenHelper 개체는 하나의 데이터베이스 연결을 유지합니다. 읽기 및 쓰기 연결을 제공하는 것처럼 보이지만 실제로는 그렇지 않습니다. 읽기 전용을 호출하면 상관없이 쓰기 데이터베이스 연결을 얻을 수 있습니다.

따라서 하나의 도우미 인스턴스, 하나의 db 연결. 여러 스레드에서 사용하더라도 한 번에 하나씩 연결합니다. SqliteDatabase 개체는 Java 잠금을 사용하여 액세스를 직렬화합니다. 따라서 100 개의 스레드에 하나의 db 인스턴스가있는 경우 실제 온 디스크 데이터베이스에 대한 호출이 직렬화됩니다.

따라서 하나의 도우미, 하나의 db 연결은 Java 코드로 직렬화됩니다. 하나의 스레드, 1000 개의 스레드 사이에 공유 된 하나의 도우미 인스턴스를 사용하면 모든 db 액세스 코드가 직렬입니다. 그리고 인생은 좋다.

동시에 실제 고유 연결에서 데이터베이스에 쓰려고하면 실패합니다. 첫 번째 작업이 완료 될 때까지 기다리지 않고 기록합니다. 단순히 변경 사항을 기록하지 않습니다. 더 나쁜 것은 SQLiteDatabase에서 올바른 버전의 삽입 / 업데이트를 호출하지 않으면 예외가 발생하지 않습니다. LogCat에 메시지가 표시됩니다.

그래서, 여러 스레드? 하나의 도우미를 사용하십시오. 기간. 하나의 스레드 만 쓸 것이라는 것을 알고 있다면 여러 연결을 사용할 수 있으며 읽기 속도가 빨라지지만 구매자는주의해야합니다. 나는 그렇게 많이 테스트하지 않았습니다.

여기에 훨씬 더 자세한 내용과 예제 앱이있는 블로그 게시물이 있습니다.

Gray와 나는 실제로 그의 Ormlite를 기반으로하는 ORM 도구를 마무리하고 있는데, 이는 Android 데이터베이스 구현에서 기본적으로 작동하며 블로그 게시물에서 설명하는 안전한 생성 / 호출 구조를 따릅니다. 그것은 곧 나올 것입니다. 구경하다.


그 동안 후속 블로그 게시물이 있습니다.

또한 이전에 언급 한 잠금 예제의 2point0 으로 포크를 확인하십시오 .


동시 데이터베이스 액세스

내 블로그에 같은 기사 (포맷을 더 좋아함)

Android 데이터베이스 스레드에 안전하게 액세스하는 방법을 설명하는 작은 기사를 작성했습니다.


자신의 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();

logcat에 다음 메시지가 표시되고 변경 사항 중 하나가 기록되지 않습니다.

android.database.sqlite.SQLiteDatabaseLockedException: database is locked (code 5)

이것은 새로운 SQLiteOpenHelper 객체를 만들 때마다 실제로 새로운 데이터베이스 연결 을 만들고 있기 때문에 발생 합니다. 동시에 실제 고유 연결에서 데이터베이스에 쓰려고하면 실패합니다. (위 답변에서)

여러 스레드가있는 데이터베이스를 사용하려면 하나의 데이터베이스 연결을 사용하고 있는지 확인해야합니다.

단일 SQLiteOpenHelper 객체를 보유하고 반환하는 단일 클래스 데이터베이스 관리자만들어 보겠습니다 .

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

하나의 데이터베이스 연결 만 사용하기 때문에 getDatabase () 메서드 Thread1Thread2에 대해 동일한 SQLiteDatabase 객체 인스턴스를 반환 합니다 . 무슨 일이 일어나고 있는지, Thread1 은 데이터베이스를 닫을 수 있지만 Thread2 는 여전히 그것을 사용하고 있습니다. 그래서 IllegalStateException 충돌이 발생합니다.

아무도 데이터베이스를 사용하고 있지 않은지 확인한 다음 닫아야합니다. stackoveflow의 일부 사람들은 SQLiteDatabase 를 절대 닫지 않는 것이 좋습니다 . 그러면 다음과 같은 logcat 메시지가 나타납니다.

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에 대해 하나의 인스턴스를 공유 하고 계수 시스템을 구현하여 열린 연결을 추적합니다.

이러한 시나리오에 대한 모범 사례가 있습니까?

모든 클래스간에 정적 필드를 공유합니다. 나는 그와 공유해야 할 다른 것들을 위해 싱글 톤을 유지하곤했습니다. 계산 체계 (일반적으로 AtomicInteger 사용)를 사용하여 데이터베이스를 일찍 닫거나 열어 두지 않도록해야합니다.

내 솔루션 :

최신 버전은 https://github.com/JakarCo/databasemanager를 참조 하세요. 하지만 여기에서도 코드를 최신 상태로 유지하려고합니다. 내 솔루션을 이해하려면 코드를보고 내 메모를 읽으십시오. 내 노트는 일반적으로 매우 유용합니다.

  1. 코드를 .txt라는 새 파일에 복사 / 붙여 넣기합니다 DatabaseManager. (또는 github에서 다운로드)
  2. 확장 DatabaseManager하고 구현 onCreate하고 onUpgrade평소처럼. DatabaseManager디스크에 서로 다른 데이터베이스를 갖기 위해 클래스 의 여러 하위 클래스를 만들 수 있습니다 .
  3. 하위 클래스를 인스턴스화 getDb()하고 SQLiteDatabase클래스 를 사용하도록 호출하십시오 .
  4. close()인스턴스화 한 각 하위 클래스를 호출 합니다.

복사 / 붙여 넣기 할 코드 :

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에 액세스 할 수 있습니다.


Dmytro의 대답은 내 경우에 잘 작동합니다. 함수를 동기화 된 것으로 선언하는 것이 더 낫다고 생각합니다. 적어도 내 경우에는 null 포인터 예외를 호출합니다. 예를 들어 getWritableDatabase가 아직 한 스레드에서 반환되지 않았고 그 동안 다른 스레드에서 openDatabse가 호출되었습니다.

public synchronized SQLiteDatabase openDatabase() {
        if(mOpenCounter.incrementAndGet() == 1) {
            // Opening new database
            mDatabase = mDatabaseHelper.getWritableDatabase();
        }
        return mDatabase;
    }

몇 시간 동안 이것으로 고생 한 후 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();

루프가 반복 될 때마다 새 DBAdapter를 만드는 것이 도우미 클래스를 통해 데이터베이스로 문자열을 가져올 수있는 유일한 방법이었습니다.


SQLiteDatabase API에 대한 나의 이해는 다중 스레드 응용 프로그램이있는 경우 단일 데이터베이스를 가리키는 하나 이상의 SQLiteDatabase 개체를 가질 수 없다는 것입니다.

객체는 확실히 생성 될 수 있지만 다른 스레드 / 프로세스가 다른 SQLiteDatabase 객체를 사용하기 시작하면 삽입 / 업데이트가 실패합니다 (예 : JDBC 연결에서 사용하는 방법).

여기에서 유일한 해결책은 1 개의 SQLiteDatabase 객체를 사용하는 것이며 startTransaction ()이 둘 이상의 스레드에서 사용될 때마다 Android는 서로 다른 스레드에 대한 잠금을 관리하고 한 번에 하나의 스레드 만 독점 업데이트 액세스 권한을 갖도록 허용합니다.

또한 데이터베이스에서 "읽기"를 수행하고 다른 스레드에서 동일한 SQLiteDatabase 객체를 사용할 수 있으며 (다른 스레드가 쓰는 동안) 데이터베이스 손상이 발생하지 않습니다. 즉 "읽기 스레드"가 ""까지 데이터베이스에서 데이터를 읽지 않습니다. 쓰기 스레드 "는 모두 동일한 SQLiteDatabase 개체를 사용하지만 데이터를 커밋합니다.

이것은 JDBC에서 연결 개체가있는 방식과 다릅니다. 여기서 읽기와 쓰기 스레드간에 연결 개체를 전달 (동일 사용)하면 커밋되지 않은 데이터도 인쇄 할 수 있습니다.

엔터프라이즈 응용 프로그램에서 UI 스레드가 기다릴 필요가 없도록 조건부 검사를 사용하고 BG 스레드는 SQLiteDatabase 개체를 독점적으로 보유합니다. UI 작업을 예측하고 BG 스레드가 'x'초 동안 실행되지 않도록 연기합니다. 또한 PriorityQueue를 유지 관리하여 UI 스레드가 먼저 가져 오도록 SQLiteDatabase Connection 개체를 전달하는 것을 관리 할 수 ​​있습니다.


Google I / O 2017에서 발표 된 새로운 아키텍처 접근 방식을 적용 해 볼 수 있습니다 .

또한 Room 이라는 새로운 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.
}

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();
}

몇 가지 문제가 있었기 때문에 내가 왜 잘못되었는지 이해했다고 생각합니다.

나는 getWriteableDatabase를 호출 close()하는 미러로 가까운 헬퍼를 호출 한 을 포함하는 데이터베이스 래퍼 클래스를 작성한 open()다음 ContentProvider. 에 대한 모델 은 코드가 사용하기 때문에 큰 단서라고 생각하는 ContentProvider사용하지 않습니다 . 어떤 경우에는 여전히 직접 액세스를 수행 중이었습니다 (메인에서 화면 유효성 검사 쿼리를 수행하여 getWriteableDatabase / rawQuery 모델로 마이그레이션했습니다.SQLiteDatabase.close()getWriteableDatabase

나는 싱글 톤을 사용하는데 가까운 문서에 약간 불길한 코멘트가 있습니다.

열려있는 데이터베이스 개체를 모두 닫습니다.

(내 대담).

그래서 백그라운드 스레드를 사용하여 데이터베이스에 액세스하고 포 그라운드와 동시에 실행되는 간헐적 인 충돌이 발생했습니다.

따라서 close()참조를 보유하는 다른 스레드에 관계없이 데이터베이스를 강제로 닫아야 한다고 생각 합니다. 따라서 close()그 자체는 단순히 일치를 취소하는 getWriteableDatabase것이 아니라 열려있는 요청을 강제로 닫는 입니다. 대부분의 경우 코드가 단일 스레딩이므로 문제가되지 않지만 다중 스레드의 경우 항상 동기화되지 않은 상태로 열고 닫을 가능성이 있습니다.

SqLiteDatabaseHelper 코드 인스턴스가 중요하다는 설명을 다른 곳에서 읽은 다음 닫기를 원하는 유일한 시간은 백업 복사를 수행하고 모든 연결을 강제로 닫고 SqLite를 강제로 종료하려는 경우입니다. 배회 할 수있는 캐시 된 항목을 모두 쓰십시오. 즉, 모든 애플리케이션 데이터베이스 활동을 중지하고, Helper가 추적을 잃은 경우를 대비하여 닫고, 파일 수준 활동 (백업 / 복원)을 수행 한 다음 다시 시작하십시오.

통제 된 방식으로 시도하고 종료하는 것이 좋은 생각처럼 들리지만 현실은 Android가 VM을 폐기 할 수있는 권한을 보유하므로 종료시 캐시 된 업데이트가 작성되지 않을 위험이 줄어들지 만 기기가 스트레스를 받고 데이터베이스에 대한 커서와 참조를 올바르게 해제했다면 (정적 멤버가 아니어야 함) 도우미는 어쨌든 데이터베이스를 닫을 것입니다.

그래서 내 생각은 접근 방식은 다음과 같습니다.

getWriteableDatabase를 사용하여 싱글 톤 래퍼에서 엽니 다. (컨텍스트의 필요성을 해결하기 위해 정적에서 애플리케이션 컨텍스트를 제공하기 위해 파생 된 애플리케이션 클래스를 사용했습니다.)

가까이에 직접 전화하지 마십시오.

명백한 범위가없고 암시 적 close ()를 트리거하기 위해 참조 횟수에 의존하는 객체에 결과 데이터베이스를 저장하지 마십시오.

파일 수준 처리를 수행하는 경우 모든 데이터베이스 활동을 중지 한 다음 적절한 트랜잭션을 작성한다는 가정하에 런 어웨이 스레드가있는 경우 close를 호출하여 런 어웨이 스레드가 실패하고 닫힌 데이터베이스가 적어도 적절한 트랜잭션을 갖도록합니다. 잠재적으로 부분 트랜잭션의 파일 레벨 사본보다.


응답이 늦다는 것을 알고 있지만 Android에서 sqlite 쿼리를 실행하는 가장 좋은 방법은 사용자 지정 콘텐츠 공급자를 사용하는 것입니다. 이러한 방식으로 UI는 데이터베이스 클래스 (SQLiteOpenHelper 클래스를 확장하는 클래스)와 분리됩니다. 또한 쿼리는 백그라운드 스레드 (Cursor Loader)에서 실행됩니다.

참고 URL : https://stackoverflow.com/questions/2493331/what-are-the-best-practices-for-sqlite-on-android

반응형