sqliteLog 14: cannot open file at line

允我心安 提交于 2019-12-25 01:53:47

问题


I don't know where are my mistakes. I am trying to store the lyrics.db file in my DB_PATH when the database doesn't exist and my checkDatabase method returns false, or my DB is obsolete.

However, I get the following :-

E/SQLiteLog: (14) cannot open file at line 36356 of [605907e73a]
    (14) os_unix.c:36356: (2) open(/data/user/0/id.ac.umn.project_uts_mobile_16862/filesid.ac.umn.project_uts_mobile_16862/databases/lyrics.db) - 
    (1) Process ts_mobile_16862 : Pid (12455) Uid (10196) Euid (10196) Gid (10196) Egid (10196)
    (1) osStat failed "/data/user/0/id.ac.umn.project_uts_mobile_16862/filesid.ac.umn.project_uts_mobile_16862/databases/lyrics.db" due to error (2)
    (1) osStat failed "/data/user/0/id.ac.umn.project_uts_mobile_16862/filesid.ac.umn.project_uts_mobile_16862/databases" due to error (2)
    (1) osStat failed "/data/user/0/id.ac.umn.project_uts_mobile_16862/filesid.ac.umn.project_uts_mobile_16862" due to error (2)
    (1) Stat of /data/user/0/id.ac.umn.project_uts_mobile_16862 : st_mode(40700) st_uid(10196) st_gid(10196) st_ino(265643)
    (1) Stat of /data/user/0 : st_mode(40771) st_uid(1000) st_gid(1000) st_ino(262147)
    (1) Stat of /data/user : st_mode(40711) st_uid(1000) st_gid(1000) st_ino(196619)
    (1) Stat of /data : st_mode(40771) st_uid(1000) st_gid(1000) st_ino(2)
E/SQLiteDatabase: Failed to open database '/data/user/0/id.ac.umn.project_uts_mobile_16862/filesid.ac.umn.project_uts_mobile_16862/databases/lyrics.db'.
    android.database.sqlite.SQLiteCantOpenDatabaseException: unknown error (code 1294): Could not open database
    #################################################################
    Error Code : 1294 (SQLITE_CANTOPEN_ENOENT)
    Caused By : Specified directory or database file does not exist.
        (unknown error (code 1294): Could not open database)
    #################################################################
        at android.database.sqlite.SQLiteConnection.nativeOpen(Native Method)
        at android.database.sqlite.SQLiteConnection.open(SQLiteConnection.java:272)
        at android.database.sqlite.SQLiteConnection.open(SQLiteConnection.java:213)
        at android.database.sqlite.SQLiteConnectionPool.openConnectionLocked(SQLiteConnectionPool.java:701)
        at android.database.sqlite.SQLiteConnectionPool.open(SQLiteConnectionPool.java:272)
        at android.database.sqlite.SQLiteConnectionPool.open(SQLiteConnectionPool.java:239)
        at android.database.sqlite.SQLiteDatabase.openInner(SQLiteDatabase.java:1276)
        at android.database.sqlite.SQLiteDatabase.open(SQLiteDatabase.java:1231)
        at android.database.sqlite.SQLiteDatabase.openDatabase(SQLiteDatabase.java:915)
        at android.database.sqlite.SQLiteDatabase.openDatabase(SQLiteDatabase.java:864)
        at id.ac.umn.project_uts_mobile_16862.DbHelper.checkDatabase(DbHelper.java:50)
        at id.ac.umn.project_uts_mobile_16862.DbHelper.createDB(DbHelper.java:85)
        at id.ac.umn.project_uts_mobile_16862.show_lyrics.onCreate(show_lyrics.java:22)
        at android.app.Activity.performCreate(Activity.java:7258)
        at android.app.Activity.performCreate(Activity.java:7249)
        at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1222)
        at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2927)
        at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:3059)
        at android.app.ActivityThread.-wrap11(Unknown Source:0)
        at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1724)
        at android.os.Handler.dispatchMessage(Handler.java:106)
        at android.os.Looper.loop(Looper.java:164)
        at android.app.ActivityThread.main(ActivityThread.java:7000)
        at java.lang.reflect.Method.invoke(Native Method)
        at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:441)
        at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1408)

