Android SQLite DB เมื่อใดจึงจะปิด


98

ฉันกำลังทำงานกับฐานข้อมูล SQLite บน Android ตัวจัดการฐานข้อมูลของฉันคือซิงเกิลตันและตอนนี้จะเปิดการเชื่อมต่อกับฐานข้อมูลเมื่อเริ่มต้น ปลอดภัยที่จะเปิดฐานข้อมูลทิ้งไว้ตลอดเวลาเพื่อที่ว่าเมื่อมีคนเรียกชั้นเรียนของฉันให้ทำงานกับฐานข้อมูลจะเปิดอยู่แล้ว? หรือฉันควรเปิดและปิดฐานข้อมูลก่อนและหลังการเข้าถึงแต่ละครั้ง การเปิดทิ้งไว้ตลอดเวลามีอันตรายหรือไม่?

ขอบคุณ!

คำตอบ:


60

ฉันจะให้มันเปิดตลอดเวลาและปิดในวิธีวงจรบางอย่างเช่นหรือonStop onDestroyด้วยวิธีนี้คุณสามารถตรวจสอบได้อย่างง่ายดายว่ามีการใช้งานฐานข้อมูลอยู่แล้วisDbLockedByCurrentThreadหรือไม่โดยการโทรหรือisDbLockedByOtherThreadsบนSQLiteDatabaseวัตถุชิ้นเดียวทุกครั้งก่อนที่คุณจะใช้งาน วิธีนี้จะป้องกันการปรับแต่งหลายอย่างในฐานข้อมูลและบันทึกแอปพลิเคชันของคุณจากเหตุขัดข้องที่อาจเกิดขึ้น

ดังนั้นในซิงเกิลตันของคุณคุณอาจมีวิธีการเช่นนี้เพื่อรับSQLiteOpenHelperวัตถุชิ้นเดียวของคุณ:

private SQLiteDatabase db;
private MyDBOpenHelper mySingletonHelperField;
public MyDBOpenHelper getDbHelper() {
    db = mySingletonHelperField.getDatabase();//returns the already created database object in my MyDBOpenHelper class(which extends `SQLiteOpenHelper`)
    while(db.isDbLockedByCurrentThread() || db.isDbLockedByOtherThreads()) {
        //db is locked, keep looping
    }
    return mySingletonHelperField;
}

ดังนั้นเมื่อใดก็ตามที่คุณต้องการใช้ออบเจ็กต์ตัวช่วยเปิดของคุณให้เรียกใช้เมธอด getter (ตรวจสอบให้แน่ใจว่าเป็นเธรด)

วิธีอื่นในซิงเกิลตันของคุณอาจเป็น (เรียกทุกครั้งก่อนที่คุณจะพยายามโทรหา getter ด้านบน):

public void setDbHelper(MyDBOpenHelper mySingletonHelperField) {
    if(null == this.mySingletonHelperField) {
        this.mySingletonHelperField = mySingletonHelperField;
        this.mySingletonHelperField.setDb(this.mySingletonHelperField.getWritableDatabase());//creates and sets the database object in the MyDBOpenHelper class
    }
}

คุณอาจต้องการปิดฐานข้อมูลในซิงเกิลตันด้วย:

public void finalize() throws Throwable {
    if(null != mySingletonHelperField)
        mySingletonHelperField.close();
    if(null != db)
        db.close();
    super.finalize();
}

หากผู้ใช้แอปพลิเคชันของคุณมีความสามารถในการสร้างการโต้ตอบกับฐานข้อมูลจำนวนมากได้อย่างรวดเร็วคุณควรใช้สิ่งที่ฉันได้แสดงไว้ข้างต้น แต่ถ้ามีการโต้ตอบกับฐานข้อมูลเพียงเล็กน้อยฉันก็ไม่ต้องกังวลเรื่องนี้และเพียงแค่สร้างและปิดฐานข้อมูลทุกครั้ง


ฉันสามารถเพิ่มความเร็วในการเข้าถึงฐานข้อมูลด้วยปัจจัย 2 ด้วยวิธีนี้ (เปิดฐานข้อมูลไว้) ขอบคุณ
teh.fonsi

2
การปั่นเป็นเทคนิคที่แย่มาก ดูคำตอบของฉันด้านล่าง
mixel

1
@mixel จุดดี. ฉันเชื่อว่าฉันโพสต์คำตอบนี้ก่อนที่ API 16 จะพร้อมใช้งาน แต่ฉันคิดผิด
เจมส์

