I'm trying to load an existing SQLite database into Android Studio. However, the database adapter is returning an empty database instead of giving me a copy of my populated database.
I've got my permissions
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
My DBAdapter
package com.example.testbed;
import android.app.SearchManager;
import android.content.ContentValues;
import android.content.Context;
import android.content.ContextWrapper;
import android.content.Intent;
import android.database.Cursor;
import android.database.SQLException;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteException;
import android.database.sqlite.SQLiteOpenHelper;
import android.database.sqlite.SQLiteQueryBuilder;
import android.util.Log;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.ArrayList;
public class DBAdapter extends SQLiteOpenHelper{
public static final String TAG = DBAdapter.class.getSimpleName();
public static int flag;
private static SQLiteDatabase db;
private static String DB_NAME = "recipes.db";
public static String TABLE_NAME = "DrinkRecipes_Loaded";
public static String DB_PATH = "";
public static String COL_WORD = "WORD";
String outFileName = "";
public static Context myContext;
private String loadedTable = "DrinkRecipes_Loaded";
private String specialtyTable = "DrinkRecipes_Specialty";
private String beautyTable = "DrinkRecipes_Beauty";
private String kidsTable = "DrinkRecipes_Kids";
private String shakesTable = "DrinkRecipes_Shakes";
public DBAdapter(Context context) {
super(context, DB_NAME, null, 1);
this.myContext = context;
ContextWrapper cw = new ContextWrapper(context);
DB_PATH = "/data/data" + BuildConfig.APPLICATION_ID + "/databases/";
Log.e(TAG, "Databasehelper: DB_PATH " + DB_PATH);
outFileName = DB_PATH + DB_NAME;
File file = new File(DB_PATH);
Log.e(TAG, "Databasehelper: " + file.exists());
if (!file.exists()) {
file.mkdir();
}
}
/**
* Creates a empty database on the system and rewrites it with your own database.
*/
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 {
checkDB = SQLiteDatabase.openDatabase(outFileName, null, SQLiteDatabase.OPEN_READONLY);
} catch (SQLiteException e) {
try {
copyDataBase();
} catch (IOException e1) {
e1.printStackTrace();
}
}
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 {
Log.i("Database",
"New database is being copied to device!");
byte[] buffer = new byte[1024];
OutputStream myOutput = null;
int length;
// Open your local db as the input stream
InputStream myInput = null;
try {
myInput = myContext.getAssets().open(DB_NAME);
// transfer bytes from the inputfile to the
// outputfile
myOutput = new FileOutputStream(DB_PATH + DB_NAME);
while ((length = myInput.read(buffer)) > 0) {
myOutput.write(buffer, 0, length);
}
myOutput.close();
myOutput.flush();
myInput.close();
Log.i("Database",
"New database has been copied to device!");
} catch (IOException e) {
e.printStackTrace();
}
}
public void openDataBase() throws SQLException {
//Open the database
String myPath = DB_PATH + DB_NAME;
db = SQLiteDatabase.openDatabase(myPath, null, SQLiteDatabase.OPEN_READONLY);
Log.e(TAG, "openDataBase: Open " + db.isOpen());
}
// we have created a new method for reading loaded recipes.
public ArrayList<recipeModal> readLoadedRecipes() {
// on below line we are creating a
// database for reading our database.
SQLiteDatabase db = this.getReadableDatabase();
// on below line we are creating a cursor with query to read data from database.
Cursor cursorRecipes = db.rawQuery("SELECT * FROM " + TABLE_NAME, null);
// on below line we are creating a new array list.
ArrayList<recipeModal> recipeModalArrayList = new ArrayList<>();
// moving our cursor to first position.
if (cursorRecipes.moveToFirst()) {
do {
// on below line we are adding the data from cursor to our array list.
recipeModalArrayList.add(new recipeModal(cursorRecipes.getString(0),
cursorRecipes.getString(1),
cursorRecipes.getString(2),
cursorRecipes.getString(3),
cursorRecipes.getString(4),
cursorRecipes.getString(5),
cursorRecipes.getString(6),
cursorRecipes.getString(7),
cursorRecipes.getString(8)));
} while (cursorRecipes.moveToNext());
// moving our cursor to next.
}
// at last closing our cursor
// and returning our array list.
cursorRecipes.close();
return recipeModalArrayList;
}
// we have created a new method for reading loaded recipes.
public ArrayList<beautyModal> readBeautyRecipes() {
// on below line we are creating a
// database for reading our database.
SQLiteDatabase db = this.getReadableDatabase();
// on below line we are creating a cursor with query to read data from database.
Cursor beautyRecipes = db.rawQuery("SELECT * FROM " + beautyTable, null);
// on below line we are creating a new array list.
ArrayList<beautyModal> beautyModalArrayList = new ArrayList<>();
// moving our cursor to first position.
if (beautyRecipes.moveToFirst()) {
do {
// on below line we are adding the data from cursor to our array list.
beautyModalArrayList.add(new beautyModal(beautyRecipes.getString(0),
beautyRecipes.getString(1),
beautyRecipes.getString(2),
beautyRecipes.getString(3),
beautyRecipes.getString(4),
beautyRecipes.getString(5)));
} while (beautyRecipes.moveToNext());
// moving our cursor to next.
}
// at last closing our cursor
// and returning our array list.
beautyRecipes.close();
return beautyModalArrayList;
}
// we have created a new method for reading loaded recipes.
public ArrayList<specialtyModal> readSpecialtyRecipes() {
// on below line we are creating a
// database for reading our database.
SQLiteDatabase db = this.getReadableDatabase();
// on below line we are creating a cursor with query to read data from database.
Cursor specialtyRecipes = db.rawQuery("SELECT * FROM " + specialtyTable, null);
// on below line we are creating a new array list.
ArrayList<specialtyModal> specialtyModalArrayList = new ArrayList<>();
// moving our cursor to first position.
if (specialtyRecipes.moveToFirst()) {
do {
// on below line we are adding the data from cursor to our array list.
specialtyModalArrayList.add(new specialtyModal(specialtyRecipes.getString(0),
specialtyRecipes.getString(1),
specialtyRecipes.getString(2),
specialtyRecipes.getString(3),
specialtyRecipes.getString(4),
specialtyRecipes.getString(5),
specialtyRecipes.getString(6),
specialtyRecipes.getString(7)));
} while (specialtyRecipes.moveToNext());
// moving our cursor to next.
}
// at last closing our cursor
// and returning our array list.
specialtyRecipes.close();
return specialtyModalArrayList;
}
// we have created a new method for reading loaded recipes.
public ArrayList<kidsModal> readKidsRecipes() {
// on below line we are creating a
// database for reading our database.
SQLiteDatabase db = this.getReadableDatabase();
// on below line we are creating a cursor with query to read data from database.
Cursor kidsRecipes = db.rawQuery("SELECT * FROM " + kidsTable, null);
// on below line we are creating a new array list.
ArrayList<kidsModal> kidsModalArrayList = new ArrayList<>();
// moving our cursor to first position.
if (kidsRecipes.moveToFirst()) {
do {
// on below line we are adding the data from cursor to our array list.
kidsModalArrayList.add(new kidsModal(kidsRecipes.getString(0),
kidsRecipes.getString(1),
kidsRecipes.getString(2),
kidsRecipes.getString(3),
kidsRecipes.getString(4)));
} while (kidsRecipes.moveToNext());
// moving our cursor to next.
}
// at last closing our cursor
// and returning our array list.
kidsRecipes.close();
return kidsModalArrayList;
}
// we have created a new method for reading loaded recipes.
public ArrayList<shakesModal> readShakesRecipes() {
// on below line we are creating a
// database for reading our database.
SQLiteDatabase db = this.getReadableDatabase();
// on below line we are creating a cursor with query to read data from database.
Cursor shakesRecipes = db.rawQuery("SELECT * FROM " + shakesTable, null);
// on below line we are creating a new array list.
ArrayList<shakesModal> shakesModalArrayList = new ArrayList<>();
// moving our cursor to first position.
if (shakesRecipes.moveToFirst()) {
do {
// on below line we are adding the data from cursor to our array list.
shakesModalArrayList.add(new shakesModal(shakesRecipes.getString(0),
shakesRecipes.getString(1),
shakesRecipes.getString(2),
shakesRecipes.getString(3),
shakesRecipes.getString(4)));
} while (shakesRecipes.moveToNext());
// moving our cursor to next.
}
// at last closing our cursor
// and returning our array list.
shakesRecipes.close();
return shakesModalArrayList;
}
#Override
public synchronized void close() {
if (db != null)
db.close();
super.close();
}
public void onCreate(SQLiteDatabase arg0) {
}
#Override
public void onUpgrade(SQLiteDatabase arg0, int arg1, int arg2) {
}
}
And when I call it, the only thing that I get is a crashing application and an empty database written to the correct path. I'm very desperate for some solutions to this problem and would appreciate any feedback.
I've tried a few different solutions that seemed promising on Google, but no luck.
EDIT: Stack trace at error
E/SQLiteLog: (1) no such table: DrinkRecipes_Loaded in "SELECT * FROM DrinkRecipes_Loaded"
D/AndroidRuntime: Shutting down VM
E/AndroidRuntime: FATAL EXCEPTION: main
Process: com.example.testbed, PID: 4732
java.lang.RuntimeException: Unable to start activity ComponentInfo{com.example.testbed/com.example.testbed.ViewRecipes}: android.database.sqlite.SQLiteException: no such table: DrinkRecipes_Loaded (code 1 SQLITE_ERROR[1]): , while compiling: SELECT * FROM DrinkRecipes_Loaded
at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:3851)
at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:4027)
at android.app.servertransaction.LaunchActivityItem.execute(LaunchActivityItem.java:85)
at android.app.servertransaction.TransactionExecutor.executeCallbacks(TransactionExecutor.java:135)
at android.app.servertransaction.TransactionExecutor.execute(TransactionExecutor.java:95)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:2336)
at android.os.Handler.dispatchMessage(Handler.java:106)
at android.os.Looper.loop(Looper.java:247)
at android.app.ActivityThread.main(ActivityThread.java:8676)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:602)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1130)
Caused by: android.database.sqlite.SQLiteException: no such table: DrinkRecipes_Loaded (code 1 SQLITE_ERROR[1]): , while compiling: SELECT * FROM DrinkRecipes_Loaded
at android.database.sqlite.SQLiteConnection.nativePrepareStatement(Native Method)
at android.database.sqlite.SQLiteConnection.acquirePreparedStatement(SQLiteConnection.java:1463)
at android.database.sqlite.SQLiteConnection.prepare(SQLiteConnection.java:901)
at android.database.sqlite.SQLiteSession.prepare(SQLiteSession.java:590)
at android.database.sqlite.SQLiteProgram.<init>(SQLiteProgram.java:62)
at android.database.sqlite.SQLiteQuery.<init>(SQLiteQuery.java:37)
at android.database.sqlite.SQLiteDirectCursorDriver.query(SQLiteDirectCursorDriver.java:46)
at android.database.sqlite.SQLiteDatabase.rawQueryWithFactory(SQLiteDatabase.java:2063)
at android.database.sqlite.SQLiteDatabase.rawQuery(SQLiteDatabase.java:2002)
at com.example.testbed.DBAdapter.readLoadedRecipes(DBAdapter.java:128)
at com.example.testbed.ViewRecipes.onCreate(ViewRecipes.java:84)
at android.app.Activity.performCreate(Activity.java:8215)
at android.app.Activity.performCreate(Activity.java:8199)
at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1309)
at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:3824)
at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:4027)
at android.app.servertransaction.LaunchActivityItem.execute(LaunchActivityItem.java:85)
at android.app.servertransaction.TransactionExecutor.executeCallbacks(TransactionExecutor.java:135)
at android.app.servertransaction.TransactionExecutor.execute(TransactionExecutor.java:95)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:2336)
at android.os.Handler.dispatchMessage(Handler.java:106)
at android.os.Looper.loop(Looper.java:247)
at android.app.ActivityThread.main(ActivityThread.java:8676)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:602)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1130)
There are a few issues with your code.
You are trying to make the database file a directory rather than making the parent the directory.
You are not catering for later android versions that default to using WAL (write-ahead logging) as opposed to journal logging by using getReabableDatabase when creating (copying the asset).
getReadableDatabase will do as it says and create the database. The copy then overwrites this (if it works).
However, in WAL mode two additional files are created for the WAL logging and populated accordingly. Then:
After the asset has been copied SQLite determines that the WAL file is not the WAL file for the copied database and therefore fails to open the potentially corrupt database.
The Android API detects this failure but does what it has been asked and creates an empty database and hence the Table not found.
Using getReadableDatabase just to circumvent the databases directory not existing is overkill/inefficient as all the processing to create a new database is undertaken.
Here's a rewrite of your code noting that:-
all of the access methods have been commented out (rather than create all the classes)
the createDatabase and checkDatabase methods have been commented out. They have been replaced by the getDatabase method.
the CopyDatabase method has been changed to return false if an io error occurs (getDatabase then throws a runTimeException)
The getDatabase method
checks to see if the database exists and returns if it does. Otherwise, it
checks to see if the parentFile (databases directory) exists,
if not then it makes the directory (and potentially other ones although they should always exist)
calls the CopyDatabase method
So the code (with much of the original code commented out) is :-
public class DBAdapter extends SQLiteOpenHelper {
public static final String TAG = DBAdapter.class.getSimpleName();
public static int flag;
private static SQLiteDatabase db;
private static String DB_NAME = "recipes.db";
public static String TABLE_NAME = "DrinkRecipes_Loaded";
public static String DB_PATH = "";
public static String COL_WORD = "WORD";
String outFileName = "";
public static Context myContext;
private String loadedTable = "DrinkRecipes_Loaded";
private String specialtyTable = "DrinkRecipes_Specialty";
private String beautyTable = "DrinkRecipes_Beauty";
private String kidsTable = "DrinkRecipes_Kids";
private String shakesTable = "DrinkRecipes_Shakes";
public DBAdapter(Context context) {
super(context, DB_NAME, null, 1);
this.myContext = context;
//ContextWrapper cw = new ContextWrapper(context); Not used
DB_PATH = context.getDatabasePath(DB_NAME).getPath(); // no need to hard code anything bar the DB name
/*
//DB_PATH = "/data/data" + BuildConfig.APPLICATION_ID + "/databases/";
Log.e(TAG, "Databasehelper: DB_PATH " + DB_PATH);
outFileName = DB_PATH + DB_NAME;
File file = new File(DB_PATH);
Log.e(TAG, "Databasehelper: " + file.exists());
if (!file.exists()) {
file.getParentFile().mkdirs(); // CHANGED you don't want the database to be a directory
}
*/
getDatabase(context);
}
/*<<<<< ADDED >>>>>*/
private void getDatabase(Context context) {
File dbFile = new File(context.getDatabasePath((DB_NAME)).getPath());
if (dbFile.exists()) return; // Database found so all done
// Otherwise ensure that the database directory exists (does not by default until later versions)
if (!dbFile.getParentFile().exists()) {
dbFile.getParentFile().mkdirs();
}
if (!copyDataBase()) {
throw new RuntimeException("Unable to copy database from the asset (check the stack-trace).");
}
}
/**
* Creates a empty database on the system and rewrites it with your own database.
*/
/* NOTE NEEDED/UNSAFE as getDatabase create a database and for later version of Android it will be in WAL mode
thus the additional WAL files are created. When the database is then opened SQLite see that the WAL files are not
for the copied database and thus the open fails. Android's API then creates an new empty database file
*/
/*
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(); // Causes issues if in WAL mode and result in database
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 {
checkDB = SQLiteDatabase.openDatabase(outFileName, null, SQLiteDatabase.OPEN_READONLY);
} catch (SQLiteException e) {
try {
copyDataBase();
} catch (IOException e1) {
e1.printStackTrace();
}
}
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 boolean /* <<<<< CHANGED SIGNATURE */ copyDataBase() {
Log.i("Database",
"New database is being copied to device!");
byte[] buffer = new byte[4096]; //Probably more efficient as default page size will be 4k
OutputStream myOutput = null;
int length;
// Open your local db as the input stream
InputStream myInput = null;
try {
myInput = myContext.getAssets().open(DB_NAME);
// transfer bytes from the inputfile to the
// outputfile
myOutput = new FileOutputStream(DB_PATH);
while ((length = myInput.read(buffer)) > 0) {
myOutput.write(buffer, 0, length);
}
myOutput.close();
myOutput.flush();
myInput.close();
Log.i("Database",
"New database has been copied to device!");
} catch (IOException e) {
e.printStackTrace();
return false;
}
return true;
}
public void openDataBase() throws SQLException {
//Open the database
String myPath = DB_PATH + DB_NAME;
db = SQLiteDatabase.openDatabase(myPath, null, SQLiteDatabase.OPEN_READONLY);
Log.e(TAG, "openDataBase: Open " + db.isOpen());
}
/*
// we have created a new method for reading loaded recipes.
public ArrayList<recipeModal> readLoadedRecipes() {
// on below line we are creating a
// database for reading our database.
SQLiteDatabase db = this.getReadableDatabase();
// on below line we are creating a cursor with query to read data from database.
Cursor cursorRecipes = db.rawQuery("SELECT * FROM " + TABLE_NAME, null);
// on below line we are creating a new array list.
ArrayList<recipeModal> recipeModalArrayList = new ArrayList<>();
// moving our cursor to first position.
if (cursorRecipes.moveToFirst()) {
do {
// on below line we are adding the data from cursor to our array list.
recipeModalArrayList.add(new recipeModal(cursorRecipes.getString(0),
cursorRecipes.getString(1),
cursorRecipes.getString(2),
cursorRecipes.getString(3),
cursorRecipes.getString(4),
cursorRecipes.getString(5),
cursorRecipes.getString(6),
cursorRecipes.getString(7),
cursorRecipes.getString(8)));
} while (cursorRecipes.moveToNext());
// moving our cursor to next.
}
// at last closing our cursor
// and returning our array list.
cursorRecipes.close();
return recipeModalArrayList;
}
// we have created a new method for reading loaded recipes.
public ArrayList<beautyModal> readBeautyRecipes() {
// on below line we are creating a
// database for reading our database.
SQLiteDatabase db = this.getReadableDatabase();
// on below line we are creating a cursor with query to read data from database.
Cursor beautyRecipes = db.rawQuery("SELECT * FROM " + beautyTable, null);
// on below line we are creating a new array list.
ArrayList<beautyModal> beautyModalArrayList = new ArrayList<>();
// moving our cursor to first position.
if (beautyRecipes.moveToFirst()) {
do {
// on below line we are adding the data from cursor to our array list.
beautyModalArrayList.add(new beautyModal(beautyRecipes.getString(0),
beautyRecipes.getString(1),
beautyRecipes.getString(2),
beautyRecipes.getString(3),
beautyRecipes.getString(4),
beautyRecipes.getString(5)));
} while (beautyRecipes.moveToNext());
// moving our cursor to next.
}
// at last closing our cursor
// and returning our array list.
beautyRecipes.close();
return beautyModalArrayList;
}
// we have created a new method for reading loaded recipes.
public ArrayList<specialtyModal> readSpecialtyRecipes() {
// on below line we are creating a
// database for reading our database.
SQLiteDatabase db = this.getReadableDatabase();
// on below line we are creating a cursor with query to read data from database.
Cursor specialtyRecipes = db.rawQuery("SELECT * FROM " + specialtyTable, null);
// on below line we are creating a new array list.
ArrayList<specialtyModal> specialtyModalArrayList = new ArrayList<>();
// moving our cursor to first position.
if (specialtyRecipes.moveToFirst()) {
do {
// on below line we are adding the data from cursor to our array list.
specialtyModalArrayList.add(new specialtyModal(specialtyRecipes.getString(0),
specialtyRecipes.getString(1),
specialtyRecipes.getString(2),
specialtyRecipes.getString(3),
specialtyRecipes.getString(4),
specialtyRecipes.getString(5),
specialtyRecipes.getString(6),
specialtyRecipes.getString(7)));
} while (specialtyRecipes.moveToNext());
// moving our cursor to next.
}
// at last closing our cursor
// and returning our array list.
specialtyRecipes.close();
return specialtyModalArrayList;
}
// we have created a new method for reading loaded recipes.
public ArrayList<kidsModal> readKidsRecipes() {
// on below line we are creating a
// database for reading our database.
SQLiteDatabase db = this.getReadableDatabase();
// on below line we are creating a cursor with query to read data from database.
Cursor kidsRecipes = db.rawQuery("SELECT * FROM " + kidsTable, null);
// on below line we are creating a new array list.
ArrayList<kidsModal> kidsModalArrayList = new ArrayList<>();
// moving our cursor to first position.
if (kidsRecipes.moveToFirst()) {
do {
// on below line we are adding the data from cursor to our array list.
kidsModalArrayList.add(new kidsModal(kidsRecipes.getString(0),
kidsRecipes.getString(1),
kidsRecipes.getString(2),
kidsRecipes.getString(3),
kidsRecipes.getString(4)));
} while (kidsRecipes.moveToNext());
// moving our cursor to next.
}
// at last closing our cursor
// and returning our array list.
kidsRecipes.close();
return kidsModalArrayList;
}
// we have created a new method for reading loaded recipes.
public ArrayList<shakesModal> readShakesRecipes() {
// on below line we are creating a
// database for reading our database.
SQLiteDatabase db = this.getReadableDatabase();
// on below line we are creating a cursor with query to read data from database.
Cursor shakesRecipes = db.rawQuery("SELECT * FROM " + shakesTable, null);
// on below line we are creating a new array list.
ArrayList<shakesModal> shakesModalArrayList = new ArrayList<>();
// moving our cursor to first position.
if (shakesRecipes.moveToFirst()) {
do {
// on below line we are adding the data from cursor to our array list.
shakesModalArrayList.add(new shakesModal(shakesRecipes.getString(0),
shakesRecipes.getString(1),
shakesRecipes.getString(2),
shakesRecipes.getString(3),
shakesRecipes.getString(4)));
} while (shakesRecipes.moveToNext());
// moving our cursor to next.
}
// at last closing our cursor
// and returning our array list.
shakesRecipes.close();
return shakesModalArrayList;
}
*/
#Override
public synchronized void close() {
if (db != null)
db.close();
super.close();
}
public void onCreate(SQLiteDatabase arg0) {
}
#Override
public void onUpgrade(SQLiteDatabase arg0, int arg1, int arg2) {
}
}
The above has been tested but using a substitute database renamed to recipes.db (NorthWind as it's pretty large) the log for a new install :-
2022-03-27 10:22:11.223 I/Database: New database is being copied to device!
2022-03-27 10:22:11.455 I/Database: New database has been copied to device!
AppInspection confirms that the database has been successfully copied (albeit not your database):-
Likewise Device File Explorer shows :-
Related
I have a recyclerview with a list of plants, and when you click on a plant it brings up info about it and there is a delete button to remove the plant from the list and database. When this is clicked, the plant disappears from the recyclerview like expected. If I stop the emulator and then run it again without closing Android studio, this change persists, but if I restart Android studio the deleted row is back. Also, when I look at the plants.db file using DB Browser I see no change. Any help is appreciated, thank you!
Here is my delete function from DatabaseHelper.java (full code below:)
public void deleteFromShelf(String get_ID)
{
SQLiteDatabase db = this.getWritableDatabase();
db.beginTransaction();
String deleteString = "DELETE FROM PLANTS_OWNED_TABLE WHERE PLANT_ID ='"+get_ID+"'";
db.execSQL(deleteString);
db.setTransactionSuccessful();
// db.execSQL("DROP TABLE IF EXISTS PLANTS_OWNED_TABLE");
db.endTransaction();
Toast.makeText(myContext, "ere i am", Toast.LENGTH_SHORT).show();
}
And here is the adapter I'm calling it from (the removeButton function is toward the end):
public class ShelfFragment2 extends Fragment {
private Plant plant;
private RecyclerView rvShelf2;
private ShelfAdapter2 adShelf2;
private RecyclerView.LayoutManager lmShelf2;
DataBaseHelper dpHelper;
public ShelfFragment2() {
}
#Override
public void onCreate(#Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
dpHelper = new DataBaseHelper(getActivity());
dpHelper.initializeDataBase();
}
#Nullable
#Override
public View onCreateView(#NonNull LayoutInflater inflater, #Nullable ViewGroup container, #Nullable Bundle savedInstanceState) {
View v = inflater.inflate(R.layout.fragment_shelf2, container, false);
ImageView ivPlant= v.findViewById(R.id.ivPlantW);
TextView tvName = v.findViewById(R.id.tvNameW);
TextView tvSName = v.findViewById(R.id.tvSNameW);
Bundle bundle = getArguments();
plant = bundle.getParcelable("Selected");
Log.i("here", plant.getName());
tvName.setText(plant.getName());
tvSName.setText(plant.getScientific_Name());
rvShelf2 = v.findViewById(R.id.rvShelf2);
rvShelf2.setHasFixedSize(true);
lmShelf2 = new LinearLayoutManager(v.getContext());
adShelf2 = new ShelfAdapter2(getContext(), plant);
rvShelf2.setLayoutManager(lmShelf2);
rvShelf2.setAdapter(adShelf2);
Button removeButton = (Button) v.findViewById(R.id.btRemovePlant);
removeButton.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
ShelfFragment shelfFragment = new ShelfFragment();
dpHelper.deleteFromShelf(String.valueOf(plant.getID()));
Toast.makeText(getContext(), "Removed from shelf!", Toast.LENGTH_SHORT).show();
getActivity().getSupportFragmentManager().beginTransaction().replace(R.id.flContainer, shelfFragment)
.addToBackStack(null).commit();
}
});
return v;
};
public void setArguments(Context context, Bundle bundle) {
}
}
Entire code of DatabaseHelper.java:
package com.example.plantapp;
import android.content.Context;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.util.Log;
import android.widget.Toast;
import androidx.annotation.Nullable;
import com.example.plantapp.fragments.SearchFragment;
import com.example.plantapp.objects.Plant;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
public class DataBaseHelper extends SQLiteOpenHelper {
private static final String DATABASE_NAME = "plants.db";
private static final String DATABASE_DIRECTORY = "/data/data/com.example.plantapp/" +
"databases/";
private static String DATABASE_PATH = DATABASE_DIRECTORY + DATABASE_NAME;
private static String OLD_DATABASE_PATH = DATABASE_DIRECTORY + "old_" + DATABASE_NAME;
private final Context myContext;
private boolean createDatabase = false;
private boolean upgradeDatabase = false;
// PLANT_TABLE
public static final String PLANT_TABLE = "PLANT_TABLE";
public static final String COLUMN_PLANT_ID = "ID";
public static final String COLUMN_PLANT_NAME = "NAME";
public static final String COLUMN_PLANT_SCI_NAME = "SCIENTIFIC_NAME";
public static final String COLUMN_PLANT_LIGHT = "LIGHT";
public static final String COLUMN_PLANT_WATER = "WATER";
public static final String COLUMN_PLANT_FERTILIZER = "FERTILIZER";
public static final String COLUMN_PLANT_TEMPERATURE = "TEMPERATURE";
public static final String COLUMN_PLANT_HUMIDITY = "HUMIDITY";
public static final String COLUMN_PLANT_FLOWERING = "FLOWERING";
public static final String COLUMN_PLANT_PESTS = "PESTS";
public static final String COLUMN_PLANT_DISEASES = "DISEASES";
public static final String COLUMN_PLANT_SOIL = "SOIL";
public static final String COLUMN_PLANT_POT_SIZE = "POT_SIZE";
public static final String COLUMN_PLANT_PRUNING = "PRUNING";
public static final String COLUMN_PLANT_PROPAGATION = "PROPAGATION";
public static final String COLUMN_PLANT_POISON = "POISONOUS_PLANT_INFO";
// USER_TABLE - keeping this table in case we want to provide a more "personalized"
// experience by using the user's name
public static final String USER_TABLE = "USER_TABLE";
public static final String COLUMN_USER_NAME = "USER_NAME";
// WISHLIST_TABLE
public static final String WISHLIST_TABLE = "WISHLIST_TABLE";
public static final String COLUMN_WISHLIST_ID = "WISHLIST_ID";
public static final String COLUMN_WISHLIST_PLANT_ID = "PLANT_ID"; // references PLANT_TABLE
// PLANTS_OWNED_TABLE
public static final String PLANTS_OWNED_TABLE = "PLANTS_OWNED_TABLE";
public static final String COLUMN_PLANTS_OWNED_ID = "PLANTS_OWNED_ID";
public static final String COLUMN_PLANTS_OWNED_PLANT_ID = "PLANT_ID"; // references PLANT_TABLE
/*
* Constructor takes and keeps a reference of the passed context in order to
* access the application assets and resources
*
* */
public DataBaseHelper(#Nullable Context context) {
super(context, DATABASE_NAME, null, 1);
myContext = context;
// Get the path of the database that is based on the context.
DATABASE_PATH = myContext.getDatabasePath(DATABASE_NAME).getAbsolutePath();
}
/*
* Upgrade the database in internal storage if it exists but is not current.
* Create a new empty database in internal storage if it does not exist.
* */
public void initializeDataBase() {
/*
* Creates or updates the database in internal storage if it is needed
* before opening the database. In all cases opening the database copies
* the database in internal storage to the cache.
* */
getWritableDatabase();
if (createDatabase) {
/*
* If the database is created by the copy method, then the creation
* code needs to go here. This method consists of copying the new
* database from assets into internal storage and then caching it.
* */
try {
/*
* Write over the empty data that created in internal
* storage with the one in assets and then cache it.
* */
copyDataBase();
} catch (IOException e) {
throw new Error("Error copying database");
}
} else if (upgradeDatabase) {
/*
* If the database is upgraded by the copy and reload method, then
* the upgrade code needs to go here. This method consists of
* renaming the old database in internal storage, create an empty
* new database in internal storage, copying the database from
* assets to the new database in internal storage, caching the new
* database from internal storage, loading the data from the old
* database into the new database in the cache and then deleting the
* old database from internal storage.
* */
try {
FileHelper.copyFile(DATABASE_PATH, OLD_DATABASE_PATH);
copyDataBase();
SQLiteDatabase old_db = SQLiteDatabase.openDatabase(OLD_DATABASE_PATH, null, SQLiteDatabase.OPEN_READWRITE);
SQLiteDatabase new_db = SQLiteDatabase.openDatabase(DATABASE_PATH, null, SQLiteDatabase.OPEN_READWRITE);
/*
* Add code to load data into the new database from the old
* database and then delete the old database from internal
* storage after all data has been transferred
* */
} catch (IOException e) {
throw new Error("Error copying database");
}
}
}
/*
* 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 {
/*
* Close SQLiteOpenHelper so it will commit the created empty database
* to internal storage.
* */
close();
/*
* Open the database in the assets folder as the input stream.
*/
InputStream myInput = myContext.getAssets().open(DATABASE_NAME);
/*
* Open the empty db in internal storage as the output stream.
*/
OutputStream myOutput = new FileOutputStream(DATABASE_PATH);
/*
* Copy over the empty db in internal storage with the database in the
* assets folder.
* */
FileHelper.copyFile(myInput, myOutput);
/*
* Access the copied database so SQLiteHelper will cache it and mark it
* as created.
* */
getWritableDatabase().close();
}
/*
* This is where the creation of tables and the initial population of the
* tables should happen, if a database is being created from scratch instead
* of being copied from the application package assets. Copying a database
* from the application package assets to internal storage inside this
* method will result in a corrupted database.
* NOTE: This method is normally only called when a database has not already
* been created. when the database has been copied, then this method is
* called the first time a reference to the database is retrieved after the
* database is copied since the database last cached by SQLiteOpenHelper is
* different than the database in internal storage.
* */
#Override
public void onCreate(SQLiteDatabase db) {
/*
* Signal that a new database needs to be copied. The copy process must
* be performed after the database in the cache has been closed causing
* it to be committed to internal storage. Otherwise the database in
* internal storage will not have the same creation timestamp as the one
* in the cache causing the database in internal storage to be marked as
* corrupted.
* */
createDatabase = true;
/*String createTableStatement = "CREATE TABLE " + PLANT_TABLE + " (" + COLUMN_PLANT_ID + " INTEGER PRIMARY KEY," +
COLUMN_PLANT_NAME + " TEXT, " + COLUMN_PLANT_SCI_NAME + " TEXT, " +
COLUMN_PLANT_LIGHT + " TEXT, " + COLUMN_PLANT_WATER + " TEXT, " +
COLUMN_PLANT_FERTILIZER + " TEXT, " + COLUMN_PLANT_TEMPERATURE + " TEXT, " +
COLUMN_PLANT_HUMIDITY + " TEXT, " + COLUMN_PLANT_FLOWERING + " TEXT, " +
COLUMN_PLANT_PESTS + " TEXT, " + COLUMN_PLANT_DISEASES + " TEXT, " +
COLUMN_PLANT_SOIL + " TEXT, " + COLUMN_PLANT_POT_SIZE + " TEXT, " +
COLUMN_PLANT_PRUNING + " TEXT, " + COLUMN_PLANT_PROPAGATION + " TEXT, " +
COLUMN_PLANT_POISON + " TEXT)";
db.execSQL(createTableStatement);*/
}
/*
* Called only if version number was changed and the database has already
* been created. Copying a database from the application package assets to
* the internal data system inside this method will result in a corrupted
* database in the internal data system.
* */
#Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
/*
* Signal that the database needs to be upgraded for the copy method of
* creation. The copy process must be performed after the database has
* been opened or the database will be corrupted.
* */
upgradeDatabase = true;
/*
* Code to update the database via execution of sql statements goes
* here.
* */
}
/*
* Called everytime the database is opened by getReadableDatabase or
* getWritableDatabase. This is called after onCreate or onUpgrade is
* called.
* */
#Override
public void onOpen(SQLiteDatabase db) {
super.onOpen(db);
}
public List<String> getPlantNamesLike(String whatUserTyped) {
//List<PlantModel> returnList = new ArrayList<>();
List<String> returnList = new ArrayList<>();
String[] nameArg = {whatUserTyped + "%"};
// get data from the database
String queryString = "SELECT " + COLUMN_PLANT_NAME + " FROM " + PLANT_TABLE +
" WHERE " + COLUMN_PLANT_NAME + " LIKE ?";
SQLiteDatabase db = this.getReadableDatabase();
Cursor cursor = db.rawQuery(queryString, nameArg);
if (cursor.moveToFirst()) {
do{
String plantName = cursor.getString(0);
//PlantModel newPlant = new PlantModel(plantName);
//returnList.add(newPlant);
returnList.add(plantName);
} while (cursor.moveToNext());
}
else {
// failure. do not add anything to list
returnList.add("No matching plant found");
}
cursor.close();
db.close();
return returnList;
}
public List<Plant> getPlants()
{
List<Plant> plants = new ArrayList<>();
String queryString = "SELECT * FROM " + PLANT_TABLE;
SQLiteDatabase db = this.getReadableDatabase();
Cursor cursor = db.rawQuery(queryString, null);
if(cursor.moveToFirst()) {
do {
Plant currentPlant = new Plant();
currentPlant.setID(cursor.getInt(0));
currentPlant.setName(cursor.getString(1));
currentPlant.setScientific_Name(cursor.getString(2));
currentPlant.setLight(cursor.getString(3));
currentPlant.setWater(cursor.getString(4));
currentPlant.setFertilizer(cursor.getString(5));
currentPlant.setTemperature(cursor.getString(6));
currentPlant.setHumidity(cursor.getString(7));
currentPlant.setFlowering(cursor.getString(8));
currentPlant.setPests(cursor.getString(9));
currentPlant.setDiseases(cursor.getString(10));
currentPlant.setSoil(cursor.getString(11));
currentPlant.setPot_size(cursor.getString(12));
currentPlant.setPruning(cursor.getString(13));
currentPlant.setPropagation(cursor.getString(14));
currentPlant.setPoisonous_plant_info(cursor.getString(15));
plants.add(currentPlant);
} while (cursor.moveToNext());
}
return plants;
}
public List<Plant> getOwnedPlants()
{
List<Plant> ownedPlants = new ArrayList<>();
String queryString = "SELECT * FROM PLANT_TABLE INNER JOIN PLANTS_OWNED_TABLE ON PLANTS_OWNED_TABLE.PLANT_ID = PLANT_TABLE.ID";
SQLiteDatabase db = this.getReadableDatabase();
Cursor cursor = db.rawQuery(queryString, null);
android.util.Log.d("CURSOR", String.format("size = %d", cursor.getCount()));
if(cursor.moveToFirst()) {
do {
Plant currentPlant = new Plant();
currentPlant.setID(cursor.getInt(0));
currentPlant.setName(cursor.getString(1));
currentPlant.setScientific_Name(cursor.getString(2));
currentPlant.setLight(cursor.getString(3));
currentPlant.setWater(cursor.getString(4));
currentPlant.setFertilizer(cursor.getString(5));
currentPlant.setTemperature(cursor.getString(6));
currentPlant.setHumidity(cursor.getString(7));
currentPlant.setFlowering(cursor.getString(8));
currentPlant.setPests(cursor.getString(9));
currentPlant.setDiseases(cursor.getString(10));
currentPlant.setSoil(cursor.getString(11));
currentPlant.setPot_size(cursor.getString(12));
currentPlant.setPruning(cursor.getString(13));
currentPlant.setPropagation(cursor.getString(14));
currentPlant.setPoisonous_plant_info(cursor.getString(15));
ownedPlants.add(currentPlant);
} while (cursor.moveToNext());
}
return ownedPlants;
}
public List<Plant> getWishlistPlants()
{
List<Plant> wishlistPlants = new ArrayList<>();
String queryString = "SELECT * FROM PLANT_TABLE INNER JOIN WISHLIST_TABLE ON WISHLIST_TABLE.PLANT_ID = PLANT_TABLE.ID";
SQLiteDatabase db = this.getReadableDatabase();
Cursor cursor = db.rawQuery(queryString, null);
android.util.Log.d("CURSOR", String.format("size = %d", cursor.getCount()));
if(cursor.moveToFirst()) {
do {
// TODO: make this code less repetitive
Plant currentPlant = new Plant();
currentPlant.setID(cursor.getInt(0));
currentPlant.setName(cursor.getString(1));
currentPlant.setScientific_Name(cursor.getString(2));
currentPlant.setLight(cursor.getString(3));
currentPlant.setWater(cursor.getString(4));
currentPlant.setFertilizer(cursor.getString(5));
currentPlant.setTemperature(cursor.getString(6));
currentPlant.setHumidity(cursor.getString(7));
currentPlant.setFlowering(cursor.getString(8));
currentPlant.setPests(cursor.getString(9));
currentPlant.setDiseases(cursor.getString(10));
currentPlant.setSoil(cursor.getString(11));
currentPlant.setPot_size(cursor.getString(12));
currentPlant.setPruning(cursor.getString(13));
currentPlant.setPropagation(cursor.getString(14));
currentPlant.setPoisonous_plant_info(cursor.getString(15));
wishlistPlants.add(currentPlant);
} while (cursor.moveToNext());
}
return wishlistPlants;
}
public void deleteFromShelf(String get_ID)
{
SQLiteDatabase db = this.getWritableDatabase();
db.beginTransaction();
String deleteString = "DELETE FROM PLANTS_OWNED_TABLE WHERE PLANT_ID ='"+get_ID+"'";
db.execSQL(deleteString);
db.setTransactionSuccessful();
// db.execSQL("DROP TABLE IF EXISTS PLANTS_OWNED_TABLE");
db.endTransaction();
Toast.makeText(myContext, "ere i am", Toast.LENGTH_SHORT).show();
}
}
I am trying to create an app. App is opening in Emulator. But when I am trying to open the app in real device, it is crashing. I tried many solutions regarding my problem. but I am unable to solve my problem. I added DatabaseHelper, Logcat. I added android.permission in AndroidManifest.xml but failed to get solution. App is not opening. Application terminated. not opening. Please suggest to solve this problem.
I deleted the app before running the app in real device. But problem remains same.
DatabaseHelper.java
public class DatabaseHelper extends SQLiteOpenHelper {
private static DatabaseHelper mInstance = null;
String DB_PATH = null;
private static String DB_NAME = "kriyayoga.db";
private static SQLiteDatabase myDataBase;
private final Context myContext;
private static final int DATABASE_VERSION = 1;
public static synchronized DatabaseHelper getInstance(Context context) {
if (mInstance == null) {
mInstance = new DatabaseHelper(context.getApplicationContext());
}
return mInstance;
}
public DatabaseHelper(Context context) {
super(context, DB_NAME, null, DATABASE_VERSION);
this.myContext = context;
this.DB_PATH = "/data/data/" + context.getPackageName() + "/" + "databases/";
//this.DB_PATH = this.myContext.getDatabasePath(DB_NAME).getAbsolutePath();
Log.e("Path 1", DB_PATH);
}
public void createDataBase() throws IOException {
boolean dbExist = checkDataBase();
if (dbExist) {
} else {
this.getReadableDatabase();
try {
copyDataBase();
} catch (IOException e) {
throw new Error("Error copying database");
}
}
}
private boolean checkDataBase() {
File dbFile = myContext.getDatabasePath(DB_NAME);
return dbFile.exists();
}
private void copyDataBase() throws IOException {
InputStream myInput = myContext.getAssets().open(DB_NAME);
String outFileName = DB_PATH + DB_NAME;
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();
}
public void openDataBase() throws SQLException {
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) {
if (newVersion > oldVersion)
try {
copyDataBase();
} catch (IOException e) {
e.printStackTrace();
}
}
#Override
public void onOpen(SQLiteDatabase db) {
onCreate(db);
}
}
Logcat Problem shows at ListNavAdapter.java
E/SQLiteLog: (1) no such table: yoga
D/AndroidRuntime: Shutting down VM
E/AndroidRuntime: FATAL EXCEPTION: main
android.database.sqlite.SQLiteException: no such table: yoga (code 1 SQLITE_ERROR): , while compiling: SELECT heading FROM yoga ORDER BY _id
at android.database.sqlite.SQLiteConnection.nativePrepareStatement(Native Method)
at android.database.sqlite.SQLiteConnection.acquirePreparedStatement(SQLiteConnection.java:946)
at android.database.sqlite.SQLiteConnection.prepare(SQLiteConnection.java:527)
at android.database.sqlite.SQLiteSession.prepare(SQLiteSession.java:588)
at android.database.sqlite.SQLiteProgram.<init>(SQLiteProgram.java:58)
at android.database.sqlite.SQLiteQuery.<init>(SQLiteQuery.java:37)
at android.database.sqlite.SQLiteDirectCursorDriver.query(SQLiteDirectCursorDriver.java:46)
at android.database.sqlite.SQLiteDatabase.rawQueryWithFactory(SQLiteDatabase.java:1408)
at android.database.sqlite.SQLiteDatabase.rawQuery(SQLiteDatabase.java:1347)
at com.priyanka.kriyayoga.ListNavAdapter.onViewCreated(ListNavAdapter.java:81)
at android.support.v4.app.FragmentManagerImpl.moveToState(FragmentManager.java:1471)
at android.support.v4.app.FragmentManagerImpl.moveFragmentToExpectedState(FragmentManager.java:1784)
at android.support.v4.app.FragmentManagerImpl.moveToState(FragmentManager.java:1852)
at android.support.v4.app.BackStackRecord.executeOps(BackStackRecord.java:802)
at android.support.v4.app.FragmentManagerImpl.executeOps(FragmentManager.java:2625)
at android.support.v4.app.FragmentManagerImpl.executeOpsTogether(FragmentManager.java:2411)
at android.support.v4.app.FragmentManagerImpl.removeRedundantOperationsAndExecute(FragmentManager.java:2366)
at android.support.v4.app.FragmentManagerImpl.execPendingActions(FragmentManager.java:2273)
at android.support.v4.app.FragmentManagerImpl.dispatchStateChange(FragmentManager.java:3273)
at android.support.v4.app.FragmentManagerImpl.dispatchActivityCreated(FragmentManager.java:3229)
at android.support.v4.app.FragmentController.dispatchActivityCreated(FragmentController.java:201)
at android.support.v4.app.FragmentActivity.onStart(FragmentActivity.java:620)
at android.support.v7.app.AppCompatActivity.onStart(AppCompatActivity.java:178)
at android.app.Instrumentation.callActivityOnStart(Instrumentation.java:1395)
at android.app.Activity.performStart(Activity.java:7293)
at android.app.ActivityThread.handleStartActivity(ActivityThread.java:3196)
at android.app.servertransaction.TransactionExecutor.performLifecycleSequence(TransactionExecutor.java:180)
at android.app.servertransaction.TransactionExecutor.cycleToPath(TransactionExecutor.java:165)
at android.app.servertransaction.TransactionExecutor.executeLifecycleState(TransactionExecutor.java:142)
at android.app.servertransaction.TransactionExecutor.execute(TransactionExecutor.java:70)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:2011)
at android.os.Handler.dispatchMessage(Handler.java:106)
at android.os.Looper.loop(Looper.java:224)
at android.app.ActivityThread.main(ActivityThread.java:7135)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:537)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:876)
When I am running the app in real device it says runtime exception
App is experiencing crashes.
in AndroidManifest.xml I added
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
In ListNavAdapter I used to get data
SQLiteDatabase sqLiteDatabase = DatabaseHelper.getInstance(this.getContext()).getReadableDatabase();
Cursor cursor1 = sqLiteDatabase.rawQuery("SELECT heading FROM yoga ORDER BY _id", new String[]{});
Your issue is very likely that the device on which you are trying this is Android 9+. With 9+ the default was changed from journal mode to WAL (Write Ahead Logging).
Using getWritableDatabase (or getReadableDatabase, they both open the database as writable if the database can be opened as writable) before the copy results in the -wal and -shm files being created. When the database is copied the -wal and -shm files remain. When the copied database is opened, the -wal file is opened by SQLite and a discrepancy is found as the -wal file belongs to the overwritten database file NOT the copied database file. In order to provide a usable database, the corrupt database file is then overwritten with a valid and therefore empty database file and hence why the table(s) appear to have vanished.
Historically (I believe as I can see no other reason) getWritableDatabase was used to get-around a cannot open file ENONENT error, this because when a App is first run, the databases directory does not exists in the data/data/package_name directory (so the copy of the file fails).
The following changes will circumvent this issue by making the databases directory (which is more efficient) when needed.
First change :-
private boolean checkDataBase() {
File dbFile = myContext.getDatabasePath(DB_NAME);
return dbFile.exists();
}
to be :-
private boolean checkDataBase() {
File dbFile = myContext.getDatabasePath(DB_NAME);
if (dbFile.exists()) return true;
if (!dbFile.getParentFile().exists()) {
dbfile.getParentFile().mkdirs();
}
return false;
}
And then remove or comment out the line this.getReadableDatabase(), in the createDataBase method.
Note the above code is in-principle code, it has not been run or tested and may therefore contain errors.
I try to use a sqlite database but the problem shows "no such table", the code works on some devices and some show that message.
class DatabaseHelper extends SQLiteOpenHelper {
private final String mDatabaseName;
private final Context mContext;
private final String mPath;
DatabaseHelper(Context context, String database, String path){
super(context,database,null,1);
this.mContext=context;
this.mDatabaseName=database;
this.mPath=path;
_createDatabase();
}
private void _createDatabase() {
if(_checkDatabase()){
return;
}
getReadableDatabase();
try {
_copyDatabase();
}catch (Exception e){
}
}
private void _copyDatabase() throws IOException {
InputStream inputStream = mContext.getAssets().open(mDatabaseName);
FileOutputStream fileOutputStream = new FileOutputStream(mPath+mDatabaseName);
byte[] bytes = new byte[1024];
do{
int n;
if((n=inputStream.read(bytes)) <= 0){
fileOutputStream.flush();
fileOutputStream.close();
return;
}
fileOutputStream.write(bytes,0,n);
}while (true);
}
private boolean _checkDatabase() {
return mContext.getDatabasePath(mDatabaseName).exists();
}
#Override
public void onCreate(SQLiteDatabase db) {
}
#Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
}
}
And database adapter look this
class DatabaseAdapter
{
private SQLiteDatabase database;
private DatabaseHelper databaseHelper;
DatabaseAdapter(Context context, String database, String path){
databaseHelper = new DatabaseHelper(context, database, path);
}
void open(){
try{
database = databaseHelper.getReadableDatabase();
}
catch (SQLiteException e)
{
database = databaseHelper.getReadableDatabase();
}
}
boolean isOpened()
{
return this.database != null && this.database.isOpen();
}
Cursor _get_rows()
{
Cursor cursor = database.rawQuery("SELECT * FROM rows ORDER BY RANDOM() LIMIT 4;", null);
cursor.moveToFirst();
return cursor;
}
}
And database controller look this
public class DatabaseController {
private static DatabaseAdapter databaseAdapter;
public static Cursor _get_rows()
{
if(!databaseAdapter.isOpened()){
databaseAdapter.open();
}
return databaseAdapter._get_rows();
}
public static void initilization(Context activity) {
String dbName= "data.db";
databaseAdapter = new DatabaseAdapter(activity.getApplicationContext(),dbName,"/data/data/"+activity.getApplicationInfo().packageName+"/databases/");
}
}
i use in activity like this
DatabaseController.initilization(this);
Cursor c = DatabaseController._get_rows();
I could not find a solution to this problem, the database was already copied to the entire directory
I suspect that the issues is that you are trapping an exception in :-
private void _createDatabase() {
if(_checkDatabase()){
return;
}
getReadableDatabase();
try {
_copyDatabase();
}catch (Exception e){
}
}
So processing continues and an empty database is created and hence no table.
The root cause might be that the databases directory might not exist. You need to look at the exception that has been trapped to determine the exact error. e.g. e.printStackTrace();
The following is how you could create the databases directory (to resolve the ENOENT (No such file or directory) exception that may be the issue) :-
e.g.
private void _copyDatabase() throws IOException {
File dir = new File(mPath); //<<<<<<<<<< ADDED
if(!dir.exists()) { //<<<<<<<<<< ADDED
dir.mkdirs(); //<<<<<<<<<< ADDED
} //<<<<<<<<<< ADDED
InputStream inputStream = mContext.getAssets().open(mDatabaseName);
FileOutputStream fileOutputStream = new FileOutputStream(mPath+mDatabaseName);
byte[] bytes = new byte[1024];
do{
int n;
if((n=inputStream.read(bytes)) <= 0){
fileOutputStream.flush();
fileOutputStream.close();
return;
}
fileOutputStream.write(bytes,0,n);
}while (true);
}
It is also inadvisable to hard code paths e.g. using :-
databaseAdapter = new DatabaseAdapter(activity.getApplicationContext(),dbName,"/data/data/"+activity.getApplicationInfo().packageName+"/databases/");
It's better to get the path using the Context's getDatabasePath method.
Note the code is in-principle code, it has not been tested or run and may therefore contain errors.
Note on the devices on which you have issues you will need to delete the database (Clear the App's data or uninstall the App) before running any amended code.
i need to retrieve data from sqlite .and retrieved data should be displayed in a gridfieldmanager layout.i have done the below code please help me how to display data from database over the myscreen.
SQLManager screen
public class SQLManager {
static String snapsdata;
private static String DB_NAME = "employee_details.db3";
private Database _db;
public SQLManager() throws Exception {
// Determine if an SDCard is present
boolean sdCardPresent = false;
String root = null;
Enumeration e = FileSystemRegistry.listRoots();
while (e.hasMoreElements()) {
root = (String) e.nextElement();
if (root.equalsIgnoreCase("sdcard/")) {
sdCardPresent = true;
}
}
if (!sdCardPresent) {
UiApplication.getUiApplication().invokeLater(new Runnable() {
public void run() {
Dialog.alert("This application requires an SD card to be
present.");
System.exit(0);
}
});
} else {
String dbLocation = "/SDCard/databases/sample/";
// Create URI
URI uri = URI.create(dbLocation + DB_NAME);
// Open or create a plain text database. This will create the
// directory and file defined by the URI (if they do not already
// exist).
Database db = DatabaseFactory.openOrCreate(uri,
new DatabaseSecurityOptions(false));
// Close the database in case it is blank and we need to write to
// the file
db.close();
//Dialog.alert("db");
// Open a connection to the database file
FileConnection fileConnection = (FileConnection) Connector
.open("file://" + dbLocation + DB_NAME);
// If the file is blank, copy the pre-defined database from this
// module to the SDCard.
if (fileConnection.exists() && fileConnection.fileSize() == 0) {
readAndWriteDatabaseFile(fileConnection);
//Dialog.alert("db1");
}
// Open the database
db = DatabaseFactory.open(uri);
_db = db;
}
}
/**
* Copies the pre-defined database from this module to the location
* specified by the fileConnection argument.
*
* #param fileConnection
* File connection to the database location
*/
public void readAndWriteDatabaseFile(FileConnection fileConnection)
throws IOException {
OutputStream outputStream = null;
InputStream inputStream = null;
// Open an input stream to the pre-defined encrypted database bundled
// within this module.
inputStream = getClass().getResourceAsStream("/" + DB_NAME);
//Dialog.alert("db" + inputStream);
// Open an output stream to the newly created file
outputStream = (OutputStream) fileConnection.openOutputStream();
// Read data from the input stream and write the data to the
// output stream.
byte[] data = new byte[1024 * 5];
int length = 0;
while (-1 != (length = inputStream.read(data))) {
outputStream.write(data, 0, length);
}
// Close the connections
if (fileConnection != null) {
fileConnection.close();
}
if (outputStream != null) {
outputStream.close();
}
if (inputStream != null) {
inputStream.close();
}
}
/**
* Constructs a new SQLManager object
*
* #param db
* Database to manage
*/
public SQLManager(Database db) {
_db = db;
}
/**
* Closes the database
*/
void closeDB() {
try {
_db.close();
} catch (DatabaseException dbe) {
}
}
public void SaveEmployeeInformation(int employeeid, String employee_name,
String position, int salary){
//return productinfo;
Statement st;
try {
st = _db.createStatement("INSERT INTO
employee_details(employee_id,employee_name,position,salary) VALUES (?, ?,
?, ?)");
try
{
st.prepare();
Object[] bindParams = {new Integer(employeeid), new
String(employee_name), new String(position), new Integer(salary)};
long rowID = st.executeInsert(bindParams);
// Dialog.alert("ro "+rowID);
}
finally
{
st.close();
closeDB();
}
} catch (DatabaseException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
public Vector getEmployeeInformation(){
Vector productinfo = new Vector();
try {
Statement statement = null;
// Read in all records from the Category table
statement = _db
.createStatement("select MAX(employee_id) as
employeeReportId from employee_details");
// ProjectImagesID Project_id,ImagePath,ImageDescription
statement.prepare();
// statement.setCursorBufferSize(10);
Cursor cursor = statement.getCursor();
Employeelist productdatas;
Row row;
// Iterate through the result set. For each row, create a new
// Category object and add it to the hash table.
while (cursor.next()) {
row = cursor.getRow();
productdatas = new Employeelist(row.getInteger(0));
productinfo.addElement(productdatas);
}
// Dialog.alert(""+productinfo.size());
statement.close();
cursor.close();
} catch (DatabaseException dbe) {
Dialog.alert("SD PRODUCTINFP " + dbe.toString());
} catch (DataTypeException e) {
Dialog.alert("PRODUCTINFP " + e.toString());
// TODO Auto-generated catch block
e.printStackTrace();
} finally {
closeDB();
}
return productinfo;
}
}
myscreen screen
public final class MyScreen extends MainScreen
{
/**
* Creates a new MyScreen object
*/
public MyScreen()
{
// Set the displayed title of the screen
setTitle("MyTitle");
// int reportid = 0;
try {
SQLManager emp = new SQLManager();
emp.SaveEmployeeInformation(7,"farah","developer", 4000);
emp.getEmployeeInformation();
} catch (Exception e)
{// TODO Auto-generated catch block
e.printStackTrace();
}
int row = 5;
GridFieldManager dfm = new GridFieldManager(row,3, 0);
add(dfm);
}
}
First try to acheive the successul set up and retreiving of data from SQlite Db. .Then with retrieved data,try to use it in grid field Manager.
Please write exact problem in code which helps us to solvethe problem.
I'm using the code below to copy the data containing files and folders with subfolders in them to data directory of my application but I get exception telling the destination location does not exist however FileOutputStream method should make the destination folders according to javadoc for this method:
Constructs a new FileOutputStream that writes to path. The file will be truncated if it exists, and created if it doesn't exist.
the code is :
private void copyFileOrDir(String path) {
AssetManager assetManager = this.getAssets();
String assets[] = null;
try {
assets = assetManager.list(path);
if (assets.length == 0) {
copyFile(path);
} else {
String fullPath = "/data/data/" + this.getPackageName() + "/" + path;
File dir = new File(fullPath);
if (!dir.exists())
dir.mkdir();
File innerDir;
for (int i = 0; i < assets.length; ++i) {
copyFileOrDir(path + "/" + assets[i]);
}
}
} catch (IOException ex) {
Log.e("tag", "I/O Exception", ex);
}
}
private void copyFile(String filename) {
AssetManager assetManager = this.getAssets();
InputStream in = null;
OutputStream out = null;
try {
in = assetManager.open(filename);
String newFileName = "/data/data/" + this.getPackageName() + "/" + filename;
out = new FileOutputStream(newFileName);
byte[] buffer = new byte[1024];
int read;
while ((read = in.read(buffer)) != -1) {
out.write(buffer, 0, read);
}
in.close();
in = null;
out.flush();
out.close();
out = null;
} catch (Exception e) {
//getting exception here...
Log.e("mgh", e.getMessage());
}
}
logcat errors are like below for any folder :
09-06 15:14:20.981: E/mgh(19262): /data/data/ir.example.sampleapplication/pzl/ui/css/main.css: open failed: ENOENT (No such file or directory)
/*I m using this code for copy data from assets folder just try it */
public class DataBaseHelper extends SQLiteOpenHelper{
private static String DB_PATH = "/data/data/com.astrobix.numerodaily/databases/";
private static String DB_NAME = "Astrobix";
private static final String tag = "DatabaseHelperClass";
public static SQLiteDatabase myDataBase;
public final Context myContext;
public static String Table_Name="Prediction";
public static final String COL_ID="ID";
public static final String COL_HEAD="HEAD";
public static final String COL_VALUE="VALUE";
private static final String TABLE_QUESTIONS ="Prediction";
private static final String Create_Table="create table if not exists "
+Table_Name
+"("
+COL_ID
+" INTEGER primary key autoincrement , "
+COL_HEAD
+" TEXT, "
+COL_VALUE
+ " VARCHAR); ";
// public static String lastrecord;
// public static String ratio="7:7";
public DataBaseHelper(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{
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;
/* File dbFile = new File(DB_PATH + DB_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 transfering bytestream.
* */
private void copyDataBase() throws IOException{
//Open your local db as the input stream
InputStream myInput = myContext.getAssets().open("Astrobix.sqlite");
// 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_READWRITE );
}
#Override
public synchronized void close() {
if(myDataBase != null)
myDataBase.close();
super.close();
}
#Override
public void onCreate(SQLiteDatabase db) {
db.execSQL(Create_Table);
}
#Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
if (db != null)
onCreate(db);
}