Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

A strange behavior for regualr sqlite operation on android

I had a problem which I already solved but I still wants to know WHY the solution solved it. I wrote an android app that had a sqlite db after a couple of times I debugged it The oncreate method in the db didnt got called (even though everything worked fine before) After I changed the db version number from 1 to 2 everything worked fine again Even though I uninstalled the app through the app manager and also removed the cache and The local database information. My question is as follows - does the local database data is saved somewhere else? In case it doesn't - Why did it worked only after I upgraded the version number not even when I erased all the app related data?

/**
 * A class to handle sqlite reads/writes of user related data to be collected
 */
public class UserDataManager extends SQLiteOpenHelper {

    // Class Variables
    private final String TAG = UserDataManager.class.getSimpleName();

    // Database Version
    private static final int DATABASE_VERSION = 1;

    // Database Name
    public static final String DATABASE_NAME = "tmc";

    // Tables
    private static final String TABLE_USER = "user";

    // Tables and table columns names
    private String CREATE_USER_TABLE;
    private static final String COLUMN_USER_ID = "user_id";
    private static final String COLUMN_USER_MAIL = "email";
    private static final String COLUMN_USER_ACTIVE = "user_active";
    private static final String COLUMN_USER_NAME = "name";
    private static final String COLUMN_USER_PASSWORD = "password";
    private static final String COLUMN_USER_PHONE_NUMBER = "phone_number";

    /**
     * Class constructor
     * 
     * @param context
     *            The context to run in
     */
    public UserDataManager(Context context) {
        super(context, DATABASE_NAME, null, DATABASE_VERSION);
    }

    // Creating Tables
    @Override
    public void onCreate(SQLiteDatabase db) {

        CREATE_USER_TABLE = "CREATE TABLE IF NOT EXISTS " + TABLE_USER + " ("
                + COLUMN_USER_ID + " INTEGER PRIMARY KEY NOT NULL, "
                + COLUMN_USER_MAIL + " VARCHAR(64) NOT NULL, "
                + COLUMN_USER_NAME + " VARCHAR(64) NOT NULL, "
                + COLUMN_USER_PASSWORD + " VARCHAR(64) NOT NULL, "
                + COLUMN_USER_PHONE_NUMBER + " VARCHAR(64) NOT NULL, "
                + COLUMN_USER_ACTIVE + " INT NOT NULL);";

        // create the tables
        db.execSQL(CREATE_USER_TABLE);
    }

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

        // Drop older table if existed
        db.execSQL("DROP TABLE IF EXISTS " + TABLE_USER);