1
@binnyb ฉันคิดว่าการอัปเดตคำตอบของคุณจะดีกว่าเพื่อไม่ให้คนเข้าใจผิด
mixel

3
WARN isDbLockedByOtherThreads () เป็น Depricated และส่งคืนเท็จตั้งแต่ API 16 การตรวจสอบ isDbLockedByCurrentThread () จะให้การวนซ้ำแบบไม่สิ้นสุดเนื่องจากหยุดเธรดปัจจุบันและไม่มีสิ่งใดสามารถ 'ปลดล็อก DB' เพื่อให้เมธอดนี้คืนค่าจริง
matreshkin

20

ณ ตอนนี้ไม่จำเป็นต้องตรวจสอบว่าฐานข้อมูลถูกล็อคโดยเธรดอื่นหรือไม่ ในขณะที่คุณใช้ Singleton SQLiteOpenHelper ในทุกเธรดคุณจะปลอดภัย จากisDbLockedByCurrentThreadเอกสารประกอบ:

ชื่อของเมธอดนี้มาจากช่วงเวลาที่มีการเชื่อมต่อกับฐานข้อมูลที่ใช้งานอยู่หมายความว่าเธรดนั้นมีการล็อกจริงบนฐานข้อมูล ปัจจุบันไม่มี "การล็อกฐานข้อมูล" ที่แท้จริงอีกต่อไปแม้ว่าเธรดอาจปิดกั้นหากไม่สามารถรับการเชื่อมต่อฐานข้อมูลเพื่อดำเนินการเฉพาะ

isDbLockedByOtherThreads เลิกใช้งานแล้วตั้งแต่ API ระดับ 16


ไม่มีประเด็นในการใช้หนึ่งอินสแตนซ์ต่อเธรด SQLiteOpenHelper ปลอดภัยต่อเธรด นี่เป็นหน่วยความจำที่ไม่มีประสิทธิภาพมากเช่นกัน แต่แอปควรเก็บ SQLiteOpenHelper ไว้เพียงอินสแตนซ์เดียวต่อฐานข้อมูล เพื่อการทำงานพร้อมกันที่ดีขึ้นขอแนะนำให้ใช้ WriteAheadLogging ซึ่งมีการรวมการเชื่อมต่อสำหรับการเชื่อมต่อสูงสุด 4 ครั้ง
ejboy

@ejboy ฉันหมายความอย่างนั้น "ใช้ Singleton (= หนึ่ง) SQLiteOpenHelper ในทุกเธรด" ไม่ใช่ "ต่อเธรด"
mixel

15

เกี่ยวกับคำถาม:

ตัวจัดการฐานข้อมูลของฉันคือซิงเกิลตันและตอนนี้จะเปิดการเชื่อมต่อกับฐานข้อมูลเมื่อเริ่มต้น

เราควรแบ่ง 'การเปิดฐานข้อมูล', 'การเปิดการเชื่อมต่อ' SQLiteOpenHelper.getWritableDatabase () ให้ DB ที่เปิดอยู่ แต่เราไม่ต้องควบคุมการเชื่อมต่อเหมือนที่ทำภายใน

ปลอดภัยที่จะเปิดฐานข้อมูลทิ้งไว้ตลอดเวลาเพื่อที่ว่าเมื่อมีคนเรียกชั้นเรียนของฉันให้ทำงานกับฐานข้อมูลจะเปิดอยู่แล้ว?

ใช่แล้ว. การเชื่อมต่อไม่แฮงค์หากธุรกรรมถูกปิดอย่างถูกต้อง โปรดทราบว่าฐานข้อมูลของคุณจะถูกปิดโดยอัตโนมัติหาก GC ดำเนินการเสร็จสิ้น

หรือฉันควรเปิดและปิดฐานข้อมูลก่อนและหลังการเข้าถึงแต่ละครั้ง

การปิดอินสแตนซ์ SQLiteDatabase ไม่ให้อะไรมากมายนอกจากการปิดการเชื่อมต่อ แต่นี่เป็นผลเสียของนักพัฒนาหากมีการเชื่อมต่อบางอย่างในขณะนี้ นอกจากนี้หลังจาก SQLiteDatabase.close () แล้ว SQLiteOpenHelper.getWritableDatabase () จะส่งคืนอินสแตนซ์ใหม่

การเปิดทิ้งไว้ตลอดเวลามีอันตรายหรือไม่?

