I'm still very new to Object Oriented programming and am having problems with the class inheritance and the scope of variables from one instantiated class to another.
I'm trying to build an android application that can read multiple XML feeds and save them to the phone's SQLite database. Each feed has a different name ("news", "audio_mixes" etc.) and I want to use these names to save each feed's data into separate database tables - each one named after the feed title.
In diagrammatic terms this is what my classes look like:
(source: baroquedub.co.uk)
The Main activity displays two buttons each of which starts an activity - both of which are instances of the TitlesBrowser class. Extras are used to pass different values of the_url and the_feed_type variables.
#Override
public void onCreate(final Bundle icicle) {
super.onCreate(icicle);
this.setContentView(R.layout.main);
this.getNewsButton = (Button) findViewById(R.id.get_news_button);
this.getNewsButton.setOnClickListener(new OnClickListener() {
public void onClick(final View v) {
Intent doGetNews = new Intent(Main.this, TitlesBrowser.class);
doGetNews.putExtra("the_url", Main.this.getString(R.string.titles_url));
doGetNews.putExtra("the_feed_type", Main.this.getString(R.string.news_type_var));
startActivity(doGetNews);
}
});
this.getMixtapesButton = (Button) findViewById(R.id.get_mixtapes_button);
this.getMixtapesButton.setOnClickListener(new OnClickListener() {
public void onClick(final View v) {
Intent doGetMixtapes = new Intent(Main.this, TitlesBrowser.class);
doGetMixtapes.putExtra("the_url", Main.this.getString(R.string.titles_url));
doGetMixtapes.putExtra("the_feed_type", Main.this.getString(R.string.mixtapes_type_var));
startActivity(doGetMixtapes);
}
});
}
The TitlesBrowser class, in its onCreate method, gets the Extras and saves them to private local variables.
Intent i = getIntent();
private String theUrl = (String) i.getStringExtra("the_url");
private String theFeedType = (String) i.getStringExtra("the_feed_type");</pre>
This class does two things,
1/ it creates an instance of the DatabaseHelper class, and uses a public set method in that class to pass on the value of the locally help theFeedType variable. Having done that it queries the database for any existing data (the theFeedType is the name of each table)
db=new DatabaseHelper(this);
db.setTableName(theFeedType);
dbCursor=db.getReadableDatabase().rawQuery("SELECT _ID, id, title FROM "+theFeedType+" ORDER BY id", null);
2/ then it loads new data from the feed URL by instantiating another class HTTPRequestHelper :
HTTPRequestHelper helper = new HTTPRequestHelper(responseHandler);
helper.performPost(theUrl, theFeedType);
The HTTP requests work fine, and depending on which of the two buttons is clicked, each of the two different activities display the appropriate XML (i.e. retrieve the correct data)
- therefore I know that theUrl and theFeedType variables are local to each instance of the TitlesBrowser class.
One calls:
http://baroquedub.co.uk/get-feed.php?feed=news
and the other one:
http://baroquedub.co.uk/get-feed.php?feed=audio_mixes
The problem is with the DatabaseHelper class:
public class DatabaseHelper extends SQLiteOpenHelper {
private static final String DATABASE_NAME="baroquedub.db";
private String table_name;
public DatabaseHelper(Context context) {
super(context, DATABASE_NAME, null, 1);
}
#Override
public void onCreate(SQLiteDatabase db) {
db.execSQL("CREATE TABLE "+ table_name + "(_id INTEGER PRIMARY KEY AUTOINCREMENT, " +
"id INTEGER, " +
"title TEXT, " +
"details TEXT, " +
"picture TEXT, " +
"viewed BOOLEAN DEFAULT '0' NOT NULL);");
}
#Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
android.util.Log.w("Baroquedub", "Upgrading database, which will destroy all old data");
db.execSQL("DROP TABLE IF EXISTS mixtapes");
onCreate(db);
}
public void setTableName(String theName){
this.table_name = theName;
}
}
I would expect it to create a new table each time it is instantiated (whether "news" or "audio_mixes" was passed from its parent TitlesBrowser class.
But it only works once - if I start the application and click on "news" a table called news is created and each time I return to that activity it successfully retrieves data from that same database.
But if I then start the other activity by clicking on the other button (i.e access the other feed) I get an SQL error telling me that a database of that name doesn't exist. In other words the onCreate db.execSQL("CREATE TABLE... method doesn't appear to get called again.
It's as if multiple copies of the DatabaseHelper class aren't being created, although there are two instances of TitlesBrowser.
--
Here's a demonstration of the problem:
http://screencast.com/t/NDFkZDFmMz
This has been really tricky to explain (especially for a newbie!!!) and I hope it makes some sense. I'd be very grateful for any help, advice or guidance.
When you instanciate a SQLiteOpenHelper class you actually access a singleton in the Activity's context. The helper decides when to call the OnCreate / onUpgrade methods - specifically, if the DB doesn't exist or is stale.
Either way, the onCreate method is not called each time you create a new instance of the class - that would really make no sense. You should put the two CREATE TABLE commands in the onCreate method.
(PS I assume the error you're getting is that a table is missing and not the entire database).
A big thank you to adamk for providing the guidance necessary to solve this, in the answer he provided.
He suggested that I add two CREATE TABLE commands in the DatabaseHelper's onCreate method (on for each of my feeds). However, I needed to abstract things a little more so that I could still use multiple instantiations of the TitlesBrowser activity and so that I wouldn't have to rewrite the helper class every time a new feed came on board.
For those interested, here's the solution I came up with.
First of all, I removed the CREATE TABLE commands in the DatabaseHelper's onCreate method. I also removed the private table_name variable (and its associated set method) and I replaced this with a public method called makeTable():
public class DatabaseHelper extends SQLiteOpenHelper {
private static final String DATABASE_NAME="baroquedub.db";
public DatabaseHelper(Context context) {
super(context, DATABASE_NAME, null, 1);
}
#Override
public void onCreate(SQLiteDatabase db) {
}
#Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
android.util.Log.w("Baroquedub", "Upgrading database, which will destroy all old data");
db.execSQL("DROP TABLE IF EXISTS mixtapes");
onCreate(db);
}
public void makeTable(String theTableName){
SQLiteDatabase thisDB = DatabaseHelper.this.getWritableDatabase();
thisDB.execSQL("CREATE TABLE IF NOT EXISTS "+ theTableName + "(_id INTEGER PRIMARY KEY AUTOINCREMENT, " +
"id INTEGER, " +
"title TEXT, " +
"details TEXT, " +
"picture TEXT, " +
"viewed BOOLEAN DEFAULT '0' NOT NULL);");
}
}
In TitlesBrowser, rather than instantiating the DatabaseHelper, setting the table name and then reading in the data:
db=new DatabaseHelper(this);
db.setTableName(theFeedType);
dbCursor=db.getReadableDatabase().rawQuery("SELECT _ID, id, title FROM "+theFeedType+" ORDER BY id", null);
Instead, I reworked all this so that it would more elegantly handle the creation and loading of the database data from each table:
The database Helper is created as before,
then the new databaseLoad() method tries to read from the required table
and if it can't, it calls the DatabaseHelper's public makeTable() method,
before finally trying to load the data again:
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Intent i = getIntent();
theUrl = (String) i.getStringExtra("the_url");
theFeedType = (String) i.getStringExtra("the_feed_type");
// show saved in DB
db=new DatabaseHelper(this);
databaseLoad();
}
private void databaseLoad(){
try { // table exists
dbCursor=db.getReadableDatabase()
.rawQuery("SELECT _ID, id, title FROM "+theFeedType+" ORDER BY id", null);
displayContent();
} catch (Exception e) { // table doesn't exist
db.makeTable(theFeedType);
databaseLoad(); // try again
}
}
Related
I am creating an app in which people give scores to other persons. For that, I want to insert names of some person (say 5) to a database during installation of the app. Now the when I run the app first time everything goes fine. But when I run it second time, the data are inserted again making it 10, i.e. duplicate data. The problem is whenever I run this activity, it adds the data. What is the solution to this problem?
Here's my code:
#TargetApi(Build.VERSION_CODES.HONEYCOMB)
public class GamePlay extends ActionBarActivity {
//Some code-----------
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.gameplay);
//Some code----
db=openOrCreateDatabase("MyGameData", MODE_PRIVATE, null);
try{
db.execSQL("create table teachers(_id integer primary key autoincrement , name varchar(30) , gender varchar(2) , score integer)");
Toast.makeText(this, "Table created", Toast.LENGTH_SHORT).show();
}
catch(Exception e){
Toast.makeText(this, "Exception 1", Toast.LENGTH_SHORT).show();
Log.i("Create table", e.getMessage());
}
// Insert values in table-------
String str_name[]={"arun", "dhanraj", "decoder", "sanjana"};
String str_gender[]={"m", "m", "m", "f"};
ContentValues cv = new ContentValues();
for(int i=0; i<str_name.length; i++){
cv.put("name", str_name[i]);
cv.put("gender", str_gender[i]);
db.insert("teachers", null, cv);
}
//Some code------
If I get your problem right, you could use a helper class and extend SQLiteOpenHelper.
In the onCreate method you can insert the needed data. This method is called only once.
Hope this helps.
You should have a SQLiteOpenHelper class which contains some methods for database management and lifecycle like
onCreate(SQLiteDatabase db) // Called when the database is created for the first time.
onDowngrade(SQLiteDatabase db, int oldVersion, int newVersion) //Called when the database needs to be downgraded.
onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) // Called when the database needs to be upgraded.
And other ones, you can have a look here: http://developer.android.com/reference/android/database/sqlite/SQLiteOpenHelper.html
Following this tutorial should keep you on the good path about using this helper:
http://developer.android.com/training/basics/data-storage/databases.html
My advice is to create some Querys like the ones that will populate and execute them after the creation of the Tables which should be there too. This way, your app will only make those inserts the first time, and keep them even if you update your database (if you don't state something else on the onUpgrade() method).
This should fix your problem.
i have a listview in my project that links to database and shows its context from database but my problem is whenever my application runs it records goes twice (ex.2records first run, 4records with same context,...) and i do not know that wat is problem
this is my database class:
new File(DIR_DATABASE).mkdirs();
dataBase = SQLiteDatabase.openOrCreateDatabase(DIR_DATABASE + "/information.sqlite", null);
dataBase.execSQL("CREATE TABLE IF NOT EXISTS information (" +
"information_id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL UNIQUE ," +
"information_title TEXT)");
dataBase.execSQL("INSERT INTO information (information_title) VALUES ('قسمت اول')");
dataBase.execSQL("INSERT INTO information (information_title) VALUES ('قسمت دوم')");
and its my main class that shows listview:
ListView lstC findViewById(R.id.lstContent);
adapter = new AdapterNote(title);
lstContent.setAdapter(adapter);
readFromDataBase();
adapter.notifyDataSetChanged();
}
private void readFromDataBase() {
Cursor cursor = G.dataBase.rawQuery("SELECT * FROM information", null);
while (cursor.moveToNext()) {
StructNote detail = new StructNote();
detail.title = cursor.getString(cursor.getColumnIndex("information_title"));
title.add(detail);
}
cursor.close();
adapter.notifyDataSetChanged();
}
You need to understand SQLiteOpenHelper for this. This is workflow issue, so it would be better you should go through some tutorial. This is a nice tutorial where you can learn the concept.
In very short i am listing few points that may be useful for you:
In your application you create a subclass of the SQLiteOpenHelper class.SQLiteOpenHelper is a helper class to manage database creation and version management. In subclass override, onCreate() and onUpgrade().
public class MySQLiteHelper extends SQLiteOpenHelper {
public void onCreate(SQLiteDatabase database) {
// create database command.
}
#Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
// upgrade database command here.
}
}
Create a DAO class that will manage the interaction with the database. Your CRUD methods will go here. See DAO pattern for details. This class will centralize the access to database, hence your activity, fragment will interact with this class to perform operation. Direct access to database won't be allowed.
public class ModelDataSource {
// needed to perform operation on database
private SQLiteDatabase database;
//needed to retrieve database object
private MySQLiteHelper dbHelper;
public boolean insertModel(Model model) {
// perform insert operation on database
}
}
In your activity, you can interact with DAO to perform some action.
public class YourActivity extends Activity {
private ModelDataSource datasource;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
datasource = new ModelDataSource(this);
datasource.open();
boolean insertion_status = datasource.insert(modelobject);
}
}
My application has 7 activities and all are highly dependent on database and each activity when accessed will query database a lot.
For this issue I have created a singleton database object for the database class and for every operation I am calling below statement.
Database.getInstance(c1).getWritableDatabase()
for inserts, selects and deletes I am using like below
-- insert Database.getInstance(c1).getWritableDatabase().insert
-- select Database.getInstance(c1).getWritableDatabase().rawquery
-- delete Database.getInstance(c1).getWritableDatabase().rawquery
I am using this way because by this only one object is used in whole application and multiple objects won't be created.
But when searching the web, I learned that getWritableDatabase() will open a connection to database. Now I am confused whether object to database class will open a connection or calling getwritabledatabase will open a connection.
I am worrying because I feel I am creating too many connections unknowingly.
Edit
I have tried to create a writable database object as below in every activity and use that object in that actovity but it returned me database locked exception hence I was forced to use the Database.getInstance(c1).getWritableDatabase() for every db operation
I have created as below
Sqldatabase sd=Database.getInstance(c1).getWritableDatabase()
I am wanting to discover how the database should be opened and where it should be closed to make a application stable and avoid memory leaks. I am am a beginner to Android, and keen to find out if my design is a valid one.
Edit
Database file
public class Database extends SQLiteOpenHelper{
private static String dbname="Director";
private static int dbversion=1;
SQLiteDatabase db;
private Context m1Context;
private static Database minstance;
public Database(Context context) {
super(context, dbname, null, dbversion);
// TODO Auto-generated constructor stub
}
public synchronized static Database getInstance(Context m1Context){
if (minstance==null){
minstance=new Database(m1Context);
}
return minstance;
}
#Override
public void onCreate(SQLiteDatabase db) {
// TODO Auto-generated method stub
STable st=new StockTable(m1Context);
BTable bt=new BrokerageTable(m1Context);
SList sl=new StockList(m1Context);
db.execSQL(st.stocktable);
db.execSQL(bt.Brokerage);
db.execSQL(sl.Create());
}
#Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
// TODO Auto-generated method stub
}
}
class file:
public class SList {
Context c1;
Cursor getid;
StockList(Context mContext){
c1=mContext;
}
**//SQLiteDatabase sd=Database.getInstance(c1).getWritableDatabase();**
String Ctable;
//String ,selectIDgetstocks,deletestock;
public String tablename="Stocklist";
public String Column1="_id";
public String Column2="Sname";
ContentValues cv=new ContentValues();
String getstocks="Select " + Column1 + " as _id, " + Column2 + " From "+ tablename;
String selectID="Select Max("+ Column1 + ") from " + tablename;
public String Create(){
Ctable="Create Table " + tablename + " (" + Column1 + " INTEGER PRIMARY KEY , " + Column2 + " Text" + ")";
return Ctable;
}
public void insert(int stockid,String name){
cv.put(Column1, stockid);
cv.put(Column2, name);
++Database.getInstance(c1).getWritableDatabase().insert(tablename,null,cv);++
}
}
You should have a function to open the database, a function to close the database, and other functions to query. Consider the following class:
public void openDatabase(){
database = Database.getInstance(c1).getWritableDatabase();
}
public void closeDatabase(){
database.close()
}
public void insert(){
//your insertion code here using database object
}
public String select(){
//your selection code here using database object
}
make database class variable. This way you can call openDatabase() at first and never have to open again until you close it or instantiate the class for the first time.
Then you can use insert or select as required.
Hope this helps
I believe class Database is wrapper for SQLiteOpenHelper, right? Since you have a singleton, each time you call getWritableDatabase() it's utilizing existing connection. The new connection is being called only at the first call. You can open new connection at onResume or onCreate and close it at onDestroy or onPause, for example.
From the docs on getWritableDatabase():
Once opened successfully, the database is cached, so you can call this method every time you need to write to the database
My DBHelper class
public class DBHelper extends SQLiteOpenHelper {
public DBHelper(Context context)
{
super(context,"SIMPLE_DB",null,1);
}
#Override
public void onCreate(SQLiteDatabase db) {
db.execSQL("CREATE TABLE SIMPLE_TABLE ( " +
"ID INTEGER PRIMARY KEY " +
"DESC TEXT);");
}
#Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
}
}
Activity class
public class SimpleDatabase extends Activity {
/** Called when the activity is first created. */
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
DBHelper dbHelper = new DBHelper(this);
SQLiteDatabase db = dbHelper.getReadableDatabase();
db.execSQL("INSERT INTO SIMPLE_TABLE VALUES (NULL, 'test');");
Cursor cursor = db.rawQuery("SELECT * FROM SIMPLE_TABLE", null);
TextView text = (TextView)findViewById(R.id.textbox);
text.setText(cursor.getString(0));
}
}
I figure it crashed (application has stopped unexpectedly!) at SQLiteDatabase db = ... because if I commented the code out from there to the end then it worked fine. But I have no idea whatsoever why it does that.
Any help would be appreciated.
Never mind, figured out what I did wrong now.
db.execSQL("CREATE TABLE SIMPLE_TABLE ( " + "ID INTEGER PRIMARY KEY<comma goes here> " + "DESC TEXT);");
Commas are serious businesses. Sorry for the stupid question.
first of all you should ensure that your SQL statement is correct. If sqlite3 is in your path you could execute the command:
$>: sqlite3 testdb.db
after that you are in an shell where you can test your SQL statements if there are syntactically correct. (Hint: refering the example above: it is not correct).
After that you should handle your cursor correctly as described by Aurora.
Maybe you should implement your onUpgrade() method, e.g:
#Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
db.execSQL("DROP TABLE IF EXISTS SIMPLE_TABLE");
onCreate(db);
}
First, to help in your debugging, make sure to use the debug monitor and Log. This will make your life a lot easier in the long run! If you are using Eclipse, select Window -> Open Perspective -> Other -> DDMS. The LogCat will show you what is happening as the program is run. Documentation describes it here. Run your program again and watch the LogCat. It should give you more information and tell you what line of code is crashing.
As far as your code goes, the first thing I notice is that after you get a cursor back, you need to call cursor.moveToFirst(); to select the (first) row. Then when you call cursor.getString(0);, it indicates the zeroeth column of the zeroeth row. If you do not call moveToFirst(); then you cursor.getString(0) is going to get the zeroeth column of the -1st row and be an index out of bounds error. By default, the cursor will start at row -1.
Depending on how you want to move through your cursor, and how many results/rows you get back, you may also need to call cursor.movetoPosition() or cursor.moveToNext(). Check out the documentation on cursors here.
I am writing an app that displays fun-facts (and the source they are from). The user can browse through the facts one by one.
Here is the design I thought of :
Create a table in SQLiteDatabase with a text column that stores the fun-fact and a second column that stores it's source. (not sure if this is the best way to go about doing it, but I want the app available even without the network)
My question is, when database is initially created on the device, should I manually populate the database from within the code, something like this pseodo-code:-
#Override
public void onCreate(SQLiteDatabase db) {
//Create the table
//Populate the table
//Insert statement 1
//Insert statement 2
//Insert statement 3
...
//Insert statement 500
}
Surely there must be a better method to create the initial database when the app is installed?
Are you certain that you really need a databse? Doesn't it just add unnecessary overhead to something so trivial?
Can't you just declare the array in your code, or am I missing something? Whether it's in the db or your code, it is taking up space. The db will add some overhead to that and vious?will take some time to load, plus your code has to handle errors, etc.
Woudl you not be better off with a simple array declared in your code? Or am I misisng something obvious? (maybe users can d/l a new db? But is that so much more overhead than d/ling a new program?)
If I'm way off, please explain (rather than downvoting). I am trying to help
Edit: presumably you already have your facts soemwhere? Maybe in a text file? You could just write code to parse that and initialze and array (or populate a db). It should bascially be a short for loop.
use a class derived from SQLiteOpenHelper
i already wrote sth obout this on my blog www.xenonite.net
public class myDatabase extends SQLiteOpenHelper
{
private static final String DB_NAME = "database.db";
private static final int DB_VERSION = 1;
public MyDatabase(Context context)
{
super(context, DB_NAME, null, DB_VERSION);
}
#Override
public void onCreate(SQLiteDatabase db)
{
db.execSQL("CREATE TABLE tbl_test ( id INTEGER PRIMARY KEY AUTOINCREMENT, test TEXT NOT NULL )");
}
#Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion)
{
db.execSQL("DROP TABLE IF EXISTS tbl_test");
onCreate(db);
}
}
you can use this like
myDatabase db = new myDatabase(getApplicationContext());
sql = "INSERT INTO tbl_test (test) VALUES ('xyz')";
db.getWritableDatabase().execSQL(sql);
String sql = "SELECT id FROM tbl_test";
Cursor result = db.getWritableDatabase().rawQuery(sql, null);
int value;
while(result.moveToNext())
{
value = result.getInt(0);
}
on every call to db, myDatabase.onCreate() is called, so your database will be created on the first run. when changing the table structure, implement onUpgrade() and increment DB_VERSION