Android P-'SQLite: ऐसी कोई तालिका त्रुटि' परिसंपत्तियों से डेटाबेस की प्रतिलिपि बनाने के बाद




android-9.0-pie (8)

Android P के साथ मेरे मुद्दों को नीचे दिए गए के रूप में createDataBase () विधि में इस .getReadableDatabase () के बाद 'this.close ()' जोड़कर हल किया गया।

private void createDataBase() throws IOException {
    this.getReadableDatabase();
    this.close(); 
    try {           
        copyDataBase();            
    } catch (IOException e) {           
        throw new RuntimeException(e);
    }
}

मेरे पास मेरे ऐप्स एसेट फ़ोल्डर में सहेजा गया डेटाबेस है और जब ऐप पहले खुलता है तो मैं नीचे दिए गए कोड का उपयोग करके डेटाबेस की प्रतिलिपि बनाता हूं।

inputStream = mContext.getAssets().open(Utils.getDatabaseName());

        if(inputStream != null) {

            int mFileLength = inputStream.available();

            String filePath = mContext.getDatabasePath(Utils.getDatabaseName()).getAbsolutePath();

            // Save the downloaded file
            output = new FileOutputStream(filePath);

            byte data[] = new byte[1024];
            long total = 0;
            int count;
            while ((count = inputStream.read(data)) != -1) {
                total += count;
                if(mFileLength != -1) {
                    // Publish the progress
                    publishProgress((int) (total * 100 / mFileLength));
                }
                output.write(data, 0, count);
            }
            return true;
        }

उपरोक्त कोड समस्या के बिना चलता है, लेकिन जब आप डेटाबेस को क्वेरी करने का प्रयास करते हैं तो आपको SQLite मिलता है: ऐसी कोई तालिका अपवाद नहीं।

यह समस्या केवल Android P में होती है, Android के सभी पुराने संस्करण सही तरीके से काम करते हैं।

क्या यह Android P के साथ एक ज्ञात समस्या है या कुछ बदल गया है?


Android PIE और उसके बाद के डेटाबेस फ़ाइल पथ के लिए निम्न पंक्ति का उपयोग करने का सबसे सरल उत्तर:

DB_NAME="xyz.db";
DB_Path = "/data/data/" + BuildConfig.APPLICATION_ID + "/databases/"+DB_NAME;

एक समान समस्या थी, और इसे मेरे SQLiteOpenHelper में जोड़कर हल किया

    @Override
    public void onOpen(SQLiteDatabase db) {
        super.onOpen(db);
        db.disableWriteAheadLogging();
    }

जाहिर तौर पर Android P, PRAGMA Log चीज को अलग सेट करता है। फिर भी कोई विचार नहीं है अगर साइड इफेक्ट होगा, लेकिन लगता है काम कर रहा है!


ऐसा लगता है कि आप आउटपुट स्ट्रीम को बंद नहीं करते हैं। हालांकि यह शायद यह नहीं समझाता है कि डीबी वास्तव में क्यों नहीं बनाया गया है (जब तक कि एंड्रॉइड पी ने एक बहु एमबी बफर नहीं जोड़ा) यह एक कोशिश के साथ-संसाधन का उपयोग करने के लिए एक अच्छा अभ्यास है, जैसे कुछ:

// garantees that the data are flushed and the resources freed
try (FileOutputStream output = new FileOutputStream(filePath)) {
    byte data[] = new byte[1024];
    long total = 0;
    int count;
    while ((count = inputStream.read(data)) != -1) {
        total += count;
        if (mFileLength != -1) {
            // Publish the progress
            publishProgress((int) (total * 100 / mFileLength));
        }
        output.write(data, 0, count);
    }

    // maybe a bit overkill
    output.getFD().sync();
}