it's my code for load the DB

public class DbHelper extends SQLiteOpenHelper {

    private static String DB_PATH = "";
    private static String DB_NAME = "lyrics.db";
    private SQLiteDatabase vDatabase;
    private Context vContext = null;

    public DbHelper(Context context) {
        super(context, DB_NAME, null, 1);
        if( Build.VERSION.SDK_INT >= 17)
            DB_PATH = context.getFilesDir().getPath()+context.getPackageName()+"/databases/";
        else
            DB_PATH = context.getApplicationInfo().dataDir+"/databases/";

        this.vContext = context;
    }

    @Override
    public synchronized void close() {
        if( vDatabase != null ) {
            vDatabase.close();
        }
        super.close();
    }

    private boolean checkDatabase() {
        SQLiteDatabase dbTemp = null;
        try{
            String path = DB_PATH + DB_NAME;
            dbTemp = SQLiteDatabase.openDatabase(path, null, OPEN_READWRITE);
        } catch ( SQLiteException e) { }
        if (dbTemp != null ) {
            dbTemp.close();
        }
        return dbTemp != null;
    }

    public void copyDB() throws SQLiteException{
        try {
            InputStream myInput = vContext.getAssets().open(DB_NAME);
            String outputFileName = DB_PATH + DB_NAME;
            Log.d("LIFECYCLE", outputFileName);
            OutputStream myOutput = new FileOutputStream(outputFileName);

            byte[] buffer = new byte[1024];
            int length;
            while( (length=myInput.read(buffer)) > 0 ){
                myOutput.write(buffer, 0, length);
            }

            myOutput.flush();
            myOutput.close();
            myInput.close();
        } catch ( IOException e) {
            e.printStackTrace();
        }
    }

    public void openDB() {
        String path = DB_PATH + DB_NAME;
        vDatabase = SQLiteDatabase.openDatabase(path, null, OPEN_READWRITE);
    }

    public void createDB() {
        boolean dbExist = checkDatabase();
        if ( dbExist ){

        }
        else {
            this.getReadableDatabase();
            copyDB();
        }
    }

    public List<Lyric> getAllSong(){
        List<Lyric> temp = new ArrayList<>();
        SQLiteDatabase db = this.getWritableDatabase();
        Cursor cursor;
        try{
            cursor = db.rawQuery( "SELECT * FROM lyrics" , null);
            if( cursor == null) return null;

            cursor.moveToFirst();
            do {
                Lyric lyric = new Lyric(
                        cursor.getString(cursor.getColumnIndex("index")),
                        cursor.getString(cursor.getColumnIndex("song")),
                        cursor.getString(cursor.getColumnIndex("year")),
                        cursor.getString(cursor.getColumnIndex("artist")),
                        cursor.getString(cursor.getColumnIndex("genre")),
                        cursor.getString(cursor.getColumnIndex("lyrics"))
                        );
                temp.add(lyric);
            } while (cursor.moveToNext());
            cursor.close();
        } catch ( Exception e){ }
        db.close();
        return temp;
    }

    @Override
    public void onCreate(SQLiteDatabase db) {

    }

    @Override
    public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {

    }
}

I've already trying to debug this code for almost 15 hours, and even don't get a single clue, where are my mistakes.

how to check, if that path exist? and how to make directory / path if it didn't exist?


回答1:


Step by Step guide.

1. Create the database using an appropriate SQLite management tool with the table lyrics populated with the required data. Ensuring the database is saved.

  • In this case NaviCat was used and the following SQL was used to create and populate the lyrics table.

:-

CREATE TABLE IF NOT EXISTS lyrics (
    id INTEGER PRIMARY KEY,
    song TEXT, 
    year TEXT,
    artist TEXT,
    genre TEXT,
    lyrics TEXT
);
INSERT INTO lyrics (song, year, artist, genre, lyrics) VALUES
    ('song1','1970','Fred','Rock','Rock rock rock'),
    ('song2','1980','Mary','Pop','Pop pop pop'),
    ('song3','1960','Sue','Folk','Folk folk folk');
  • Note index is a keyword so is not a valid column name unless it is enclosed, so id has been used instead of index.

