I have one database and I've created a Database class that has a private static class DbHelper extends SQLiteOpenHelper to help me manage my database.
This database is access from four different activities.
I have this public void onCreate(SQLiteDatabase db) that it's what creates my database tables and adds values to those tables. Since the program only enter here the first time the user runs the app, I want to create a ProgressDialog.
This is what I have:
Override
public void onCreate(SQLiteDatabase db) {
ProgressDialog progresso = ProgressDialog.show(context, "Info", "Loading DB" );
// Code to create the tables and add the values
progress.dismiss();
}
Got 2 questions.
First:
Since this can be accessed from 4 different activities how can I get the context?
Making:
Context context = getAplicationContext();
Second:
Why can't I use strings from strings.xml?
I can't make this:
ProgressDialog progresso = ProgressDialog.show(context, getString(R.string.driProgressTitle), getString(R.string.driProgressMessage) );
UPDATE
public class Database {
ProgressDialog progresso;
private DbHelper DBHelper;
private final Context Context;
private SQLiteDatabase BaseDados;
private static class DbHelper extends SQLiteOpenHelper {
public DbHelper(Context context) {
super(context, BD_NAME, null, BD_VERSION);
// TODO Auto-generated method stub
}
#Override
public void onCreate(SQLiteDatabase db) {
// Whanted to show here the progress dialog
new loadDB().execute();
// Creates the table and adds the values
}
#Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVesion) {
db.execSQL("DROP TABLE IF EXISTS MYTABLE");
onCreate(db);
}
}
public Database(Context c) {
Context = c;
}
public Database open() throws SQLException {
DBHelper = new DbHelper(Context);
BaseDados = DBHelper.getWritableDatabase();
return this;
}
public void close() {
DBHelper.close();
}
public Cursor getData(int tipo, long groupId, String query[]) {
// Performs the querys to my database
}
return null;
}
public class loadDB extends AsyncTask<Void, Void, Integer> {
#Override
protected Integer doInBackground(Void... params) {
progresso = ProgressDialog.show(Context, Context.getString(R.string.ProgressTitle), Context.getString(R.string.ProgressMessage) );
return 1;
}
#Override
protected void onPostExecute(Integer result) {
progresso.dismiss();
super.onPostExecute(result);
}
}
}
With the above code I'm getting this error No enclosing instance of type Database is accessible. Must qualify the allocation with an enclosing instance of type Database (e.g. x.new A() where x is an instance of Database). in new loadDB().execute();.
Don't you have access to the Context from the constructor?
SQLiteOpenHelper(Context context, String name, SQLiteDatabase.CursorFactory factory, int version)
SQLiteOpenHelper(Context context, String name, SQLiteDatabase.CursorFactory factory, int version, DatabaseErrorHandler errorHandler)
EDIT:
Your code:
public DbHelper(Context context) {
super(context, BD_NAME, null, BD_VERSION);
// TODO Auto-generated method stub
}
You have access to the context at this point.. can you just do something like add a field called context then do this:
Context context;
public DbHelper(Context context) {
super(context, BD_NAME, null, BD_VERSION);
// TODO Auto-generated method stub
this.context = context;
}
Now, you have access to the context anywhere in the class? Assuming that the constructor is called before onCreate.
The onCreate(SQLiteDatabase) method only gets called the first time you make a call to getReadableDatabase() or getWritableDatabase (i.e. the first time it is opened), not when the app is first run (of course you could first this to happen by simply opening the database the closing it).
I'm not sure giving it the Application context will work since the progressDialog will need to be displayed in the currently running Activity.
Instead of trying to display progress bars for precisely when the database is first accessed, have your current Activity display one any time it access the database itself. That way the first access to the database is masked by regular access to it.
If you don't want to display progress bars for all DB access, then again you can just open and close the database and have your Activity display a progress bar before you open it, and remove the progress bar when you close it (not doing anything with the database except let onCreate() run).
Unless your database structures are really big, you probably won't even see the progress bar. It doesn't take very long to create the tables.
Use Loader for reading data from DB. Don't clutter background classes with UI stuff
onCreateLoader(..
//you can start progress
onLoadFinished(..
// you can stop it
Also you read data from background thread which is recommended.
You're unable to access the context of the application, as SQLiteOpenHelper does not have contain these methods (unlike Activity)
My advice to you is to use the DbHelper class to set up the database and to manage any sql transactions. Have it's constructor take a context object, then create instances of it within a class which extends Activity, passing the context through to the constructor.
It would also be good practice to have a data access object class sitting inbetween the activity and the sqlliteOpenHelper, to manage opening, closing and issuing transactions too your database object
To show user progress, you will have to run your work in a separate thread, otherwise GUI will just freeze until the database work is completed and user may get the "Activity not responding, force close" message.
Use Application class to store a global flag about if database has been created or not.
Any starting activity checks this flag, if database has not been initialized, it creates an AsyncTask , passes on db and its own context in constructor. AsyncTask will have three methods:
onPreExecute : show your dialog here. Make dialog non cancel able so user can't skip it.
doInBackground : database building code here. (call the Database helper class methods)
onPostExecute : dismiss the dialog. Now user can interact with the Activity.
Related
when I extend my DatabaseClass with SQLiteOpenHelper class I have to implement constructor which must contains Context in its parameter. If I have to use this databases with my Other classes context maybe changed. What changes are done when I provide different context.
public class DatabaseClass extends SQLiteOpenHelper {
static String TABLE_NAME = "hammad";
static String DATABASE_NAME = "databases.db";
SQLiteDatabase database;
public DatabaseClass(Context context) {
super(context, DATABASE_NAME, null, 1);
Log.i("xcv", "Constructor called");
this.database = this.getWritableDatabase();
}
#Override
public void onCreate(SQLiteDatabase sqLiteDatabase) {
Log.i("xcv", "OnCreate");
try {
sqLiteDatabase.execSQL("create table student (id integer primary key autoincrement,name varchar)");
Log.i("xcv", "On Create query Table created");
} catch (Exception ffff) {
Log.i("xcv", "2:"+ffff.getMessage());
}
}
StartPage.java
public class StartPage extends AppCompatActivity {
DatabaseClass database;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_start_page);
setLoginBtn(); // Set Login Btn
database=new DatabaseClass(this);
}
}
Class2.java
public class Class2 extends AppCompatActivity {
DatabaseClass database;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_start_page);
setLoginBtn(); // Set Login Btn
database=new DatabaseClass(this);
}
}
"Context may be changed" You should be first clear about this line... Yes context will be changed every time you create new instance of database by calling from different class and it has to change.... Bcoz context is the one that states this activity has the permision to access the database.... So when you pass "this" of xyz activity then that xyz has permission to access data and modify data in the database... It is like showing an ID card to database before taking control over its resources....
Also you can access your database from any class by passing the context, you can retrieve data, modify data and close connection...
Scenario : Its like single fridge in a home you can open n only see whats in it.. You can take out something and eat it... Or you can place in something... And samething can be done by any of the family members... While "this" or "context" is the individual person
SQLiteOpenHelper uses the Context passed in to find your application package-private data directory where the database files are located. The package is the package declared in your app's AndroidManifest.xml, not the Java package of your class files.
All Contexts in your application have the same package-private data directory and the database will work just fine with any valid Context.
As a new Android programmer, I followed various online examples to create my own class that extends SQLiteOpenHelper...
public class MySQLiteHelper extends SQLiteOpenHelper {
private static final int DATABASE_VERSION=1;
private static final String DATABASE_NAME="MyDB";
// Main Database Operations
public MySQLiteHelper(Context context) {
super(context, DATABASE_NAME, null, DATABASE_VERSION);
}
#Override
public void onCreate(SQLiteDatabase db) {
db.execSQL(CREATE_TABLE_A);
db.execSQL(CREATE_TABLE_B);
}
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion){
...
public long addRecord(){
SQLiteDatabase db = this.getWritableDatabase();
....
etc
This works throughout my various activities with no issues. When I want to use it, I call...
MySQLiteHelper db = new MySQLiteHelper(this);
Then I call the various routines like...
db.addRecord();
Now I have created a secondary class that I need to use throughout the application. In that class, there are some routines that need to process data from the database. Problem is, I can't declare the MySQLiteHelper because of the this which errors out.
Doing some online search I think I understand that I need to get the Context from the application (or the calling activity??), but not sure how to go about doing that with this code.
As I mentioned, I am new to Android... so any code examples would be greatly appreciated.
EDIT
I need to clarify this secondary class as mentioned above. From the aforementioned online examples, the second class is used to "hold" the database record information. It looks something like this...
public class Acct {
private int _id;
private String _name;
private String _phone;
public Acct() {
}
public int get_id(){
return this._id;
}
public void set_id(int id) {
this._id=id;
}
public void set_name(String name){
this._name=name;
}
public String get_name(){
return this._name;
}
public void set_phone(String phone){
this._name=phone;
}
public String get_phone(){
return this._phone;
}
...
This is typically used with something like this in an activity...
MySQLiteHelper db = new MySQLiteHelper(this);
Acct acct = new Acct();
acct=db.getAccount(searchId);
myEditText.setText(acct.get_name());
...
Where my problem arises, I want to create a routine and code it IN the Acct class so it can be referenced such as...
acct.UpdateData();
This UpdateData routine is where we need to access the db and thus the MySQLiteHelper. It needs to be able, for each account, to go into the database, access some information from another table, do some processing, and store a summary back into this table for easier reference. As mentioned, there is no Context in the Acct class, so this is where I am getting confused.
And to make matters worse, because the Acct class is a 'holding' place for data from the DB, the online examples also use Acct IN the 'MySQLiteHelper' itself during the getAccount routine....
public Acct getAccount(int id){
SQLiteDatabase db = this.getReadableDatabase();
String SQL_STRING="SELECT * FROM "+ACCT_TABLE+" WHERE "+ACCT_FLD_ID+" = "+String.valueOf(id);
Cursor cursor =
db.rawQuery(SQL_STRING, null);
Acct acct = new Acct();
if (cursor!=null) {
cursor.moveToFirst();
acct.set_id(cursor.getInt((cursor.getColumnIndex(ACCT_FLD_ID))));
acct.set_name(cursor.getString(cursor.getColumnIndex(ACCT_FLD_NAME)));
acct.set_phone(cursor.getString(cursor.getColumnIndex(ACCT_FLD_PHONE)));
cursor.close();
} else {
acct = null;
}
db.close();
return acct;
}
I hope all this additional helped clarify what I am trying to do for the couple comments and answers posted so far. If you need more information, please ask. I'd like to get this to work, just still not sure how.
Your problem is you need a Context to call the constructor of MySQLiteHelper. You've been successful doing so in an Activity (which is a Context), but now you have some other class (which I will call "Foo") that isn't a Context and doesn't have one.
A quick solution is to make Foo take a Context in its constructor, and instantiate your MySQLiteHelper like so:
public class Foo {
private MyOpenHelper openHelper;
public Foo(Context context) {
openHelper = new MyOpenHelper(context.getApplicationContext());
}
}
If Foo is a singleton, you can do the same thing in whatever method obtains the instance (i.e. force the caller to provide a Context). Every application component either is a Context (Activity, Service) or has a Context (BroadcastReceiver gets one in onReceive(), ContentProvider has getContext()).
The use of getApplicationContext() here is worth noting: The Application object for your app is always a singleton--only one instance of it will exist for as long as your app is running (this is guaranteed by the OS). Activities can be destroyed, and creating a MySQLiteHelper with one can cause a memory leak. The application Context always exists and so it cannot be leaked.
Instead of using this, Try to use MainActivity.this or getApplicationContext().
Hope this help!
I have the following code below.
I am creating a database in my application that uses SQLiteOpenHelper.
I have couple of concerns and would appreciate some consults.
Direct answers for these were not found on stack overflow as they might be subjective.
1 - I will be using this database from several activities. However I am not planning on making this a singleton to avoid leaks, but rather I will be getting the getWritableDatabase() and getReadableDatabase() inside each method. I plan on doing a db.close() inside each activity's onDestroy() .Is this advisable ? given my app has couple of activites and is not a huge app.
2 - I am not following and DAO model, nor I am using a different class for every table.
The way I see it, I don't need to. Do I ?
3 - (A question rather than consult)
In the code below, I am not creating a database of the form
private SQLiteDatabase database;
So all the references to the database (from my activities) are being done via the methods in the same subclassed SQLiteOpenHelper, therefore I am referencing the physically created database directly via getWritableDatabase and getReadableDatabase.
Do I need to create an instance of SQLiteDatabase and use it ? Even inside the subclass of SQLiteOpenHelper ?
Below is the code.
public class DbHelper extends SQLiteOpenHelper
{
private static final String DATABASE_NAME = "myDbName";
private static final String DATABASE_TABLE = "myTable";
private static final int DATABASE_VERSION = 1;
private Context ctx;
public DbHelper(Context context)
{
super(context, DATABASE_NAME, null, DATABASE_VERSION);
this.ctx = context;
}
#Override
public void onCreate(SQLiteDatabase db)
{
db.execSQL("CREATE TABLE myTable(_id INTEGER PRIMARY KEY, title TEXT);");
}
#Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion)
{
//nothing here now. maybe later.
}
public void insertTitle(String title)
{
ContentValues titleCV = new ContentValues();
titleCV .put("title", title);
getWritableDatabase().insert(DATABASE_TABLE, null, titleCV );
}
public void getTitles()
{
Cursor result = getReadableDatabase().rawQuery("SELECT _id, title FROM myTable", null);
while (result.moveToNext())
{
int id = result.getInt(0);
String titleGotten= result.getString(1);
}
result.close();
}
Q1
If you have a scenario within your app that have two parallel threads accessing the database, use a single instance of the SQLiteOpenHelper (singleton or member in the Application or whatever). If not you don't need to.
about calling db.close(), if it is in the onDestroy(), then it's fine.
Q2
a DAO is an abstraction layer to ease maintaining and scaling your project. If you are not going to scale or maintain your code (upcoming releases or something), then I suppose you don't need one.
Q3
You don't need to create an instance of SQLiteDatabse. when you call getReadableDatabase() or getWritableDatabase(), SQLiteOpenHelper creates and maintains an instance. The same instance is used the next time you call getReadable\WritableDatabase().
let me know if you still have questions.
step 1: make a staic instace of SqliteOpenHelper
step 2: you never close conexion to database, sqlite manage itself the sequencial access to write or read :)
private static ControladorBBDD instancia;
my class: public class ControladorBBDD extends SQLiteOpenHelper {
default :
private ControladorBBDD(Context ctx_p) throws Exception {
super(ctx_p, DB_NAME, null, DATABASE_VERSION);
try {
ControladorBBDD.ctx = ctx_p;
DB_PATH = ctx.getDatabasePath(DB_NAME).getAbsolutePath();
String myPath = DB_PATH;// + DB_NAME;
this.createDataBase();
db = SQLiteDatabase.openDatabase(myPath, null,
SQLiteDatabase.OPEN_READWRITE);
} catch (SQLiteException ex) {
Conexiones.escribirLog(Log.getStackTraceString(ex),
ctx.getString(R.string.versionReal));
db.close();
}
}
and my way to implement a conexion to database:
public static synchronized ControladorBBDD getBBDD(Context ctx_p)
throws Exception {
if (instancia == null) {
instancia = new ControladorBBDD(ctx_p);
}
return instancia;
}
and to call it from activities:
dblectura = ControladorBBDD.getBBDD(getApplicationContext());
where private ControladorBBDD dblectura;
i hope that it helps, important thing is that you use applicationContext, no Activity context ;))
well if i were u i would create a class and the dbhelper as a subclass then i would use a open and a close function for main class and also the insert
whenever i want to use database i do it like this
mainclass mc=new mainclass(this);
mc.open();
mc.insert();
mc.close();
On my app I make use of two datatabases.
This is the class that handles the database management and all the query that are made to it.
public class Database {
private DbHelper DBHelper;
private final Context Context;
private SQLiteDatabase MyDBone, MyDBtwo;
static Context ctx;
private static class DbHelper extends SQLiteOpenHelper {
public DbHelper(Context context, String dbName, int dbVersion) {
super(context, dbName, null, dbVersion);
}
#Override
public void onCreate(SQLiteDatabase db) {
// This is where the two databases are created
}
}
#Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVesion) {
// database upgrades are handled here
}
}
}
// database constructor
public Database(Context c) {
Context = c;
ctx = c;
}
// database open
public Database open() throws SQLException {
DBHelper = new DbHelper(Context, BD_NAME, BD_VERSION);
// I have here some if code to decide witch one of the bellow is used
if{
MyDBone = DBHelper.getWritableDatabase();
} else{
MyDBtwo = DBHelper.getWritableDatabase();
}
return this;
}
// database close
public void close() {
DBHelper.close();
}
public Cursor getData(........) {
// My querys are made here
}
}
My problem is that the databases are too big. In the onCreate method I'm getting the error: The code of method onCreate(SQLiteDatabase) is exceeding the 65535bytes limit. On the other side, my app is getting very big on size.
I would like to know what's the best way to address this issue since I can't change my databases.
Since my app must be run offline I can't make query's on a webserver.
I beleive that the best aproach would be to, on the first run of the app, download the databases from somewhere on the internet (drive, dropbox or other side) but since my programming skils are a little green I must pospone this to a must do in the future.
Is it possible, maintaining my Database class, prepack the apk with the databases and install them on the sdcard? On the other side this will increase the apk size (the total of the databases is 15 mb).
Please advise on the best way to address this issue.
Regards,
favolas
I'm trying to get a better understanding of the SQLiteOpenHelper class and how and when onCreate and onUpgrade are called.
My problem is that each time I quit and start my application (technically it's actually each time I create a new instance of MyDB), onCreate is called, and all of the data from the previous usage is effectively wiped... WTF???
I got around this problem initially by creating a singleton MyDBFactory where I created a single instance of MyDB. This allowed the data to be persistent while the application is running at least.
What I would like is for my database schema and data to be persistent!
I basically have:
public class MyDB extends SQLiteOpenHelper{
private static int VERSION = 1;
...
public ContactControlDB(Context context) {
super(context, null, null, VERSION);
}
#Override
public void onCreate(SQLiteDatabase db) {
try {
db.execSQL(DATABASE_CREATE);
db.execSQL(INSERT_DATA);
} catch (SQLException ex) {
ex.printStackTrace();
}
}
and:
public class MyDBFactory{
private static MyDB db;
public static MyDB getInstance(Context context) {
if(db == null) {
db = new MyDB (context);
}
return db;
}
}
What I'd like to know is why onCreate is called every time I have 'new MyDB(context)', and where my database goes each time my app exits.
If you have some appropriate links, or some knowledge that would clue me up a bit, I'd greatly appreciate it!
the line:
super(context, null, null, VERSION);
the second parameter is null specifying that the database should be created in memory, you should give it a name.
Android reference