मैं इसी तरह के मुद्दे पर भाग गया। मैं एक डेटाबेस की नकल कर रहा था लेकिन एक परिसंपत्ति से नहीं। मैंने पाया कि समस्या का मेरे डेटाबेस फाइल कॉपी कोड से कोई लेना-देना नहीं था। न ही इसे खुली फाइलों के साथ करना पड़ता था, न कि बंद, फ्लशिंग या सिंकिंग के साथ। मेरा कोड आमतौर पर एक मौजूदा अनोपेन डेटाबेस को अधिलेखित करता है। एंड्रॉइड पाई के साथ नया / अलग-अलग प्रतीत होता है और एंड्रॉइड के पिछले रिलीज से अलग है, यह है कि जब एंड्रॉइड पाई एक SQLite डेटाबेस बनाता है, तो यह डिफ़ॉल्ट रूप से journal_mode को WAL (राइट-फॉरवर्ड लॉगिंग) सेट करता है। मैंने कभी भी WAL मोड का उपयोग नहीं किया है और SQLite डॉक्स का कहना है कि journal_mode को डिफ़ॉल्ट रूप से DELETE होना चाहिए। समस्या यह है कि अगर मैं किसी मौजूदा डेटाबेस फ़ाइल को अधिलेखित करता हूं, तो इसे my.db, राइट-फॉरवर्ड लॉग, my.db-wal, अभी भी मौजूद है और प्रभावी रूप से "ओवरराइड्स" है जो नई कॉपी की गई my.db फाइल में है। जब मैंने अपना डेटाबेस खोला, तो sqlite_master टेबल में आमतौर पर केवल android_metadata के लिए एक पंक्ति होती थी। मुझे उम्मीद थी कि सभी टेबल गायब थे। मेरा समाधान केवल डेटाबेस को खोलने के बाद, विशेष रूप से एंड्रॉइड पाई के साथ एक नया डेटाबेस बनाते समय DELETE पर जर्नल_मोड को बस सेट करना है।

PRAGMA journal_mode = DELETE;

शायद वाल बेहतर है और डेटाबेस को बंद करने का शायद कोई तरीका है ताकि राइट-फॉरवर्ड लॉग रास्ते में न मिले लेकिन मुझे वास्तव में वाल की ज़रूरत नहीं है और एंड्रॉइड के सभी पिछले संस्करणों के लिए इसकी आवश्यकता नहीं है।


मैं स्वीकृत उत्तर पर टिप्पणी नहीं कर सकता, इसलिए मुझे एक नया उत्तर खोलना होगा।

mContext.getDatabasePath () एक डेटाबेस कनेक्शन नहीं खोलता है, इसे सफल होने के लिए एक मौजूदा फ़ाइल नाम की भी आवश्यकता नहीं होती है (स्रोत / android-28 / android / app / ContextImpl.java देखें):

 @Override public File getDatabasePath(String name) { File dir; File f; if (name.charAt(0) == File.separatorChar) { // snip } else { dir = getDatabasesDir(); f = makeFilename(dir, name); } return f; } private File makeFilename(File base, String name) { if (name.indexOf(File.separatorChar) < 0) { return new File(base, name); } throw new IllegalArgumentException( "File " + name + " contains a path separator"); } 

यहाँ इस समस्या का सही समाधान है:

अपने SQLiteOpenHelper वर्ग में बस इस विधि को ओवरराइड करें:

 @Override public void onOpen(SQLiteDatabase db) { super.onOpen(db); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) { db.disableWriteAheadLogging(); } } 

सबसे पहले, इस प्रश्न को पोस्ट करने के लिए धन्यवाद। मेरे साथ भी यही बात हुई थी। सभी अच्छी तरह से काम कर रहे थे, लेकिन तब जब Android P पूर्वावलोकन के खिलाफ परीक्षण किया गया तो मैं क्रैश हो रहा था। यह बग मुझे इस कोड के लिए मिला है:

private void copyDatabase(File dbFile, String db_name) throws IOException{
    InputStream is = null;
    OutputStream os = null;

    SQLiteDatabase db = context.openOrCreateDatabase(db_name, Context.MODE_PRIVATE, null);
    db.close();
    try {
        is = context.getAssets().open(db_name);
        os = new FileOutputStream(dbFile);

        byte[] buffer = new byte[1024];
        while (is.read(buffer) > 0) {
            os.write(buffer);
        }
    } catch (IOException e) {
        e.printStackTrace();
        throw(e);
    } finally {
        try {
            if (os != null) os.close();
            if (is != null) is.close();

        } catch (IOException e) {
            e.printStackTrace();
        }

    }
}

मैं जिस मुद्दे पर भाग गया था, वह SDK 28+ में खुला ठीक काम करता है। OpenOrCreateDatabase अब स्वचालित रूप से आपके लिए android_metadata तालिका नहीं बनाता है। इसलिए यदि आप "TABLE से सेलेक्ट * की एक क्वेरी करते हैं" तो यह उस TABLE को नहीं मिलेगी क्योंकि क्वेरी "पहले" टेबल के बाद दिखना शुरू होती है जो मेटाडेटा टेबल होनी चाहिए। मैंने android_metadata तालिका को मैन्युअल रूप से जोड़कर इसे ठीक किया और सब कुछ ठीक था। आशा है कि किसी और को यह उपयोगी लगता है। यह पता लगाने के लिए हमेशा के लिए लिया गया क्योंकि विशिष्ट प्रश्नों ने अभी भी ठीक काम किया है।





android-9.0-pie