2. The file was saved as lyrics.db and copied into the assets folder of the project.

3. DbHelper.java (various modifications)

public class DbHelper extends SQLiteOpenHelper {

    private static String DB_NAME = "lyrics.db";
    private SQLiteDatabase vDatabase;
    private Context vContext;

    public DbHelper(Context context) {
        super(context, DB_NAME, null, 1);
        this.vContext = context;
        // Copy the DB if need be when instantiating the DbHelper
        if (!checkDataBase()) {
            copyDB();
        }
        vDatabase = this.getWritableDatabase(); //Get the database when instantiating
    }

    /**
     * No need for build version check as getDataBasePath works for all versions
     * No need for open and close of Database, just open it once for the lifetime (more efficient)
     */

    /**
     * Check if the database already exist to avoid re-copying the file each time you open the application.
     * @return true if it exists, false if it doesn't
     */
    private boolean checkDataBase() {
        /**
         * Does not open the database instead checks to see if the file exists
         * also creates the databases directory if it does not exists
         * (the real reason why the database is opened, which appears to result in issues)
         */
        File db = new File(vContext.getDatabasePath(DB_NAME).getPath()); //Get the file name of the database
        Log.d("DBPATH","DB Path is " + db.getPath());
        if (db.exists()) return true; // If it exists then return doing nothing

        // Get the parent (directory in which the database file would be)
        File dbdir = db.getParentFile();
        // If the directory does not exist then make the directory (and higher level directories)
        if (!dbdir.exists()) {
            db.getParentFile().mkdirs();
            dbdir.mkdirs();
        }
        return false;
    }

    public void copyDB() throws SQLiteException{
        try {
            InputStream myInput = vContext.getAssets().open(DB_NAME);
            String outputFileName = vContext.getDatabasePath(DB_NAME).getPath(); //<<<<<<<<<< changed
            Log.d("LIFECYCLE", outputFileName);
            OutputStream myOutput = new FileOutputStream(outputFileName);

            byte[] buffer = new byte[1024];
            int length;
            while( (length=myInput.read(buffer)) > 0 ){
                myOutput.write(buffer, 0, length);
            }
            myOutput.flush();
            myOutput.close();
            myInput.close();
        } catch ( IOException e) {
            e.printStackTrace();
        }
    }

    public List<Lyric> getAllSong(){
        List<Lyric> temp = new ArrayList<>();
        Cursor cursor = vDatabase.query("lyrics",null,null,null,null,null,null);
        //Cursor cursor = db.rawQuery( "SELECT * FROM lyrics" , null); // used query method generally preferred to rawQuery
        //if( cursor == null) return null; // Cursor will not be null may be empty
        //cursor.moveToFirst(); // changed to use simpler loop
        while (cursor.moveToNext()) {
            Lyric lyric = new Lyric(
                    //cursor.getString(cursor.getColumnIndex("index")), //<<<<<<< changed due to column name change
                    cursor.getString(cursor.getColumnIndex("id")),
                    cursor.getString(cursor.getColumnIndex("song")),
                    cursor.getString(cursor.getColumnIndex("year")),
                    cursor.getString(cursor.getColumnIndex("artist")),
                    cursor.getString(cursor.getColumnIndex("genre")),
                    cursor.getString(cursor.getColumnIndex("lyrics"))
            );
            temp.add(lyric);
        }
        cursor.close();
        //db.close(); // inefficient to keep on opening and closing db
        return temp;
    }

    @Override
    public void onCreate(SQLiteDatabase db) {
    }

    @Override
    public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
    }
}
  • Check the comments re modifications

4. Uninstall the existing App or delete the App's data (IMPORTANT)

5. Test it.

The following is an activity that will test the above.

public class MainActivity extends AppCompatActivity {

    DbHelper vDBHlpr;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        vDBHlpr = new DbHelper(this);
        List<Lyric> mylyricslist =  vDBHlpr.getAllSong();
        for (Lyric l: mylyricslist) {
            Log.d("LYRICFROMDB","Song is " + l.getSong() + " Year is " + l.getYear() + " Artist is " + l.getArtist() + " Genre is " + l.getGenre() + " Lyrics are " + l.getLyrics());
        }
    }
}