        // Create tables again
        onCreate(db);
    }

    /**
     * Adding a user to the database
     * 
     * @param userId
     *            The created user id
     * @param userName
     *            The user name
     * @param userEmail
     *            The user email
     * @param userPassword
     *            The user password
     * @param userPhoneNumber
     *            The user phone number
     * @param isActive
     *            Set to 1 if the user is active 0 otherwise
     * @return True if the user added successfully false otherwise
     */
    public boolean AddUser(int userId, String userName, String userEmail,
            String userPassword, String userPhoneNumber, boolean isActive) {

        // method variables
        long rowId;
        boolean pass = false;
        int active = isActive ? 1 : 0;
        SQLiteDatabase db = null;
        ContentValues row = null;

        // try to add the user to the db
        try {
            row = new ContentValues();
            db = this.getWritableDatabase();
            db.delete(TABLE_USER, null, null);
            row.put(COLUMN_USER_ID, userId);
            row.put(COLUMN_USER_NAME, userName);
            row.put(COLUMN_USER_MAIL, userEmail);
            row.put(COLUMN_USER_PASSWORD, userPassword);
            row.put(COLUMN_USER_CAR_NUMBER, userPhoneNumber);
            row.put(COLUMN_USER_ACTIVE, active);
            rowId = db.insert(TABLE_USER, null, row);
            if (rowId > -1) {
                pass = true;
            }
        } catch (SQLException exception) {
            Log.e(TAG, exception.getMessage());
        } finally {
            if (db != null) {
                // close database connection
                db.close();
            }
        }
        return pass;
    }

    /**
     * Get the current registered user
     * 
     * @return The id of the column of the registered user
     */
    public int GetRegisteredUserId() {

        // method variables
        int columnIndex = -1;
        int userId = -1;
        SQLiteDatabase db = null;
        Cursor cursor = null;

        // try to get the user from the database
        try {
            db = this.getReadableDatabase();
            cursor = db.query(TABLE_USER, new String[] { COLUMN_USER_ID },
                    null, null, null, null, null);
            if (cursor != null) {
                boolean moved = cursor.moveToFirst();
                if (moved) {
                    columnIndex = cursor.getColumnIndex(COLUMN_USER_ID);
                    if (columnIndex > -1) {
                        userId = cursor.getInt(columnIndex);
                    }
                }
            }
        } catch (SQLException exception) {
            Log.e(TAG, exception.getMessage());
        } finally {
            if (cursor != null)
                // release cursor
                cursor.close();
            if (db != null)
                // close database connection
                db.close();
        }
        return userId;
    }

    /**
     * Get the current user email
     * 
     * @return The id of the column of the registered user
     */
    public String GetRegisteredUserEmail() {

        // method variables
        int columnIndex = -1;
        String userEmail = null;
        SQLiteDatabase db = null;
        Cursor cursor = null;

        // try to get the user from the database
        try {
            db = this.getReadableDatabase();
            cursor = db.query(TABLE_USER, new String[] { COLUMN_USER_MAIL },
                    null, null, null, null, null);
            if (cursor != null) {
                boolean moved = cursor.moveToFirst();
                if (moved) {
                    columnIndex = cursor.getColumnIndex(COLUMN_USER_MAIL);
                    if (columnIndex > -1) {
                        userEmail = cursor.getString(columnIndex);
                    }
                }
            }
        } catch (SQLException exception) {
            Log.e(TAG, exception.getMessage());
        } finally {
            if (cursor != null)
                // release cursor
                cursor.close();
            if (db != null)
                // close database connection
                db.close();
        }
        return userEmail;
    }

    /**
     * Get the current user password
     * 
     * @return The password of the current logged user
     */
    public String GetRegisteredUserPassword() {

        // method variables
        int columnIndex = -1;
        String userPassword = null;
        SQLiteDatabase db = null;
        Cursor cursor = null;

        // try to get the user from the database
        try {
            db = this.getReadableDatabase();
            cursor = db.query(TABLE_USER,
                    new String[] { COLUMN_USER_PASSWORD }, null, null, null,
                    null, null);
            if (cursor != null) {
                boolean moved = cursor.moveToFirst();
                if (moved) {
                    columnIndex = cursor.getColumnIndex(COLUMN_USER_PASSWORD);
                    if (columnIndex > -1) {
                        userPassword = cursor.getString(columnIndex);
                    }
                }
            }
        } catch (SQLException exception) {
            Log.e(TAG, exception.getMessage());
        } finally {
            if (cursor != null)
                // release cursor
                cursor.close();
            if (db != null)
                // close database connection
                db.close();
        }
        return userPassword;
    }

    /**
     * Get number of rows in the user table
     * 
     * @return the number of the rows in the user table (How many users are
     *         saved in the DB)
     */
    public int GetRowCount() {

        // method variables
        int rowsCount = 0;
        SQLiteDatabase db = null;
        Cursor cursor = null;

        // try to get the user from the database
        try {
            db = this.getReadableDatabase();
            cursor = db.query(TABLE_USER, null, null, null, null, null, null);
            if (cursor != null) {
                boolean moved = cursor.moveToFirst();
                if (moved) {
                    do {
                        rowsCount++;
                    } while (cursor.moveToNext());
                }
            }
        } catch (SQLException exception) {
            Log.e(TAG, exception.getMessage());
        } finally {
            if (cursor != null)
                // release cursor
                cursor.close();
            if (db != null)
                // close database connection
                db.close();
        }
        return rowsCount;
    }

    /**
     * Remove a user from the database
     * 
     * @param userId
     *            The user id
     */
    public void LogoutUser() {

        // method variables
        SQLiteDatabase db = null;

        // try to remove a user from the database
        try {
            db = this.getWritableDatabase();
            onUpgrade(db, DATABASE_VERSION, DATABASE_VERSION);
        } catch (SQLException exception) {
            Log.e(TAG, exception.getMessage());
        } finally {
            if (db != null) {
                // close database connection
                db.close();
            }
        }
    }

    /**
     * Set a user to be active or not
     * 
     * @param isActive
     *            1 if the cigarette is active 0 otherwise
     * @return True if the cigarette active field has changed false otherwise
     */
    public boolean SetUserActive(boolean isActive) {
        // method variables
        int rowsAffected;
        int active = isActive ? 1 : 0;
        long userId;
        String userIdString;
        boolean pass = true;
        SQLiteDatabase db = null;
        ContentValues values = null;

        // try to remove a device from the database
        try {
            userId = GetRegisteredUserId();
            if (userId > -1) {
                userIdString = String.valueOf(userId);
                db = this.getWritableDatabase();
                values = new ContentValues();
                values.put(COLUMN_USER_ACTIVE, active);
                rowsAffected = db.update(TABLE_USER, values, COLUMN_USER_ID
                        + " = ?", new String[] { userIdString });
                if (rowsAffected != 1) {
                    pass = false;
                }
            }
        } catch (SQLException exception) {
            Log.e(TAG, exception.getMessage());
        } finally {
            if (db != null) {
                // close database connection
                db.close();
            }
        }
        return pass;
    }
}



