I followed the tutorial for adding sqlite database to apk.But the application force closes on my phone
I modified my sqliteopenhelper as follows:
public class openingclass extends SQLiteOpenHelper
{
public openingclass(Context c) {
super(c,Db_NAME, null, DB_VERSION);
}
public void createDataBase() {
boolean dbExist;
try {
dbExist = checkDataBase();
} catch (SQLiteException e) {
e.printStackTrace();
throw new Error("database dose not exist");
}
if(dbExist){
//do nothing - database already exist
}else{
try {
copyDataBase();
} catch (IOException e) {
e.printStackTrace();
throw new Error("Error copying database");
}
//By calling this method and empty database will be created into the default system path
//of your application so we are gonna be able to overwrite that database with our database.
this.getReadableDatabase();
}
}
/**
* 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(){
SQLiteDatabase checkDB = null;
try{
String myPath = DB_PATH +"/"+ Db_NAME;
checkDB = SQLiteDatabase.openDatabase(myPath, null, SQLiteDatabase.OPEN_READONLY);
}catch(SQLiteException e){
//database does't exist yet.
throw new Error("database does't exist yet.");
}
if(checkDB != null){
checkDB.close();
}
return checkDB != null ? true : false;
}
/**
* Copies your database from your local assets-folder to the just created empty database in the
* system folder, from where it can be accessed and handled.
* This is done by transfering bytestream.
* */
private void copyDataBase() throws IOException{
//copyDataBase();
//Open your local db as the input stream
InputStream myInput = c1.getAssets().open(Db_NAME);
// Path to the just created empty db
String outFileName = DB_PATH +"/"+ Db_NAME;
File databaseFile = new File(DB_PATH);
// check if databases folder exists, if not create one and its subfolders
if (!databaseFile.exists()){
databaseFile.mkdir();
}
//Open the empty db as the output stream
OutputStream myOutput = new FileOutputStream(outFileName);
//transfer bytes from the inputfile to the outputfile
byte[] buffer = new byte[1024];
int length;
while ((length = myInput.read(buffer))>0){
myOutput.write(buffer, 0, length);
}
//Close the streams
myOutput.flush();
myOutput.close();
myInput.close();
}
#Override
public synchronized void close() {
if(myDataBase != null)
myDataBase.close();
super.close();
}
#Override
public void onCreate(SQLiteDatabase arg0) {
String S = "create table " +
TABLE_NAME +
" (" +
TABLE_COL_MAIL + " text primary key," +
TABLE_COL_NAME + " text," +
TABLE_COL_PASS + " text," +
TABLE_COL_PHO + " text," +
TABLE_COL_ADD + " text," +
TABLE_COL_GEN + " text," +
TABLE_COL_DOB + " text" +
");";
arg0.execSQL(S);
String S1 = "create table " +
SECOND_TABLE_NAME +
" (" +
TABLE_COL_USER + " text," +
TABLE_COL_PRODUCT + " text," +
TABLE_COL_QUANTITY + " integer" +
");";
arg0.execSQL(S1);
}
#Override
public void onUpgrade(SQLiteDatabase arg0, int arg1, int arg2) {
// TODO Auto-generated method stub
}
I also am hard coding the path , which i am not sure of.
private final String DB_PATH="/data/data/com.example.shopkart/databases/";
private final String Db_NAME = "dbshopkart.db";
Other threads tell me that the path is incorrect.How do i set the correct path with/without hard coding it.Please help!
File publicDir = Environment.getExternalStorageDirectory();
publicDir.getAbsoluteFile();
maybe this will help you
Related
My app is a quiz that has 33 stages and 25 categories, quiz data are in a LOCAL sqlite database that has three tables (Stages + Categories + Questions), users scores are saved to the Stage/Category tables in the database upon successful completion of each level.
I need from time to time to revise the data OR add new stages, categories and questions; I do so in the sqlite database itself, then I copy the database and paste it the Asset folder thus replacing old one, and the incitement version number.
These changes appear to new users after first installation BUT DO NOT appear to existing users after update.
I would like to reflect all changes and STILL PRESERVE user data (scores) that are already stored in the database.
I have tried the following:
1- copyDataBase onUpgrade - RESULT: changes and not reflected and user data is preserved.
2- Create a new_sqlite_database, copyDataBase onUpgrade using the new_sqlite_database - RESULT: changes are reflected, app crashes onclicklistner of a stage only once and works thereafter, user data is not preserved.
3- Alter Table with temp names, Drop old tables, create new tables with same old names, copyDataBase, update new tables records using temp tables - RESULT: Blank screens.
QuizDbHelper
class QuizDbHelper extends SQLiteOpenHelper {
private static final String DATABASE_NAME = "qa_sqlite.db";
private static String DATABASE_LOCATION = "/data/data/packagename/databases/";
private static final int DATABSE_VERSION = 12;
private final Context mContext;
private SQLiteDatabase mDatabase;
private final Handler handler = new Handler();
public QuizDbHelper(Context context) {
super(context, DATABASE_NAME, null, DATABSE_VERSION);
if (android.os.Build.VERSION.SDK_INT >= 17)
DATABASE_LOCATION = context.getApplicationInfo().dataDir + "/databases/";
else
DATABASE_LOCATION = "/data/data/" + context.getPackageName() + "/databases/";
this.mContext = context;
copyDataBase();
this.getReadableDatabase();
}
#Override
public void onCreate(SQLiteDatabase db) {
copyDataBase();
Log.w("POSITIVE ... ","onCreate Called - Database Copied");
}
#Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
if (newVersion > oldVersion) {
copyDataBase();
Log.w("POSITIVE ... ", "onUpgrade Called - Database Copied");
}
}
private boolean checkDataBase() {
File dbFile = new File(DATABASE_LOCATION + DATABASE_NAME);
return dbFile.exists();
}
private void copyDataBase() {
if (!checkDataBase()) {
this.getReadableDatabase();
this.close();
try {
copyDBFile();
} catch (IOException mIOException) {
throw new Error("ُCopy Database Failed");
}
}
}
private void copyDBFile() throws IOException {
InputStream mInput = mContext.getAssets().open(DATABASE_NAME);
OutputStream mOutput = new FileOutputStream(DATABASE_LOCATION + DATABASE_NAME);
byte[] mBuffer = new byte[1024];
int mLength;
while ((mLength = mInput.read(mBuffer)) > 0)
mOutput.write(mBuffer, 0, mLength);
mOutput.flush();
mOutput.close();
mInput.close();
}
private void openDatabase() {
String dbPath = mContext.getDatabasePath(DATABASE_NAME).getPath();
if (mDatabase != null && mDatabase.isOpen()) {
return;
}
mDatabase = SQLiteDatabase.openDatabase(dbPath, null, SQLiteDatabase.OPEN_READWRITE);
}
private void closeDatabase() {
if (mDatabase != null) {
mDatabase.close();
}
}
}
Database should be created or updated using onCreate or onUpgrade in QuizDbHelper after calling mDBHelper.getReadableDatabase(); onCreate in Mainavtivity.java ... example:
Mainavtivity.java
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
QuizDbHelper mDBHelper = new QuizDbHelper(this);
mDBHelper.getReadableDatabase();
SOME OTHER CODE
}
I NEED Two thing::
1. Reflect all changes after users update the app
2. PRESERVE user's data (scores) that are already stored in the database.
I haven't ever had to do this for one of my apps, but if I did, I would have a method to merge the new version of the database with the old one. The basic flow would be to see if the local database were present, if not, copy the new database to the local folder. If it does exist, check the db version. If the db version is less than the new database version, then copy the new database to the local folder with a temp name, then call doMerge().
doMerge would open the new database and select all rows from one of the tables into a cursor. Then, it would iterate through the cursor and do a select query on the old database to see if that record already exists in the old database, if so, skip it. Say, the new database has a cursor of all of the Stages records, you take each record's Name or Description value and "SELECT * FROM old.stages WHERE Name = '" + newName + "'"
If not found, insert the values from the new database into the old database. Repeat this for all three tables.
The trick will be managing the primary keys in each table. What if the user has inserted 50 records in one of the tables, and those keys match the primary keys in the new rows in the new database? If you have primary/foreign keys in your other tables, you would have to sync them, which would get hairy.
I managed to achieve the first objective
reflecting all changes after app upgrade ... here is what I have done:
**// 1. Check database version:**
private Boolean checkDataBaseVersion() {
Boolean Upgrade = false;
SQLiteDatabase db = mContext.openOrCreateDatabase(DATABASE_NAME, Context.MODE_PRIVATE, null);
Integer CurrentdbVersion = db.getVersion();
if (CurrentdbVersion < DATABSE_VERSION) {
Upgrade = true;
} else {
Upgrade = false;
}
Log.w("POSITIVE ...", "checkDataBase - Database Upgrade = " + Upgrade + " ... CurrentdbVersion: "
+ CurrentdbVersion + " DATABSE_VERSION: " + DATABSE_VERSION);
db.close();
return Upgrade;
}
**// 2. change copy database code:**
public void copyDataBase() {
if (!checkDataBase()) {
this.getReadableDatabase();
this.close();
try {
copyDBFile();
Log.w("POSITIVE ...", "copyDataBase - Database does not exists ... db copied");
} catch (IOException mIOException) {
throw new Error("حدث خطأ في نسخ قاعدة البانات من الملف");
}
} else if (checkDataBaseVersion()) {
//this.getReadableDatabase();
//this.close();
try {
copyDBFile();
Log.w("POSITIVE ...", "copyDataBase - Database exists - new version exists ... db copied ");
} catch (IOException mIOException) {
throw new Error("حدث خطأ في نسخ قاعدة البانات من الملف");
}
} else {
Log.w("POSITIVE ...", "copyDataBase - Database exists - no new version ... nothing done ");
}
}
private boolean checkDataBase() {
File dbFile = new File(DATABASE_LOCATION + DATABASE_NAME);
Log.w("POSITIVE ...", "checkDataBase - Database exists = " + dbFile.exists());
close(); /// NEW
return dbFile.exists();
}
**// 3. remove copy database code from public QuizDbHelper(Context context) and place it where it is needed:**
public QuizDbHelper(Context context) {
super(context, DATABASE_NAME, null, DATABSE_VERSION);
if (android.os.Build.VERSION.SDK_INT >= 17)
DATABASE_LOCATION = context.getApplicationInfo().dataDir + "/databases/";
else
DATABASE_LOCATION = "/data/data/" + context.getPackageName() + "/databases/";
this.mContext = context;
}
i'm a beginner in java and android. now i want to work with external database (cancer.db).
i created "DataBaseHelper" class.
public class DataBaseHelper extends SQLiteOpenHelper{
//The Android's default system path of your application database.
private static String DB_PATH = "/data/data/YOUR_PACKAGE/databases/";
private static String DB_NAME = "myDBName";
private SQLiteDatabase myDataBase;
private final Context myContext;
/**
* Constructor
* Takes and keeps a reference of the passed context in order to access to the application assets and resources.
* #param context
*/
public DataBaseHelper(Context context) {
super(context, DB_NAME, null, 1);
this.myContext = context;
}
public void createDataBase() throws IOException{
boolean dbExist = checkDataBase();
if(dbExist){
//do nothing - database already exist
}else{
//By calling this method and empty database will be created into the default system path
//of your application so we are gonna be able to overwrite that database with our database.
this.getReadableDatabase();
try {
copyDataBase();
} catch (IOException e) {
throw new Error("Error copying database");
}
}
}
/**
* 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(){
SQLiteDatabase checkDB = null;
try{
String myPath = DB_PATH + DB_NAME;
checkDB = SQLiteDatabase.openDatabase(myPath, null, SQLiteDatabase.OPEN_READONLY);
}catch(SQLiteException e){
//database does't exist yet.
}
if(checkDB != null){
checkDB.close();
}
return checkDB != null ? true : false;
}
/**
* Copies your database from your local assets-folder to the just created empty database in the
* system folder, from where it can be accessed and handled.
* This is done by transfering bytestream.
* */
private void copyDataBase() throws IOException{
//Open your local db as the input stream
InputStream myInput = myContext.getAssets().open(DB_NAME);
// Path to the just created empty db
String outFileName = DB_PATH + DB_NAME;
//Open the empty db as the output stream
OutputStream myOutput = new FileOutputStream(outFileName);
//transfer bytes from the inputfile to the outputfile
byte[] buffer = new byte[1024];
int length;
while ((length = myInput.read(buffer))>0){
myOutput.write(buffer, 0, length);
}
//Close the streams
myOutput.flush();
myOutput.close();
myInput.close();
}
public void openDataBase() throws SQLException{
//Open the database
String myPath = DB_PATH + DB_NAME;
myDataBase = SQLiteDatabase.openDatabase(myPath, null, SQLiteDatabase.OPEN_READONLY);
}
#Override
public synchronized void close() {
if(myDataBase != null)
myDataBase.close();
super.close();
}
#Override
public void onCreate(SQLiteDatabase db) {
}
#Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
}
// Add your public helper methods to access and get content from the database.
// You could return cursors by doing "return myDataBase.query(....)" so it'd be easy
// to you to create adapters for your views.}
then i use codes below in "on create" method to create or open database:
private static String DB_PATH = "/data/data/info.myprogram/databases/";
private static String DB_NAME = "cancer.db";
DataBaseHelper myDbHelper = new DataBaseHelper();
myDbHelper = new DataBaseHelper(this);
try {
myDbHelper.createDataBase();
} catch (IOException ioe) {
throw new Error("Unable to create database");
}
try {
myDbHelper.openDataBase();
}catch(SQLException sqle){
throw sqle;
}
i use these codes to get query. my table name is "shb":
SQLiteDatabase db;
Cursor cc= db.rawQuery("SELECT Name FROM shb WHERE _id=1",null);
cc.moveToFirst();
String sss=cc.getString(1);
now when i start debugging i get errors. what's the wrong code? whats my mistake? how should i get query?
and excuse me for my weak English, because it's not my mother tongue .
First create a sqlite database and create table inside it, put it in assets folder of your app.use that database by using following code. please make changes in code for your datbaseName, tableName, fieldsName and path of database etc. i have also write the code of getting list of data by passing paerticuler value.
public class MySQLiteHelper extends SQLiteOpenHelper {
public static final String TABLE_CARS = "Cars";
public static final String COL_ENAME = "ename";
public static final String COL_MODAL = "modal";
public static final String COL_ANAME = "aname";
public static final String COL_TYPES = "types";
private static final String TAG = "MSSS : DatabaseHelper";
// Your Database Name
private static final String DATABASE_NAME = "4saleCars.sqlite";
// Your Your Database Version
private static final int DATABASE_VERSION = 1;
private final Context myContext;
private SQLiteDatabase myDataBase;
// The Android's default system path of your application database. DB_PATH =
// "/data/data/YOUR_PACKAGE/databases/"
private String DB_PATH = "/data/data/net.forsalemall.android/databases/";
public MySQLiteHelper(Context context) {
super(context, DATABASE_NAME, null, DATABASE_VERSION);
this.myContext = context;
try {
createDataBase();
} catch (Exception e) {
Log.e(TAG,
"DatabaseHelper_constuctor createDataBase :"
+ e.fillInStackTrace());
}
}
#Override
public void onCreate(SQLiteDatabase database) {
}
#Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
}
// ***************************** My DB Handler Methods
// *****************************
/**
* Creates a empty database on the system and rewrites it with your own
* database.
**/
public void createDataBase() throws IOException {
boolean dbExist = checkDataBase();
if (dbExist) {
// Database already exist
openDataBase();
} else {
// By calling this method and empty database will be created into
// the default system path
// of your application so we are gonna be able to overwrite that
// database with our database.
myDataBase = getWritableDatabase();
try {
copyDataBase();
} catch (IOException e) {
throw new Error("Error createDataBase().");
}
}
}
/**
* 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() {
SQLiteDatabase checkDB = null;
try {
String myPath = DB_PATH + DATABASE_NAME;
checkDB = SQLiteDatabase.openDatabase(myPath, null,
SQLiteDatabase.OPEN_READWRITE);
} catch (SQLiteException e) {
// database does't exist yet.
}
if (checkDB != null) {
checkDB.close();
}
return checkDB != null ? true : false;
}
/*
* public boolean isDataBaseExist() { File dbFile = new File(DB_PATH +
* DATABASE_NAME); return dbFile.exists(); }
*/
/**
* Copies your database from your local assets-folder to the just created
* empty database in the system folder, from where it can be accessed and
* handled. This is done by transferring bytestream.
**/
private void copyDataBase() throws IOException {
try {
// Open your local db as the input stream
InputStream myInput = myContext.getAssets().open(DATABASE_NAME);
// Path to the just created empty db
String outFileName = DB_PATH + DATABASE_NAME;
// Open the empty db as the output stream
OutputStream myOutput = new FileOutputStream(outFileName);
// transfer bytes from the inputfile to the outputfile
byte[] buffer = new byte[1024];
int length;
while ((length = myInput.read(buffer)) > 0) {
myOutput.write(buffer, 0, length);
}
// Close the streams
myOutput.flush();
myOutput.close();
myInput.close();
} catch (IOException e) {
throw new Error("Error copyDataBase().");
}
}
public void openDataBase() throws SQLException, IOException {
try {
// Open the database
String myPath = DB_PATH + DATABASE_NAME;
myDataBase = SQLiteDatabase.openDatabase(myPath, null,
SQLiteDatabase.OPEN_READWRITE);
} catch (SQLiteException e) {
throw new Error("Error openDataBase().");
}
}
// ***************************** My DML Methods
// *****************************
public List<String> getModalList(String aname, Context context) {
List<String> list = new ArrayList<String>();
list.add(context.getResources().getString(R.string.all));
Cursor cursor;
String query;
query = "SELECT modal FROM Cars WHERE aname = '" + aname + "';";
try {
cursor = myDataBase.rawQuery(query, null);
if (cursor != null) {
while (cursor.moveToNext())
list.add(Utils.getDecodedString(cursor.getString(0)));
}
cursor.deactivate();
} catch (Exception e) {
Log.e(TAG, "getModalList Error : " + e.fillInStackTrace());
}
return list;
}
}
use this getModalList in your activity just like below
MySQLiteHelper dbHelper = new MySQLiteHelper(
AdvanceSearchActivity.this);
String aname = Utils.getEncodedString(catName);
car_modal = dbHelper.getModalList(aname,AdvanceSearchActivity.this);
For my internship I'm working on an Android application, and trying to get a local database running to set up for offline referal as well as preventing new BasicAuth requests and a required database pull every time the user returns to a certain activity. To achieve this, I'm trying to set up a SQLite database, but things are not really working out.
I'm currently using this tutorial to try and set up the database. For my application specifically I edited the MySQLiteHelper and TicketsDataSource classes.
MySQLiteHelper:
import android.content.Context;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;
import android.util.Log;
public class MySQLiteHelper extends SQLiteOpenHelper {
public static final String TABLE_TICKETS = "tickets";
public static final String COLUMN_ID = "_id";
public static final String COLUMN_TICKET_ID = "ticketId";
public static final String COLUMN_TICKET_TITLE = "ticketTitle";
public static final String COLUMN_PROJECT_NAME = "projectName";
public static final String COLUMN_CLIENT_NAME = "clientName";
public static final String COLUMN_TICKET_DESCRIPTION = "ticketDescription";
private static final String DATABASE_NAME = "commments.db";
private static final int DATABASE_VERSION = 1;
// Database creation SQL statement
private static final String DATABASE_CREATE = "create table "
+ TABLE_TICKETS
+ "("
+ COLUMN_ID + " integer primary key autoincrement, "
+ COLUMN_TICKET_ID + " text not null, "
+ COLUMN_TICKET_TITLE+ " text not null, "
+ COLUMN_PROJECT_NAME+ " text not null, "
+ COLUMN_CLIENT_NAME+ " text not null, "
+ COLUMN_TICKET_DESCRIPTION+ " text not null);";
public MySQLiteHelper(Context context) {
super(context, DATABASE_NAME, null, DATABASE_VERSION);
}
#Override
public void onCreate(SQLiteDatabase database) {
database.execSQL(DATABASE_CREATE);
}
#Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
Log.w(MySQLiteHelper.class.getName(),
"Upgrading database from version " + oldVersion + " to "
+ newVersion + ", which will destroy all old data");
db.execSQL("DROP TABLE IF EXISTS " + TABLE_TICKETS);
onCreate(db);
}
}
TicketsDataSource:
import java.util.ArrayList;
import java.util.List;
import android.content.ContentValues;
import android.content.Context;
import android.database.Cursor;
import android.database.SQLException;
import android.database.sqlite.SQLiteDatabase;
public class TicketsDataSource {
// Database fields
private SQLiteDatabase database;
private MySQLiteHelper dbHelper;
private String[] allColumns = { MySQLiteHelper.COLUMN_ID,
MySQLiteHelper.COLUMN_TICKET_ID, MySQLiteHelper.COLUMN_TICKET_TITLE, MySQLiteHelper.COLUMN_PROJECT_NAME,
MySQLiteHelper.COLUMN_CLIENT_NAME, MySQLiteHelper.COLUMN_TICKET_DESCRIPTION};
public TicketsDataSource(Context context) {
dbHelper = new MySQLiteHelper(context);
}
public void open() throws SQLException {
database = dbHelper.getWritableDatabase();
}
public void close() {
dbHelper.close();
}
public Ticket createTicket(String ticket) {
ContentValues values = new ContentValues();
values.put(MySQLiteHelper.COLUMN_TICKET_DESCRIPTION, ticket);
long insertId = database.insert(MySQLiteHelper.TABLE_TICKETS, null,
values);
Cursor cursor = database.query(MySQLiteHelper.TABLE_TICKETS,
allColumns, MySQLiteHelper.COLUMN_ID + " = " + insertId, null,
null, null, null);
cursor.moveToFirst();
Ticket newTicket = cursorToTicket(cursor);
cursor.close();
return newTicket;
}
public void deleteTicket(Ticket ticket) {
long id = ticket.getId();
System.out.println("Ticket deleted with id: " + id);
database.delete(MySQLiteHelper.TABLE_TICKETS, MySQLiteHelper.COLUMN_ID
+ " = " + id, null);
}
public List<Ticket> getAllTickets() {
List<Ticket> tickets = new ArrayList<Ticket>();
Cursor cursor = database.query(MySQLiteHelper.TABLE_TICKETS,
allColumns, null, null, null, null, null);
cursor.moveToFirst();
while (!cursor.isAfterLast()) {
Ticket ticket = cursorToTicket(cursor);
tickets.add(ticket);
cursor.moveToNext();
}
// make sure to close the cursor
cursor.close();
return tickets;
}
private Ticket cursorToTicket(Cursor cursor) {
Ticket ticket = new Ticket();
ticket.setId(cursor.getLong(0));
ticket.setTicketId(cursor.getLong(1));
ticket.setTicketTitle(cursor.getString(2));
ticket.setClientName(cursor.getString(3));
ticket.setProjectName(cursor.getString(4));
ticket.setTicketDescription(cursor.getString(5));
return ticket;
}
}
In my activity, I then call the TicketsDataSource class by first defining it:
private TicketsDataSource datasource;
And then calling it from an if-statement that checks if the server has been called succesfully already:
else {
datasource = new TicketsDataSource(this);
datasource.open();
}
Unfortunately, when I switch to the DDMS view, I can't see any database being created after I run the application multiple times. In fact, I can't even see any other folders within the main "data" folder in the DDMS view. I'm not getting any error messages (except for an 'eglSurfaceAttrib not implemented', which seems to be irrelevant after some searching on google) at this moment, but when I place a system print in the onCreate function of the MySQLiteHelper class, it does not print anything in the log (it does appear in the else-statement). I'm not sure if my Java-code is faulty, or if there's something wrong with my general setup.
Thanks in advance for your help,
Dennis
You'll only be able to see files/folders in the /data folder if you're
a: using the emulator, or
b: using a rooted phone. Those are protected folders and can't be viewed by mere mortals.
The real test of what you've written is this - can you write to and read from the database?
The best android db troubleshooting solution for me was to add this method to my SQliteHelper class which will copy your sqlite db to the sd card on the device your testing on. You can then open it up using any number of sqlite manager desktop apps and verify your tables and data. You don't need to root the phone this way. While developing, I stick calls to this method where i need to get a snapshot of the db:
public static void backup() {
try {
File sd = Environment.getExternalStorageDirectory();
File data = Environment.getDataDirectory();
if (sd.canWrite()) {
String currentDBPath = "//data//com.example//databases//" + DATABASE_NAME;
String backupDBPath = DATABASE_VERSION + DATABASE_NAME;
File currentDB = new File(data, currentDBPath);
File backupDB = new File(sd, "//Download//" + 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();
}
}
} catch (Exception e) {
Log.e(TAG, e.getMessage());
}
}
you need to add external storage permissions to your manifest.
You can see my database helper class below. I use prepopulated sqlite database imported in assets folder. Whenever I add a table to my existing database, I get no such table error if my app is already installed on my phone. I guess my onUpgrade() method is now so good. It works, don't get me wrong, when I change some data to existing tables, I increase db version and it gets updated. But if I add a table I get error.
public class DataBaseHelper extends SQLiteOpenHelper
{
private static String TAG = "DataBaseHelper"; // Tag just for the LogCat window
//destination path (location) of our database on device
private static String DB_PATH = "/data/data/rs.androidaplikacije.themostcompleteiqtest/databases/";
private static String DB_NAME ="pitanja.sqlite";// Database name
private static SQLiteDatabase mDataBase;
private final Context mContext;
private static final int DATABASE_VERSION = 3;
public DataBaseHelper(Context mojContext)
{
super(mojContext, DB_NAME, null, 3);// 1 it's Database Version
DB_PATH = mojContext.getApplicationInfo().dataDir + "/databases/";
this.mContext = mojContext;
}
public void createDataBase() throws IOException
{
//If database not exists copy it from the assets
this.getReadableDatabase();
this.close();
try
{
//Copy the database from assests
copyDataBase();
Log.e(TAG, "createDatabase database created");
}
catch (IOException mIOException)
{
throw new Error("ErrorCopyingDataBase");
}
}
/**
* 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
*/
public boolean checkDataBase(){
SQLiteDatabase checkDB = null;
try{
String myPath = DB_PATH + DB_NAME;
checkDB = SQLiteDatabase.openDatabase(myPath, null, SQLiteDatabase.OPEN_READONLY);
}catch(SQLiteException e){
//database does't exist yet.
}
if(checkDB != null){
checkDB.close();
}
return checkDB != null ? true : false;
}
/*Check that the database exists here: /data/data/your package/databases/Da Name
private boolean checkDataBase()
{
File dbFile = new File(DB_PATH + DB_NAME);
//Log.v("dbFile", dbFile + " "+ dbFile.exists());
return dbFile.exists();
}
*/
//Copy the database from assets
private void copyDataBase() throws IOException
{
InputStream mInput = mContext.getAssets().open(DB_NAME);
String outFileName = DB_PATH + DB_NAME;
OutputStream mOutput = new FileOutputStream(outFileName);
byte[] mBuffer = new byte[1024];
int mLength;
while ((mLength = mInput.read(mBuffer))>0)
{
mOutput.write(mBuffer, 0, mLength);
}
mOutput.flush();
mOutput.close();
mInput.close();
}
//Open the database, so we can query it
public boolean openDataBase() throws SQLException
{
String mPath = DB_PATH + DB_NAME;
//Log.v("mPath", mPath);
mDataBase = SQLiteDatabase.openDatabase(mPath, null, SQLiteDatabase.CREATE_IF_NECESSARY);
//mDataBase = SQLiteDatabase.openDatabase(mPath, null, SQLiteDatabase.NO_LOCALIZED_COLLATORS);
return mDataBase != null;
}
#Override
public void close()
{
if(mDataBase != null)
mDataBase.close();
super.close();
}
#Override
public void onCreate(SQLiteDatabase arg0) {
}
#Override
public void onUpgrade(SQLiteDatabase arg0, int arg1, int arg2) {
try {
// delete existing?
// Copy the db from assests
copyDataBase();
Log.e(TAG, "database updated");
} catch (IOException mIOException) {
Log.e(TAG, mIOException.toString());
try {
throw mIOException;
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
There's 2 ways to do onUpgrade, depending on your app.
1)Drop all your old tables, then run onCreate. THis basically wipes out all your old data and starts over. Its a good technique if you can regenerate the old data somehow, or just don't care about it.
2)Carefully maintain a diff of your schema between each released version, and write SQL statements to make the proper changes between them- add new tables, alter existing tables to add/remove columns, etc. This is time consuming and fragile, so use this only if you need to keep data between those versions.
On the Emulator the app runs fine. On the device the moment im trying to copy my database over the application's database and execute a query the app force closes. Any idea why this could be happening? Do i have to request any kind of permissions or something in manifest for it to run?
Database.Java
public class Database extends SQLiteOpenHelper{
//The Android's default system path of your application database.
private static String DB_PATH = "/data/data/gr.BHC.www/databases/";
//Name of the Database to be created.
private static String DB_NAME = "BHCLibrary3.sqlite";
private SQLiteDatabase myDataBase;
private final Context myContext;
* Constructor
* Takes and keeps a reference of the passed context in order to access to the application assets and resources.
* #param context
*/
public Database(Context context) {
super(context, DB_NAME, null, 1);
this.myContext = context;
}
/**
* Creates a empty database on the system and rewrites it with your own database.
* */
public void createDataBase() throws IOException{
//First we check if the database already exists, Method declared later
boolean dbExist = checkDataBase();
if(dbExist){
//do nothing - database already exists
}else{
//By calling this method an empty database will be created into the default system path
//of your application so we are going to be able to overwrite that database with our database.
this.getReadableDatabase();
try {
copyDataBase(); //Method declared later
} catch (IOException e) {
throw new Error("Error copying database");
}
}
}
/**
* 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() {
//SQLiteDatabase checkdb = null;
boolean checkdb = false;
try{
String myPath = DB_PATH + DB_NAME;
File dbfile = new File(myPath);
//checkdb = SQLiteDatabase.openDatabase(myPath,null,SQLiteDatabase.OPEN_READWRITE);
checkdb = dbfile.exists();
}
catch(SQLiteException e){
System.out.println("Database doesn't exist");
}
return checkdb;
}
/**
* Copies your database from your local assets-folder to the just created empty database in the
* system folder, from where it can be accessed and handled.
* This is done by transferring byte stream.
* */
private void copyDataBase() throws IOException{
//Open your local db as the input stream
InputStream myInput = myContext.getAssets().open(DB_NAME);
// Path to the just created empty db
String outFileName = DB_PATH + DB_NAME;
//Open the empty db as the output stream
OutputStream myOutput = new FileOutputStream(outFileName);
//transfer bytes from the inputfile to the outputfile
byte[] buffer = new byte[1024];
int length;
while ((length = myInput.read(buffer))>0){
myOutput.write(buffer, 0, length);
}
//Close the streams
myOutput.flush();
myOutput.close();
myInput.close();
}
//Opening the Database
public void openDataBase() throws SQLException{
//Open the database
String myPath = DB_PATH + DB_NAME;
myDataBase = SQLiteDatabase.openDatabase(myPath, null, SQLiteDatabase.OPEN_READWRITE);
}
//Finally overriding a few methods as required
#Override
public synchronized void close() {
if(myDataBase != null)
myDataBase.close();
super.close();
}
#Override
public void onCreate(SQLiteDatabase db) {
//First we check if the database already exists, Method declared later
boolean dbExist = checkDataBase();
if(dbExist){
//do nothing - database already exists
}else{
//By calling this method an empty database will be created into the default system path
//of your application so we are going to be able to overwrite that database with our database.
this.getReadableDatabase();
try {
copyDataBase(); //Method declared later
} catch (IOException e) {
throw new Error("Error copying database");
}
}
}
#Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
}
}
And Results.java (The activity i run my query)
public class SearchResults extends ListActivity {
/** Called when the activity is first created. */
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.searchresults);
Database myDbHelper = new Database(null);
myDbHelper = new Database(this);
}
// Get the intent, verify the action and get the query
Intent intent = getIntent();
String query = intent.getStringExtra(SearchManager.QUERY);
SQLiteDatabase myDb = myDbHelper.getReadableDatabase();
//Executing our query against the server using rawQuery and getting the cursor
String select="SELECT DISTINCT b._ISBN as _id, b.BookTitle, b.Edition, b.Year, b.Pages, b.Rating, c.Category, p.Publisher, w.LastName" +
" FROM" +
" Books b" +
" JOIN Categories_Books cb ON cb._Books_ISBN = _id" +
" JOIN Categories c ON c._CategoryID = cb._Categories_CategoryID" +
" JOIN Publishers p ON p._PublisherID = b.PublisherID" +
" JOIN Writers_Books wb ON wb._Books_ISBN = _id" +
" JOIN Writers w ON w._WriterID = wb._Writers_WriterID" +
" WHERE b.BookTitle LIKE '%" + query +"%'" +
" OR c.Category LIKE '%" + query +"%'" +
" OR p.Publisher LIKE '%" + query +"%'" +
" OR w.LastName LIKE '%" + query +"%'" +
" OR _id LIKE '%" + query +"%'" +
" GROUP BY b.BookTitle";
Cursor c = myDb.rawQuery(select, null);
startManagingCursor(c);
// the desired columns to be bound
String[] columns = new String[] { "Books.BookTitle", "Publishers.Publisher" };
// the XML defined views which the data will be bound to
int[] to = new int[] { R.id.ISBN_entry, R.id.Title_entry };
//Getting results into our listview
try
{
SimpleCursorAdapter mAdapter = new SimpleCursorAdapter(this, R.layout.listlayout, c, columns, to);
this.setListAdapter(mAdapter);
}
catch( Exception e)
{
}
}
}
Help would be appreciated.
EDIT: The error im getting is : java.lang.runtimeexception: Unable to start activity Componentinfo(gr.BHC.www/gr.BHC.www.SearchResults} and then various exceptions saying table books etc dont exist.
EDIT2: I saw the exception im getting usually related with content providers but i still cant figure out why i'd get that.
I think I solved the problem. I made some changes on your codes and now it is working. Here are the codes:
SearchResults.java
public class SearchResults extends ListActivity {
/** Called when the activity is first created. */
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// setContentView(R.layout.searchresults);
DbManager myDbHelper = new DbManager(null);
myDbHelper = new DbManager(this);
// Get the intent, verify the action and get the query
myDbHelper.createNewDatabase();
try {
myDbHelper.open();
Log.d("Search Results", "database opened");
} catch (SQLException sqle) {
throw sqle;
}
Intent intent = getIntent();
String query = intent.getStringExtra(SearchManager.QUERY);
// Executing our query against the server using rawQuery and getting the
// cursor
String select = "SELECT DISTINCT b._ISBN as _id, b.BookTitle, b.Edition, b.Year, b.Pages, b.Rating, c.Category, p.Publisher, w.LastName"
+ " FROM"
+ " Books b"
+ " JOIN Categories_Books cb ON cb._Books_ISBN = _id"
+ " JOIN Categories c ON c._CategoryID = cb._Categories_CategoryID"
+ " JOIN Publishers p ON p._PublisherID = b.PublisherID"
+ " JOIN Writers_Books wb ON wb._Books_ISBN = _id"
+ " JOIN Writers w ON w._WriterID = wb._Writers_WriterID"
+ " WHERE b.BookTitle LIKE '%"
+ query
+ "%'"
+ " OR c.Category LIKE '%"
+ query
+ "%'"
+ " OR p.Publisher LIKE '%"
+ query
+ "%'"
+ " OR w.LastName LIKE '%"
+ query
+ "%'"
+ " OR _id LIKE '%"
+ query
+ "%'"
+ " GROUP BY b.BookTitle";
Cursor c = myDbHelper.rawQ(select);
startManagingCursor(c);
// the desired columns to be bound
String[] columns = new String[] { "Books.BookTitle",
"Publishers.Publisher" };
// the XML defined views which the data will be bound to
int[] to = new int[] { R.id.ISBN_entry, R.id.Title_entry };
// Getting results into our listview
try {
SimpleCursorAdapter mAdapter = new SimpleCursorAdapter(this,
R.layout.listlayout, c, columns, to);
this.setListAdapter(mAdapter);
} catch (Exception e) {
}
}
}
And your new database helper, DbManager:
DbManager.java
public class DbManager extends SQLiteOpenHelper {
private static final String DB_NAME = "BHCLibrary3.sqlite";
private static final String DB_PATH = "/data/data/gr.BHC.www/databases/";
private static final Integer DB_VERSION = 1;
private static final String TAG = "DbManager";
private final Context context;
private SQLiteDatabase db;
private DbManager dbManager;
public DbManager(Context context) {
super(context, DB_NAME, null, DB_VERSION);
this.context = context;
}
#Override
public void onCreate(SQLiteDatabase db) {
db.execSQL("CREATE TABLE 'notes' (_id integer primary key autoincrement, title text not null);");
}
#Override
public void onUpgrade(SQLiteDatabase sqLiteDatabase, int i, int i1) {
}
public DbManager open() {
dbManager = new DbManager(context);
db = dbManager.getWritableDatabase();
return this;
}
public void createNewDatabase() {
InputStream assetsDB = null;
try {
assetsDB = context.getAssets().open(DB_NAME);
OutputStream dbOut = new FileOutputStream(DB_PATH + DB_NAME);
byte[] buffer = new byte[1024];
int length;
while ((length = assetsDB.read(buffer)) > 0) {
dbOut.write(buffer, 0, length);
}
dbOut.flush();
dbOut.close();
assetsDB.close();
Log.i(TAG, "New database created...");
} catch (IOException e) {
Log.e(TAG, "Could not create new database...");
e.printStackTrace();
}
}
public Cursor rawQ(String select) {
return db.rawQuery(select, null);
}
}
Looks like the size of your database exceeds one MB. In that case you need to store it in the assets folder as .jpg and then copy it over. This is because Android places a restriction on the size of the text assets.