ไม่มีไม่มี โปรดทราบด้วยว่าการปิดฐานข้อมูลในช่วงเวลาและเธรดที่ไม่เกี่ยวข้องเช่นใน Activity.onStop () อาจปิดการเชื่อมต่อที่ใช้งานอยู่และปล่อยให้ข้อมูลอยู่ในสถานะไม่สอดคล้องกัน


ขอบคุณหลังจากอ่านคำของคุณ "หลังจาก SQLiteDatabase.close () แล้ว SQLiteOpenHelper.getWritableDatabase () จะส่งคืนอินสแตนซ์ใหม่" ฉันตระหนักว่าในที่สุดฉันก็ได้คำตอบสำหรับปัญหาเก่าของแอปพลิเคชันของฉัน สำหรับปัญหาซึ่งกลายเป็นเรื่องสำคัญหลังจากการย้ายไปใช้ Android 9 (ดูstackoverflow.com/a/54224922/297710 )
yvolk

1

Android 8.1 มีSQLiteOpenHelper.setIdleConnectionTimeout(long)วิธีการที่:

ตั้งค่าจำนวนมิลลิวินาทีสูงสุดที่อนุญาตให้การเชื่อมต่อ SQLite ไม่ได้ใช้งานก่อนที่จะปิดและลบออกจากพูล

https://developer.android.com/reference/android/database/sqlite/SQLiteOpenHelper.html#setIdleConnectionTimeout (ยาว)


4
ทุกวันนี้มันเลิกใช้แล้ว
matreshkin

1

จากมุมมองด้านประสิทธิภาพวิธีที่ดีที่สุดคือเก็บ SQLiteOpenHelper อินสแตนซ์เดียวไว้ในระดับแอปพลิเคชัน การเปิดฐานข้อมูลอาจมีราคาแพงและเป็นการดำเนินการบล็อกดังนั้นจึงไม่ควรทำในเธรดหลักและ / หรือในวิธีวงจรชีวิตของกิจกรรม

setIdleConnectionTimeout ()เมธอด (แนะนำใน Android 8.1) สามารถใช้เพื่อเพิ่ม RAM เมื่อไม่ได้ใช้ฐานข้อมูล หากตั้งค่าการหมดเวลาว่างการเชื่อมต่อฐานข้อมูลจะถูกปิดหลังจากไม่มีการใช้งานเป็นระยะเวลาหนึ่งกล่าวคือเมื่อไม่มีการเข้าถึงฐานข้อมูล การเชื่อมต่อจะถูกเปิดขึ้นมาใหม่ในแอปอย่างโปร่งใสเมื่อมีการดำเนินการสืบค้นใหม่

นอกจากนั้นแอปยังสามารถเรียกreleaseMemory ()เมื่อเข้าสู่พื้นหลังหรือตรวจจับความดันหน่วยความจำเช่นในonTrimMemory ()



-9

สร้างบริบทแอปพลิเคชันของคุณเองจากนั้นเปิดและปิดฐานข้อมูลจากที่นั่น วัตถุนั้นยังมีวิธี OnTerminate () ที่คุณสามารถใช้เพื่อปิดการเชื่อมต่อ ฉันยังไม่ได้ลอง แต่ดูเหมือนว่าจะเป็นแนวทางที่ดีกว่า

@binnyb: ฉันไม่ชอบใช้ finalize () เพื่อปิดการเชื่อมต่อ อาจใช้งานได้ แต่จากสิ่งที่ฉันเข้าใจการเขียนโค้ดในวิธี Java finalize () เป็นความคิดที่ไม่ดี


คุณไม่ต้องการพึ่งพาการสรุปผลเพราะคุณไม่มีทางรู้ว่าจะถูกเรียกเมื่อใด วัตถุอาจอยู่ในบริเวณขอบรกจนกว่า GC จะตัดสินใจทำความสะอาดและจากนั้นจะเรียกใช้ () ให้เสร็จสิ้น นั่นเป็นเหตุผลที่ไม่มีประโยชน์ วิธีที่ถูกต้องในการทำคือการมีเมธอดที่ถูกเรียกใช้เมื่อวัตถุไม่ได้ใช้งานอีกต่อไป (ไม่ว่าจะถูกรวบรวมหรือไม่ก็ตาม) และนั่นคือสิ่งที่ onTerminate () คือ
erwan

6
เอกสารบอกชัดเจนว่าonTerminateจะไม่ถูกเรียกในสภาพแวดล้อมการใช้งานจริง - developer.android.com/reference/android/app/…
Eliezer
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.