Wonton
Wonton

Reputation: 1143

SQLite database not copied correctly in some Android devices

I've developed an app that uses a SQLite database. It's copied into the data folder of the app on a fresh install and each time the app is updated (since the database is read-only I overwrite the last version).

This is the code I'm using:

public class IfcDatabase extends SQLiteOpenHelper {

    private static final Object lock = new Object();
    private static volatile IfcDatabase mInstance;
    private SQLiteDatabase db = null;
    private Context ctxt;

    public static final int DATABASE_VERSION = 22;
    public static final String DATABASE_NAME = "mydatabase.sqlite";
    public static String DB_PATH = "";

    public static IfcDatabase getInstance(Context ctx) {

        IfcDatabase r = mInstance;
        if (r == null) {
            synchronized (lock)
            {    // While we were waiting for the lock, another
                r = mInstance;        // thread may have instantiated the object.
                if (r == null)
                {
                    DB_PATH = ctx.getDatabasePath(DATABASE_NAME).getParent()+"/";
                    Log.e("logs", "DB_PATH="+DB_PATH);
                    r = new IfcDatabase(ctx.getApplicationContext());
                    mInstance = r;
                }
            }
        }
        return r;
    }


    private IfcDatabase(Context context) {
        //super(context, DATABASE_NAME, null, DATABASE_VERSION);
        super(context, DB_PATH+DATABASE_NAME, null, DATABASE_VERSION);
        ctxt = context;
        try {
            if (!existsDatabase()) {
                SQLiteDatabase tempDb = getWritableDatabase();
                Log.e("logs", "Just before copying database");
                copyDatabase();
                Log.e("logs", "Just after copying database");
            }
        } catch (SQLException eSQL) {
            Log.e("logs", "Error: Can not open database");
        } catch (IOException IOe) {
            Log.e("logs", "Error: Can not copy initial database");
        }
    }

    private boolean existsDatabase() {
        File dbFile = new File(DB_PATH + DATABASE_NAME);
        return dbFile.exists();
    }

    public void copyDatabase() throws IOException {
        AssetManager _asset = ctxt.getAssets();
        InputStream myInput = _asset.open(DATABASE_NAME);
        String outFileName = DB_PATH + DATABASE_NAME;
        Log.e("logs", "OUTFILENAME="+outFileName);
        OutputStream myOutput = new FileOutputStream(outFileName);
        byte[] buffer = new byte[1024];
        int length;
        while ((length = myInput.read(buffer)) > 0) {
            myOutput.write(buffer, 0, length);
        }
        myOutput.flush();
        myOutput.close();
        myInput.close();
    }

    @Override
    public void onCreate(SQLiteDatabase db) {
    }

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

    public void openDatabase() {
        db = getWritableDatabase();
        Log.e("logs", "Checking if table exists (checking if database has been successfully copied)");
        if(!checkIfTableExists("mytable"))
            Log.e("logs", "At this moment this table doesn't exist");
        else
            Log.e("logs", "At this moment this table exists");
        Log.e("logs", "OPEN BBDD "+db);
    }

    private boolean checkIfTableExists(String table_name)
    {
        Cursor _cursor = db.rawQuery("SELECT name FROM sqlite_master WHERE type='table' AND name='"+table_name+"'", null);
        int count = _cursor.getCount();
        _cursor.close();
        if(count > 0)
            return true;
        else
            return false;
    }

    public void closeDatabase() {
        if (db != null)
            db.close();
    }

    ...

}

When the app is started AND when the user logs in it calls:

IfcDatabase.getInstance(this).openDatabase();

And only when the user logs out it calls:

IfcDatabase.getInstance(this).closeDatabase();

On a fresh installation openDatabase is called, database is copied from the assets to the data folder and the user can start making queries to the database. These are the logs:

DB_PATH=/data/user/0/com.mypackage.myapp/databases/
Just before copying database
OUTFILENAME=/data/user/0/com.mypackage.myapp/databases/mydatabase.sqlite
Just after copying database
Checking if table exists (checking if database has been successfully copied)
At this moment this table exists
OPEN BBDD SQLiteDatabase: /data/user/0/com.mypackage.myapp/databases/mydatabase.sqlite

Well, this is happening (this is working correctly) in almost all cases but in some specific Android models (Google Pixel and some ZTE). In these cases, it seems that this code doesn't copy correctly the database. In these cases, on a fresh installation these are the logs:

DB_PATH=/data/user/0/com.mypackage.myapp/databases/
Just before copying database
OUTFILENAME=/data/user/0/com.mypackage.myapp/databases/mydatabase.sqlite
Just after copying database
Checking if table exists (checking if database has been successfully copied)
At this moment this table doesn't exist

As you can see, it seems that the database is empty or doesn't exist. And when the user makes a query the app crashes with this exception:

java.lang.IllegalStateException: attempt to re-open an already-closed object: SQLiteDatabase: /data/user/0/com.mypackage.myapp/databases/mydatabase.sqlite

So, is it possible that Google Pixel and ZTE use the SQLite databases in a different way and my code doesn't work in those cases?

Thank you very much and kind regards!

--- EDIT ---

I should add that I've been thinking if this could be a problem with the permissions. I request the Write external storage permission, if the user denies it I ask for a confirmation and if the user keeps denying it, then the user can not use the app. But I'm almost sure that it's not related to my problem.

--- NEW EDIT ---

I've find out a new thing: it seems that is not just a Google Pixel problem but a Google Pixel WITH Android P. This worries me a lot because if it's an Android P related problem it will happen in all devices in the future.

Currently I have no devices that can install Android P and reading the specifications of Android P I cannot see anything related to databases or internal files or something like that. Does anyone know if Android P has changed anything related to databases, assets, data folder, etc..?

Upvotes: 0

Views: 258

Answers (1)

smxbird
smxbird

Reputation: 71

Maybe you can solve it by adding this to your SQLiteOpenHelper

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

Upvotes: 3

Related Questions