Notes -
1. Please note that my device is rooted and so after inserting the data to the db im
changing the permissions on the db file for 777 so I can pull it from the phone to see
whats in it (i.e. did the query pass or not)
2. The error that is being thrown is "android.database.sqlite.SQLiteException: no such
table: user "


Chocolate chips cookies will be granted for any answer... =)

like image 459
crazyPixel Avatar asked May 10 '14 20:05

crazyPixel


People also ask

What is SQLite used for in android?

SQLite Database is an open-source database provided in Android which is used to store data inside the user's device in the form of a Text file. We can perform so many operations on this data such as adding new data, updating, reading, and deleting this data.

Does SQLite work on android?

SQLite is a opensource SQL database that stores data to a text file on a device. Android comes in with built in SQLite database implementation.

Why do we need to use SQLite for mobile applications?

Below are some more reasons on why should you use SQLite: SQLite uses SQL, so it has all the features of a standard SQL database. Some developers require databases which can scale and provide support for concurrency. SQLite, with its rich feature, can be linked with any application in production.

What is the maximum size of SQLite database in android?

Maximum Database Size 140 tb but it will depends on your device disk size.


2 Answers

Why did it worked only after I upgraded the version number not even when I erased all the app related data?

  • As soon as you start working with either of getReadableDatabase() ,getWriteableDatabase() or any other SQLiteHelper class code. The first method calls is onCreate(SQLiteDatabase db) which creates Database under your application database path /data/data/PACKAGE_NAME/databases/tmc (in your case).

  • If you modify your Database structure in SQliteHelper the first method get called is onUpgrage() which checks whether Database_Version get modified or not. If it's then it execute onUpgrade() with series of DROP TABLE IF EXIST followed by onCreate() which again create your database with new structure under your application path by replacing your previous database file.

  • Clearing Cached data using Application Manager indeed clear database and cached data of that application. But SQLiteHelper did check for Database_Version with old and new one. If new one is greater than old one. It does call onUpgrage() followed by onCreate().

  • When you intent to use Database with Android Application it get store under /data/data/PACKAGE_NAME/databases/tmc with application process security. Unable to access database file unless you have rooted Android device in which you already have.


One can create Developer Options or anything you like just to pull database from your application process to SD Card for unrooted devices.

Copy database file from application process path to SD Card for unrooted devices.

try {
       File sd = Environment.getExternalStorageDirectory();
       File data = Environment.getDataDirectory();
        if (sd.canWrite()) {
        String currentDBPath = "/data/data/" + getPackageName() + "/databases/ZnameDB"; //Your DATABASE_NAME
        String backupDBPath = "ZnameDB_Dev.db"; //DATABASE_COPY_NAME UNDER SDCARD
        File currentDB = new File(currentDBPath);
        File backupDB = new File(sd, backupDBPath);
        if (currentDB.exists()) {
        FileChannel src = new FileInputStream(currentDB).getChannel();
        FileChannel dst = new FileOutputStream(backupDB).getChannel();
        dst.transferFrom(src, 0, src.size());
        src.close();
        dst.close();
        Toast.makeText(SettingsActivity.this, "Database Transfered!", Toast.LENGTH_SHORT).show();
          }
         }
     } catch (Exception e) {
        Log.e(TAG, e.toString());
    }
like image 199
Vikalp Patel Avatar answered Oct 11 '22 07:10

Vikalp Patel


Answering your first question, all data is stored under YOUR_PACKAGE/databases/DATABASE.db only.

If you erase app through app manager all data is removed, just package remains. If you uninstall your app everything is cleared including package folder. Even if you set for your app install location to external SD card, database is stored internally anyway.

From documentation:

The .apk file is saved on the external storage, but all private user data, databases, optimized .dex files, and extracted native code are saved on the internal device memory.

SQLiteOpenHelper logic is simple:

  • checks if DB exists, if not new DB is created
  • retrieve DB version, initial value is 0, that's why in your app minimum value for DB version is 1 to call onCreate() method at least once
  • if version is equal to 0, onCreate() is called, or
  • if version is different than the one provided by your code onUpgrade() or onDowngrade() is called

So, whenever you upgrade your scheme, version number MUST be increased, there is no argue about that, in order to allow your app work properly.

Now, in your specific case I can only guess. I would say that erasing your package was not entirely successful and bits of data was left, especially if you mentioned that you did some manual modification on DB file. Maybe it has something to do with Android version running on your device but you didn't mentioned which one is it.

That's all. I hope my answer is satisfying.

like image 28
Damian Petla Avatar answered Oct 11 '22 07:10

Damian Petla