Background info: new to Android, not to Java.
I am writing an app that sends its own messages separately from the default Android sms app. Since my app is not the default, I can't write to Android's provider.sms.outbox, which is absolutely fine. To work around this, I am creating a SQLite database to store my app's outgoing messages (in message objects). I am struggling to find a good, detailed explanation/tutorial on how to create a SQLite database that is available across all of my activities. I only have 2 activities; one that reads the database, and one that reads/writes the database.
I know that I need to create a subclass of the SQLiteOpenHelper, and I have:
public class dbHelper extends SQLiteOpenHelper {
//Database Info
private static final int DATABASE_VERSION = 1;
private static final String DATABASE_NAME = "outgoingMsgs.db";
public static final String TABLE_MESSAGES = "messages";
//Table Info
public static final String COLUMN_ID = "id";
public static final String COLUMN_SEND_TO_NAME = "send_to_name";
public static final String COLUMN_SEND_TO_NUM = "send_to_num";
public static final String COLUMN_BODY = "msg_body";
public dbHelper(Context context){
super(context, DATABASE_NAME, null, DATABASE_VERSION);
}
//table creation...
#Override
public void onCreate(SQLiteDatabase db) {
String CREATE_MESSAGES_TABLE = "CREATE TABLE " + TABLE_MESSAGES +
"(" + COLUMN_ID + " INTEGER PRIMARY KEY," + COLUMN_SEND_TO_NAME + " TEXT," +
COLUMN_SEND_TO_NUM + " TEXT," + COLUMN_BODY + " TEXT" + ")";
db.execSQL(CREATE_MESSAGES_TABLE);
}
//database version upgrade... destroys old and recreates
#Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
db.execSQL("DROP TABLE IF EXISTS " + TABLE_MESSAGES);
onCreate(db);
}
//adds single record
public void addRecord(myMessage msg){
SQLiteDatabase dbase = this.getWritableDatabase();
ContentValues vals = new ContentValues();
//fill vals with appropriate content
vals.put(COLUMN_SEND_TO_NAME, msg.get_name());
vals.put(COLUMN_SEND_TO_NUM, msg.get_number());
vals.put(COLUMN_BODY, msg.getBody());
//insert
dbase.insert(TABLE_MESSAGES, null, vals);
dbase.close();
}
public int getRecordCount(){
String countQuery = "SELECT * FROM " + TABLE_MESSAGES;
SQLiteDatabase dbase = this.getReadableDatabase();
Cursor cursor = dbase.rawQuery(countQuery, null);
cursor.close();
return cursor.getCount();
}
//deletes a single record
public void deleteRecord(myMessage msg){
SQLiteDatabase dbase = this.getWritableDatabase();
dbase.delete(TABLE_MESSAGES, COLUMN_ID + " = ?",
new String[] { String.valueOf(msg.get_id()) });
dbase.close();
}
}
Now, my problem is that I cannot figure out where (in which Activity or even in an Activity) or how I should create the instance of my dbHelper to make it available across my 2 activities. I know the singleton pattern is used frequently and the ContentProvider method is preferred. I don't feel that I'm ready to use ContentProviders with my very limited Android knowledge, so I would like to explore the singleton method. Can anyone please help me through this process?
how I should create the instance of my dbHelper to make it available
across my 2 activities
For what? You can just create a new instance for each Activity, since they use the very same database (outgoingMsgs.db).
For example, if you call addRecord in Activity A and use the different instance of dbHelper in Activity B to call getRecordCount, the inserted data will be selected.
You can use an Abstract class like this.
public abstract class DBAbstract extends Activity {
private DBHelper mDBHelper;
protected DBHelper getHelper(){
if(mDBHelper == null){
mDBHelper = OpenHelperManager.getHelper(this, DBHelper.class);
}
return mDBHelper;
}
protected int getDBVersion(){
return mDBHelper.getDatabaseVersion();
}
#Override
protected void onDestroy(){
super.onDestroy();
if(mDBHelper != null){
OpenHelperManager.releaseHelper();
mDBHelper = null;
}
}
And use it in your new activities.
public class YourActivity extends DBAbstract
I'm using this and it's working perfectly
Related
I am creating an app which manages ingredients. I want to create an SQLite database which will save all the ingredients which a user enters when they click the save button. However, for some reason my data won't save to the SQLite database. I cannot see what is wrong with the code at all.
DatabaseHelper.java
public class DatabaseHelper extends SQLiteOpenHelper
{
private static final String tag = "Database Helper";
private static final String TABLE_NAME = "products_registered";
private static final String col1 = "Name";
private static final String col2 = "Weight";
private static final String col3 = "Price";
private static final String col4 = "Description";
#Override
public void onCreate(SQLiteDatabase db)
{
String createTable = "CREATE TABLE IF NOT EXISTS " + TABLE_NAME +
" (_id INTEGER PRIMARY KEY AUTOINCREMENT," +
col1 + " TEXT NOT NULL," +
col2 + " DOUBLE NOT NULL," +
col3 + " DOUBLE NOT NULL," +
col4 + " TEXT);";
db.execSQL(createTable);
}
public DatabaseHelper(Context context)
{
super(context, TABLE_NAME, null, 1);
}
#Override
public void onUpgrade(SQLiteDatabase db, int i, int j)
{
db.execSQL("DROP TABLE IF EXISTS " + TABLE_NAME);
onCreate(db);
}
public boolean addData(String item1, String item2, String item3, String item4)
{
SQLiteDatabase db = this.getWritableDatabase();
ContentValues contentValues = new ContentValues();
contentValues.put(col1, item1);
contentValues.put(col2, item2);
contentValues.put(col3, item3);
contentValues.put(col4, item4);
Log.d(tag,"Adding name " + item1 + " to " + TABLE_NAME);
long result = db.insert(TABLE_NAME, null, contentValues);
db.close();
if(result == -1)
{
return false;
}
else
{
return true;
}
}
}
The Register Product Activity
public class RegisterProduct extends AppCompatActivity
{
DatabaseHelper databaseHelper;
private Button saveProductButton;
private EditText productName;
private EditText productWeight;
private EditText productPrice;
private EditText productDescription;
DatabaseHelper mDatabaseHelper;
#Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_register_product);
saveProductButton = (Button) findViewById(R.id.saveButton);
productName = (EditText) findViewById(R.id.enterName);
productWeight = (EditText) findViewById(R.id.enterWeight);
productPrice = (EditText) findViewById(R.id.enterPrice);
productDescription = (EditText) findViewById(R.id.enterDescription);
mDatabaseHelper = new DatabaseHelper(this);
try
{
saveProductButton.setOnClickListener(new View.OnClickListener()
{
public void onClick(View view)
{
String nameEntered = productName.getText().toString();
String weightEntered = productWeight.getText().toString();
String priceEntered = productPrice.getText().toString();
String descriptionEntered = productDescription.getText().toString();
addData(nameEntered, weightEntered, priceEntered, descriptionEntered);
productName.setText("");
}
});
}
finally
{
mDatabaseHelper.close();
}
}
public void addData(String newEntry1, String newEntry2, String newEntry3, String newEntry4)
{
mDatabaseHelper.addData(newEntry1, newEntry2, newEntry3, newEntry4);
}
I created a seperate class to create the database and table itself and then created the activity where I called the methods to save the data input in the activity displayed below.
Image of activity display
Your issue is most likely that you have changed the structure/schema of the database probably adding a column since running the App.
Typically you would get an SQLite exception when running the App indicating that the column was not found. However, as you have wrapped the addData in a try/finally construct the exception has been suppressed thus it appears that all is fine as the App doesn't crash or show anything in the log.
Wrapping database code in try constructs and trapping errors, as you have found can be confusing and it is suggested that you don't.
The reason why changing the schema/structure within the code for the onCreate method (the likely root cause of the issue), is that the onCreate method ONLY RUNS automatically when the database is created.
If the schema/structure of the database is changed then you need some way of either forcing the structure change or when developing an App, as the underlying data, should not need to be saved, then the easiest way is to delete the database and then rerun the App.
The database can be deleted by either deleting the App's data or by uninstalling the App (noting that any existing data will be lost).
An alternative, is to DROP the respective table(s) and recreate them. Frequently the onUpgrade method will written to do this. If this is the case (it is in your case) then another way of changing the structure is to trigger the onUpgrage method by increasing the version number pass to the SQLiteOpenHelper constructor (the super call). In your case you could change super(context, TABLE_NAME, null, 1); to be super(context, TABLE_NAME, null, 2);
After deleting the database or increasing the version number (preferably removing the try/finally construct from around the call to addData) and reunning the App, it may well just work. If not then the log should show any exceptions and problems can then be pinpointed.
The issue im having is when I attempt to execute the query it doesn't have access to the execSQL part of the command, ive been stuck on this for around an hour.
package com.androstock.myweatherapp;
import android.database.sqlite.SQLiteDatabase;
import static android.database.sqlite.SQLiteDatabase.openOrCreateDatabase;
public class Database {
SQLiteDatabase mydatabase = openOrCreateDatabase("database.db", null);
public void onCreate() {
mydatabase.execSQL("CREATE TABLE IF NOT EXISTS TutorialsPoint(Username VARCHAR,Password VARCHAR);");
mydatabase.execSQL("INSERT INTO TutorialsPoint VALUES('admin','admin');");
}
}
If somebody would be able to shed some light on where im going wrong with this that would be great thanks in advance
You are getting a little mixed up with your code.
I'd suggest making the Database class extend (be a subclass of) the SQLiteOpenHelper class and overriding the onCreate method to create the tables in the database.
e.g.
public class Database extends SQLiteOpenHelper {
// define constants so all names can be defined just once
public static final String DBNAME = "database.db"; // The database name
public static final int DBVERSION = 1; // The version (increase it to invoke the onUpgrade method to alter the DB structure)
public static final String TABLE_TUTORIALSPOINT = "tutorialspoint"; // The table name
public static final String COLUMN_USERNAME = "username"; // Columns
public static final String COLUMN_PASSWORD = "password";
SQLiteDatabase mDB; //Variable to hold the SqliteDatabase
//The Database class constructor
public Database(Context context) {
super(context, DBNAME, null, DBVERSION);
mDB = this.getWritableDatabase(); //<<<<<<<<<< store the Sqlite Database opening it, (if it doesn't exist then onCreate will be called)
}
#Override
public void onCreate(SQLiteDatabase db) {
String crt_tutorialspoint_table = "CREATE TABLE IF NOT EXISTS " + TABLE_TUTORIALSPOINT + "(" +
COLUMN_USERNAME + " VARCHAR, " +
COLUMN_PASSWORD + " VARCHAR" +
")";
db.execSQL(crt_tutorialspoint_table); // Create the table
// Preapre to insert a row using the SQLiteDatabase convenience insert method
ContentValues cv = new ContentValues();
cv.put(COLUMN_USERNAME,"admin");
cv.put(COLUMN_PASSWORD,"admin");
db.insert(TABLE_TUTORIALSPOINT,null,cv);
}
#Override
public void onUpgrade(SQLiteDatabase db, int i, int i1) {
}
}
You could then use this in an activity; something like :-
public class MainActivity extends AppCompatActivity {
Database mMYDatabaseHelper; // Declare a Database object called mMyDatabaseHelper <<< Note will be null untill instantiated (constructed)
SQLiteDatabase mMySQliteDatabase; // Declares an SQliteDatabase object again will be null
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main_activity);
mMYDatabaseHelper = new Database(this); // Construct the mMyDatabaseHelper (will create the database an insert the row)
mMySQliteDatabase = mMYDatabaseHelper.getWritableDatabase(); // Set/assign sqlite database from the helper to the SqliteDatabase object
// retrieve the rows in the table into a Cursor so the data can be extracted
// Note how the names are obtained from the constants as setup in the Database class.
Cursor mycursor = mMySQliteDatabase.query(Database.TABLE_TUTORIALSPOINT,null,null,null,null,null,null);
// loop through all rows of the cursor
while (mycursor.moveToNext()) {
String username = mycursor.getString(mycursor.getColumnIndex(Database.COLUMN_USERNAME)); // get the username from the current row
String password = mycursor.getString(mycursor.getColumnIndex(Database.COLUMN_PASSWORD)); // get the password from the current row
Log.d("TABLEINFO", "Row " + String.valueOf(mycursor.getPosition() + 1) + " has a username of " + username + " and a password of " + password);
}
mycursor.close(); // Done with the Cursor so close it
//ALL DONE if there are any rows in the table then there should be some output in the Log.
}
Note typically you would have a method in the the Database class (or elsewhere depedning upon coding conventions) such that returns a Cursor rather than access the database in the activities.
Output in the Log :-
D/TABLEINFO: Row 1 has a username of admin and a password of admin
Not too sure how to explain this but will give it my best.
I have created a database within my Android project:
public class FootySortItDatabase extends SQLiteOpenHelper {
private static final String DATABASE_NAME = "FootySortItDatabase";
private static final int DATABASE_VERSION = 1;
public FootySortItDatabase(Context context) {
super(context, DATABASE_NAME, null, DATABASE_VERSION);
}
#Override
public void onCreate(SQLiteDatabase db) {
final String SQL_CREATE_PLAYER_TABLE = "CREATE TABLE " +
PlayerDetails.PlayerTableEntry.TABLE_NAME + "(" +
PlayerDetails.PlayerTableEntry._ID + " INTEGER PRIMARY KEY AUTOINCREMENT, " +
PlayerDetails.PlayerTableEntry.COLUMN_PLAYER_NAME + " TEXT NOT NULL, " +
PlayerDetails.PlayerTableEntry.COLUMN_PLAYER_NUMBER + " TEXT NOT NULL, " +
PlayerDetails.PlayerTableEntry.COLUMN_IS_PLAYING + " INTEGER NOT NULL " +
");";
db.execSQL(SQL_CREATE_PLAYER_TABLE);
}
I have set the following contract:
public static final class PlayerTableEntry implements BaseColumns{
public static final String TABLE_NAME = "playerTable";
public static final String COLUMN_PLAYER_NAME = "playerName";
public static final String COLUMN_PLAYER_NUMBER = "playerNumber";
public static final String COLUMN_IS_PLAYING = "isPlaying";
}
I call a method which passes a DB and an array of details:
private SQLiteDatabase playerDatabase;
private Cursor getAllPlayers(){
playerDatabase = ComposeMessage.addPlayerToTheDatabase(playerDatabase, playerDataSet);
return playerDatabase.query(PlayerDetails.PlayerTableEntry.TABLE_NAME,
null,null,null,
null,null,null);
}
public static SQLiteDatabase addPlayerToTheDatabase(SQLiteDatabase playerDatabase, ArrayList<PlayerDetails> listGame) {
for (PlayerDetails i : listGame) {
ContentValues cv = new ContentValues();
cv.put(PlayerDetails.PlayerTableEntry.COLUMN_PLAYER_NAME, i.name);
cv.put(PlayerDetails.PlayerTableEntry.COLUMN_PLAYER_NUMBER, i.number);
playerDatabase.insert(PlayerDetails.PlayerTableEntry.TABLE_NAME, null, cv);
}
return playerDatabase;
}
Then, I pass the cursor into my RecyclerView :
public PlayerListRecyclerViewAdapter(Context context, Cursor cursor, ArrayList<PlayerDetails> aPlayerList,
int count) {
playerDataSet = aPlayerList;
this.pCursor = cursor;
pContext = context;
But my pCursor = 0. Which means that their are no rows in the table??
Via the terminal I use adb to navigate to the database, use SQLite3 on the DB to search the data of the table but it does not return anything:
sqlite> .tables
android_metadata playerTable
sqlite> select * from playerTable;
sqlite>
when I debug I can see during the insert part, that i.name and i.number has data to it and "it looks to be inserting it" but then why is cursor 0 and when I search the data, why is it not returning anything?
Not sure how to over come this...
There are 3 columns in the table. None of them can be null. When you write into the database, you are writing to 2 columns instead of 3. As a result, the insertion will fail.
To create a database, I recommend Schematic and use Stetho for debugging.
Here is my SQLiteOpenHelper class :
public class MySQLiteHelper extends SQLiteOpenHelper {
...
//all the code
SQLiteDatabase db;
String url;
String title;
String init_price;
String cur_price;
byte[] image;
// Database Version
private static final int DATABASE_VERSION = 1;
// Database Name
private static final String DATABASE_NAME = "ProductsDB";
// Products table name
private static final String TABLE_NAME = "products";
// Products Table Columns names
private static final String KEY_ID = "id";
private static final String KEY_URL = "url";
private static final String KEY_TITLE = "title";
private static final String KEY_INIT_PRICE = "init_price";
private static final String KEY_CUR_PRICE = "cur_price";
private static final String KEY_IMAGE = "image";
private static final String[] COLUMNS = {KEY_ID, KEY_URL, KEY_TITLE, KEY_INIT_PRICE,KEY_CUR_PRICE, KEY_IMAGE};
//An array of database
String[][] table = new String[10][5];
byte[][] images = new byte[10][1];
int len = 0; //no.of rows in the table
public MySQLiteHelper(Context context) {
super(context, DATABASE_NAME, null, DATABASE_VERSION);
}
#Override
public void onCreate(SQLiteDatabase db) {
this.db = db;
// SQL statement to create a products table
String CREATE_PRODUCTS_TABLE = "CREATE TABLE " + TABLE_NAME + " ( " +
"id INTEGER PRIMARY KEY AUTOINCREMENT, " +
"url TEXT, " +
"title TEXT, " +
"init_price TEXT," +
"cur_price TEXT," +
"image BLOB" +
" );";
// create products table
db.execSQL(CREATE_PRODUCTS_TABLE);
}
#Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
// Drop older table if existed
db.execSQL("DROP TABLE IF EXISTS " + TABLE_NAME);
// Create tables again
onCreate(db);
}
void read() {
int i = 0;
// 1. build the query
String query = "SELECT * FROM " + TABLE_NAME;
SQLiteDatabase db = this.getWritableDatabase();
Cursor cursor = db.rawQuery(query, null);
if (cursor.moveToFirst()) {
do {
table[i][0] = cursor.getString(0);
table[i][1] = cursor.getString(1);
table[i][2] = cursor.getString(2);
table[i][3] = cursor.getString(3);
table[i][4] = cursor.getString(4);
images[i] = cursor.getBlob(5);
i++;
} while (cursor.moveToNext());
}
}
String[][] getTable() {
return table;
}
}
My List class :
Here in this class, I want to create a listview from the values in the database.
How do I access those values?
List.java :
public class List extends Activity {
MySQLiteHelper obj = new MySQLiteHelper(this);
// I have all the methods to get required data from database.
// But I'm unable to call those methods directly. Why?
// I actually can access those methods from a service.
// But now, I'm getting "Cannot resolve method" error.
MySQLiteHelper obj = new MySQLiteHelper(this);
obj.getLength(); //I'm getting error here
obj.read(); // It says that it cannot resolve the method.
}
Why is that? I put the same code in a thread inside List.java class and then I'm able to access those methods.
MySQLiteHelper has no method for getLength() but that is ok. (more on this at the end)
the read() method is not public, and it really does nothing other than populate the table member/field of the MySQLiteHelper class.
You aren't leveraging the getTable() method you created (which also is not public).
lets go ahead and make getTable public (consider renaming this as it is really returning table data and not an actual database table, up to you tho), and anytime it is called, lets also call read() anytime this method is called
// renamed from getTable
public String[][] getTableData() {
read();
return table;
}
With this small modification, your usage should look something like this:
MySQLiteHelper obj = new MySQLiteHelper(this);
String[][] tabledata = obj.getTableData();
// get the Length (no getLength() method needed as we will poll the tabledata length property.
int length = tabledata.length;
I'm going to assume (and I apologize if I'm incorrect) that this your first crack at doing android databases. So it might be good to read a tutorial like this one:
http://www.androidhive.info/2011/11/android-sqlite-database-tutorial/
HTHS :)
I'm writing an Android app and am getting this error, but I'm not sure why. Can someone help me understand why I'm getting this error?
Cannot make a static reference to the non-static method updateScores(List<Score>) from the type DatabaseHandler
Here's the relevant code.
public class ScoreList extends SherlockFragmentActivity {
List<Score> listScore = new ArrayList<Score>();
public void updateListView() {
listViewScore.setAdapter(new ScoreListAdapter(ctx,
R.layout.score_row_item, listScore));
DatabaseHandler.updateScores(listScore);
}
}
Here's the DatabaseHandler class. I tried making the function static, but it won't work that way due to errors.
public class DatabaseHandler extends SQLiteOpenHelper {
private static final int DATABASE_VERSION = 1;
private static final String DATABASE_NAME = "scoreKeeper";
private static final String TABLE_GAMES = "games";
private static final String KEY_NAME = "name";
private static final String KEY_CHANGE = "scoreChange";
private static final String KEY_TOTAL = "scoreTotal";
public DatabaseHandler(Context context) {
super(context, DATABASE_NAME, null, DATABASE_VERSION);
}
#Override
public void onCreate(SQLiteDatabase db) {
String CREATE_GAMES_TABLE = "CREATE TABLE " + TABLE_GAMES + "("
+ KEY_NAME + " INTEGER PRIMARY KEY," + KEY_CHANGE + " TEXT,"
+ KEY_TOTAL + " TEXT" + ")";
db.execSQL(CREATE_GAMES_TABLE);
}
#Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
db.execSQL("DROP TABLE IF EXISTS " + TABLE_GAMES);
onCreate(db);
}
public void addScore(Score score) {
SQLiteDatabase db = this.getWritableDatabase();
ContentValues values = new ContentValues();
values.put(KEY_NAME, score.getName());
values.put(KEY_CHANGE, score.getScoreChange());
values.put(KEY_TOTAL, score.getScoreTotal());
// Inserting Row
db.insert(TABLE_GAMES, null, values);
db.close();
}
public List<Score> getAllScores() {
List<Score> scoreList = new ArrayList<Score>();
String selectQuery = "SELECT * FROM " + TABLE_GAMES;
SQLiteDatabase db = this.getWritableDatabase();
Cursor cursor = db.rawQuery(selectQuery, null);
if (cursor.moveToFirst()) {
do {
Score score = new Score("","","");
score.setName(cursor.getString(0));
score.setScoreChange(cursor.getString(1));
score.setScoreTotal(cursor.getString(2));
// Adding contact to list
scoreList.add(score);
} while (cursor.moveToNext());
}
return scoreList;
}
public void updateScores(List<Score> score) {
SQLiteDatabase db = this.getWritableDatabase();
db.execSQL("DROP TABLE IF EXISTS " + TABLE_GAMES);
onCreate(db);
ContentValues values = new ContentValues();
for(int i = 0; i < score.size(); i++){
values.put(KEY_NAME, score.get(i).getName());
values.put(KEY_CHANGE, score.get(i).getScoreChange());
values.put(KEY_TOTAL, score.get(i).getScoreTotal());
}
}
}
Because you are accessing the method with static reference
DatabaseHandler.updateScores(listScore);
means with class name..
You have to make a instance of DatabaseHandler class and use that method.
Or make a updateScores as static method like,
static public void updateScores()
But I have a doubt you have more codes in DatabaseHandler Class. (As its sqlite database helper class you are using Activity context in this class) so better to make instance of DatabaseHandler class and use method updateScores() with instance.
public void updateScores(List<Score> score)
change to
public static void updateScores(List<Score> score)
Why?
Because you are using static call
DatabaseHandler.updateScores(listScore)
Change the method updateScores to static as below:
public static void updateScores(List<Score> score)
DatabaseHandler dbh = new DatabaseHandler();
dbh.updateScores(listScore);
You can either make the Method
public void updateScores(List score)
as static or you can make an object of **DatabaseHandler ** in the caller class:
ScoreList
Error message tell you exectly problem: you reference non-static method in static manner.
There are two possibilities:
1. You want have static method (see other answers)
2. You want have non-static method. Then use this caller snippet:
DatabaseHandler helper = new DatabaseHandler();
helper.updateScores(listScore);
Error is because of you are calling the non static method using class name .to overcome this error you have to do the any one of the following things
1 . make your method updateScores as static or
2 . create an instance of DatabaseHandler and call using that object call the method.
calling method using class name requires method to be a static