The log should include (first run) :-

03-17 19:42:11.067 16057-16057/? D/DBPATH: DB Path is /data/data/com.example.so55199382lyrics/databases/lyrics.db
03-17 19:42:11.067 16057-16057/? D/LIFECYCLE: /data/data/com.example.so55199382lyrics/databases/lyrics.db
03-17 19:42:11.086 16057-16057/? D/LYRICFROMDB: Song is song1 Year is 1970 Artist is Fred Genre is Rock Lyrics are Rock rock rock
03-17 19:42:11.086 16057-16057/? D/LYRICFROMDB: Song is song2 Year is 1980 Artist is Mary Genre is Pop Lyrics are Pop pop pop
03-17 19:42:11.086 16057-16057/? D/LYRICFROMDB: Song is song3 Year is 1960 Artist is Sue Genre is Folk Lyrics are Folk folk folk

or for subsequent runs :-

03-17 19:49:11.275 16136-16136/? D/DBPATH: DB Path is /data/data/com.example.so55199382lyrics/databases/lyrics.db
03-17 19:49:11.279 16136-16136/? D/LYRICFROMDB: Song is song1 Year is 1970 Artist is Fred Genre is Rock Lyrics are Rock rock rock
03-17 19:49:11.279 16136-16136/? D/LYRICFROMDB: Song is song2 Year is 1980 Artist is Mary Genre is Pop Lyrics are Pop pop pop
03-17 19:49:11.279 16136-16136/? D/LYRICFROMDB: Song is song3 Year is 1960 Artist is Sue Genre is Folk Lyrics are Folk folk folk
  • Note run twice so you check both the copy process and handling of the existing db.

The above was tested on emulators running Android Lollipop and Pie




回答2:


I've already trying to debug this code for almost 15 hours, and even don't get a single clue, where are my mistakes.

Your issue is that the databases directory doesn't exist when you try opening the database in the checkDatabase method.

The clue is here :-

Error Code : 1294 (SQLITE_CANTOPEN_ENOENT)
    Caused By : Specified directory or database file does not exist.
        (unknown error (code 1294): Could not open database)

The path is typically data/data/your_package/databases/your_database_file

data/data will exist as it's part of android.

The package sub-directory will exist as part of installing the package.

The databases directory does not initially exist, using a sub-class of SQLiteOpenHelper will, when creating the database, create the databases folder and hence why you see attempts to open the database via such a helper, simply to create the databases directory. However, this can be problematic as from Android Pie on SQLite WAL mode is used by default, which unless the -shm and -wal files are deleted before the copy of the database, result in an empty database being created (-wal/-shm files do not belong/correlate to the copied database so database file is deleted/emptied).

I'd suggest that rather than trying to open the database that you check to see if the File exists and if not to then check if the File's parentfile exists (the databases directory) and if that doesn't exist to use the mkdirs method to create the directory.

The following is an example method that does all the above checking :-

/**
 * Check if the database already exist to avoid re-copying the file each time you open the application.
 * @return true if it exists, false if it doesn't
 */
private boolean checkDataBase() {
    /**
     * Does not open the database instead checks to see if the file exists
     * also creates the databases directory if it does not exists
     * (the real reason why the database is opened, which appears to result in issues)
     */

    File db = new File(myContext.getDatabasePath(DB_NAME).getPath()); //Get the file name of the database
    Log.d("DBPATH","DB Path is " + db.getPath());
    if (db.exists()) return true; // If it exists then return doing nothing

    // Get the parent (directory in which the database file would be)
    File dbdir = db.getParentFile();
    // If the directory does not exist then make the directory (and higher level directories)
    if (!dbdir.exists()) {
        db.getParentFile().mkdirs();
        dbdir.mkdirs();
    }

    return false;
}
  • Note the above uses the recommended getDatabasePath so there is no need to hard code anything other than the database name (you may wish to adopt this throughout your code).


来源:https://stackoverflow.com/questions/55199382/sqlitelog-14-cannot-open-file-at-line

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!