I am trying to figure out what order I need to do everything to have my program flow well and not crash. I want the player of my game to have a team of three people each as their own class with stats such as attack, health, etc.
When the game is first installed and run, I want to have a sqlite db made and filled with the 3 starter characters.
When they start it any other time, I want to have the three stored characters in the DB to be assigned to 3 local character objects.
I have a SQLite Adapter class handling the Db but I'm not sure how to order everything so that I check first to see if there is a Db in the programs Db path and open it if there is. Otherwise just create a new one and populate it with the initial characters.
I currently have:
DbAdapter = new PlayerDbAdapter(this);
DbAdapter.open();
DbAdapter.setInitialPlayers(); // inserts my initial player object info into the Db
which calls
public PlayerDbAdapter open() throws SQLException {
dbHelper = new SummonSQLiteHelper(context);
String s = DbPath + DB_NAME;
database = SQLiteDatabase.openOrCreateDatabase(s, null);
return this;
}
And now I'm a little lost as to what I need to make sure that I don't overwrite any saved info in the Db with the initial values everytime I start the program. Any suggestions?
Try like this
public PlayerDbAdapter open() throws SQLException {
dbHelper = new SummonSQLiteHelper(context);
String s = DbPath + DB_NAME;
bool b=checkDataBase(s);
if(b==false)
{
database = SQLiteDatabase.openOrCreateDatabase(s, null);
setInitialPlayers(); <---- set initial values here
}
else
{
database = SQLiteDatabase.openOrCreateDatabase(s, null);
}
return this;
}
private boolean checkDataBase(String str) {
SQLiteDatabase checkDB = null;
try {
checkDB = SQLiteDatabase.openDatabase(str, null,
SQLiteDatabase.OPEN_READONLY);
checkDB.close();
} catch (SQLiteException e) {
// database doesn't exist yet.
}
return checkDB != null ? true : false;
}
Related
I have made Singelton object to make queries to SQLiteOpenHelper in which I have saved instance of getWriteableDatabase(). Throughout the application lifecycle, for all select and insert/update queries I am using that instance from multiple IntentServices. I have read Write-ahead Logging (WAL) which supports concurrent execution of queries. I am using the above instance with WAL disabled. Actually at a point the database does not return data, I was wondering if SQLite file can get corrupted because I am using getWritableabledatabse for reading/writing from multiple intent services.
Can a deadlock occur with this approach?
As per my findings, WAL should be enabled if you are accessing database from multiple threads.
EDIT
DatabaseAdapter.java
public class DatabaseAdapter {
private Context mContext;
private SQLiteDatabase mSqLiteDatabase;
private DatabaseHelper mDbHelper;
private static DatabaseAdapter adapter;
public static DatabaseAdapter getInstance() {
if(adapter == null) {
synchronized (DatabaseAdapter.class) {
if(adapter == null)
adapter = new DatabaseAdapter(MyApp.getInstance());
}
}
return adapter;
}
public DatabaseHelper getDatabaseHelper() {
return mDbHelper;
}
private DatabaseAdapter(Context c) {
mContext = c;
mDbHelper = new DatabaseHelper(mContext);
mSqLiteDatabase = mDbHelper.getWritableDatabase();
}
private long insert(String tableName, ContentValues contentValues) throws Exception {
this.open();
long id = mSqLiteDatabase.insert(tableName, null, contentValues);
this.close();
return id;
}
private int update(String tableName, ContentValues contentValues, int pk_id) throws Exception {
this.open();
String whereClause = mDbHelper.pk_id + " = " + pk_id;
int n = mSqLiteDatabase.update(tableName, contentValues, whereClause, null);
this.close();
return n;
}
private ArrayList<MyObject> selectChallans(String whereClause, String orderby) throws Exception {
try {
ArrayList<MyObject> arrayListObjects = new ArrayList<MyObject>();
Cursor queryCursor = mSqLiteDatabase.query(tableName, null, whereClause, null, null, null, orderby, null);
if (queryCursor == null) {
return null;
}
while (queryCursor.moveToNext()) {
MyObject myobject = getMyObject(queryCursor);
if(myobject != null)
arrayListObjects.add(myobject);
}
queryCursor.close();
return arrayListObjects;
} catch (Exception e) {
e.printStackTrace();
this.forceClose();
throw e;
}
}
}
I am using this Adapter singleton instance through the application for insert/update and select queries. I was concerned about mSqLiteDatabase instance. These functions are being called from multiple IntentServices.
AFAIK, the best practice is calling getWritableabledatabse only once with one SQLiteOpenHelper. After that, you can use the returned database for all thread without any issue. You have to make sure that you are using one database connection. You can check this Good Answer for more detail.
The SqliteOpenHelper object holds on to one database connection. It appears to offer you a read and write connection, but it really doesn't. Call the read-only, and you'll get the write database connection regardless.
So, one helper instance, one db connection. Even if you use it from multiple threads, one connection at a time. The SqliteDatabase object uses java locks to keep access serialized. So, if 100 threads have one db instance, calls to the actual on-disk database are serialized.
So, one helper, one db connection, which is serialized in java code. One thread, 1000 threads, if you use one helper instance shared between them, all of your db access code is serial. And life is good (ish).
For me, I usually create and open the SQLiteOpenHelper in Application class, then I can use it everywhere in any thread in my app.
the table ( i.e. vaccines) structure is :
id- auto increment primary key
dose1_date - string
dose2_date - string
The DatabaseAccessor class is as follows. The initDB() and setVaccineDates methods are called from another activity. But the database is not updated. The logged message is found in the logcat however. The DatabaseHelper class is not shown here.
public class DatabaseAccessor {
public static DataBaseHelper myDbHelper = null;
public static SQLiteDatabase rdb = null;
public static SQLiteDatabase wdb = null;
public static synchronized final void initDB(Context context) throws Exception {
if (myDbHelper == null) {
myDbHelper = new DataBaseHelper(context);
myDbHelper.openDataBase();
rdb = myDbHelper.getReadableDatabase();
wdb = myDbHelper.getWritableDatabase();
}
}
public static void setVaccineDates(String birthDate) throws SQLException{
try {
String[] selections = null;
String qry = null;
qry = "select * from vaccines order by id";
Cursor cursor = wdb.rawQuery(qry, selections);
Log.d("update qry===== ", qry);
while (cursor.moveToNext()) {
int rowID = Integer.parseInt(cursor.getString(0));
ContentValues values = new ContentValues();
values.put("dose1_date","66666");
values.put("dose2_date","7777");
wdb.update("vaccines", values, "id=?", new String[] {String.valueOf(rowID)});
//wdb.close();
}
cursor.close();
} catch (Exception e) {
e.printStackTrace();
}
}// end of method setVaccineDates
}
What to do ?
Edit : If I uncomment the wdb.close() line , I see in logcat
'06-09 04:21:05.387: W/System.err(4144): java.lang.IllegalStateException: attempt to re-open an already-closed object: SQLiteDatabase: /data/data/com.cloudsoft.vaccine/databases/vaccines2.db
'
As a newbie in android it was just a mistake out of ignorance that this situation took place: after update operation I tried to find the changes in the database file (i.e. file with .db extension sitting inside assets folder in Eclipse) through sqlite browser . But what actually happens is the app running in the device (real one or emulator) has its own database which is created from the .db extension file inside assets folder and consequent database operations only affect the app's own database leaving no touch on the database inside the mentioned folder in Eclipse. And there is the way to watch the app's very own database in the running device in Eclipse's 'File Explorer' (in DDMS mode) with the help of Questoid SQlite Manager
In an Android 'simple' database scenario, is there any benefit or reason to use database.close() and Not databaseHelper.close() ? Is there any benefit or reason to use databaseHelper.close() and Not database.close() ?
Is there a technical reason why both these close methods (shown below) exist?
Thanks,
James
MyDatabaseHelper databaseHelper = new MyDatabaseHelper(this);
SQLiteDatabase database = databaseHelper.getWritableDatabase();
ContentValues valuesToInsert = new ContentValues();
int id = 0;
valuesToInsert.put("_id", id);
valuesToInsert.put("name", "test");
database.insert("MyRecordsTable", null, valuesToInsert);
database.close();
OR
MyDatabaseHelper databaseHelper = new MyDatabaseHelper(this);
SQLiteDatabase database = databaseHelper.getWritableDatabase();
ContentValues valuesToInsert = new ContentValues();
int id = 0;
valuesToInsert.put("_id", id);
valuesToInsert.put("name", "test");
database.insert("MyRecordsTable", null, valuesToInsert);
databaseHelper.close();
There isn't really a huge difference. This is the whole definition of close() within SQLiteOpenHelper:
/**
* Close any open database object.
*/
public synchronized void close() {
if (mIsInitializing) throw new IllegalStateException("Closed during initialization");
if (mDatabase != null && mDatabase.isOpen()) {
mDatabase.close();
mDatabase = null;
}
}
The reason both exist, is that there may be instances where developers only use SQLiteOpenHelper for interfacing with their database and want the close() method as a convenience to directly access the DB, or vice versa if developers don't choose to use the OpenHelper at all.
i encountered a little problem with my current project. I am doing an android application which needs to connect to a SQLite database to work through some statements. I believe the statements etc are fine, my only problem is the fact that the connection to the database is not succesfull.
LogCat Error:
04-18 08:20:30.688: E/Database(304): sqlite3_open_v2("jdbc:sqlite:res/raw/randomdb.db", &handle, 1, NULL) failed
So my code so far for connecting to the database is like this:
String url = "jdbc:sqlite:res/raw/randomdb.db";
SQLiteDatabase db;
db = SQLiteDatabase.openDatabase(url, null, SQLiteDatabase.OPEN_READONLY);
As you can see, i am trying to acces a database which is located in my project/res/raw folder. Does anyone see the mistake?
!!!UPDATE!!!
*I tried to go the way with SQLiteOpenHelper, but still encouner an error i cannot seem to solver. Here is my new code:*
public class DatabaseAdapter extends SQLiteOpenHelper {
private static String dbPath= "data/data/com.YourPackageName/applicationDb/";
private static String dbName = "YourDBName";
private SQLiteDatabase applicationDatabase;
private final Context applicationContext;
private boolean checkDataBase(){
File dbFile = new File( dbPath + dbName);
return dbFile.exists();
}
public void openDataBase() throws SQLException{
String fullDbPath= dbPath + dbName;
applicationDatabase = SQLiteDatabase.openDatabase( fullDbPath,null,SQLiteDatabase.OPEN_READONLY);
}
#Override
public void onCreate(SQLiteDatabase db) {
}
#Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
}
}
I get this error:
Implicit super constructor SQLiteOpenHelper() is undefined for default constructor. Must define an explicit constructor
Any ideas? Would be great!
if you want to connect your android application with SQLite database then you need to extends SQLiteOpenHelper
public class AbcClass extends SQLiteOpenHelper
after that you need to Override these method:
onCreate and onUpgrade
and constructor of AbcClass looks like:
public AbcClass(Context context) {
super(context, DB_NAME, null, VERSION); //public static final String DB_NAME = "test.sqlite";
}
public boolean databaseExist()
{
File dbFile = new File(DB_PATH + DB_NAME);
return dbFile.exists();
}
This is the solution...
OR-----------------------
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;
}
I think the path you specify is not the correct one and so as mentioned in one of the other answers the database is not found. However I had never used the method openDatabase to read from the raw folder so I do not know which is the correct path.
However once upon a time I had shipped the database along with my application. It resided in the assets folder and once the application started I copied it in the private application storage (much like any database created with SqliteOpenHelper). From then on I used the usual way with SqliteOpenHelper to access the database.
Basically for this I followed the blog mentioned in this thread and because my database file was bigger than 1MB I used the technique described in this thread. Hopefully combining those two you will get your database running!
EDIT Btw you are wrong, openDatabase does not accept url, but path.
I need to check existing database before creating new database on android 2.2. How to check it?
use openOrCreateDatabase method
Read here
----- EDIT ------
public boolean checkDataBase(){
SQLiteDatabase checkDB = null;
try{
String myPath = DB_PATH + DB_NAME;
checkDB = SQLiteDatabase.openDatabase(myPath, null, SQLiteDatabase.OPEN_READONLY | SQLiteDatabase.NO_LOCALIZED_COLLATORS);
}catch(SQLiteException e){
//database does't exist yet.
}
if(checkDB != null){
checkDB.close();
}
return checkDB != null ? true : false;
}
Doesn't it work with the DatabaseHelper ?
If you haven't tried here is code I posted before...
Android - Sqlite database method undefined fot type
You should need to check database already exists or not, if not than create database else not create database. Please you can use below query.
CREATE TABLE if not exists TABLE_NAME (key data_type);
Call this query inside onCreate method.
To check if your database was created you can use the following code and it will not be recreated every time you open the application.
dbName = is the name of you DB
public static boolean doesDatabaseExist(Context context, String dbName) {
File dbFile = context.getDatabasePath(dbName);
return dbFile.exists();
}