I need to know where to call the db.close() in my code. I've added it on onCreate() method, but when I need to use some methods it says database not open, and then I've removed from onCreate() and it says close() was not explicity called. so where should I close, could it be inside each method of the class??
here is the code:
public class HoursPerDayDataHelper {
private static final String DATABASE_NAME = "database.db";
private static final int DATABASE_VERSION = 1;
protected static final String TABLE_NAME = "table";
protected String TAG = "HoursPerDayDataHelper";
private Context context;
private SQLiteDatabase db;
OpenHelper openHelper = null;
public HoursPerDayDataHelper(Context context) {
this.context = context;
openHelper = new OpenHelper(this.context);
this.db = openHelper.getWritableDatabase();
openHelper.onCreate(db);
}
public void close() {
if (openHelper != null) {
openHelper.close();
}
}
public void deleteAll() {
this.db.delete(TABLE_NAME, null, null);
}
public String selectDuration(String date) {
String duration = "";
Integer value = 0;
String returnment = "";
Log.i(TAG, "date do select: " + date);
Cursor cursor = this.db.query(TABLE_NAME, new String[] { "duration" },
"date = ? ", new String[]{ date }, null, null, null);
Log.i(TAG, "cursor string " + cursor);
if (cursor.moveToFirst()) {
do {
Log.i(TAG, "dentro do if cursor");
duration = cursor.getString(0);
value += Integer.parseInt(duration);
} while (cursor.moveToNext());
returnment = Integer.toString(value);
}else{
Log.i(TAG, "bla bla bla");
}
if (cursor != null && !cursor.isClosed()) {
cursor.close();
}
return returnment;
}
public ArrayList<String[]> selectTopContacts() {
ArrayList<String[]> list1 = new ArrayList<String[]>();
Cursor cursor = this.db.query(TABLE_NAME, null, null, null, null, null,
"duration desc");
if (cursor.moveToFirst()) {
do {
if (cursor.getString(2) != "") {
String[] data = new String[4];
data[0] = cursor.getString(2);
data[1] = cursor.getString(4);
data[2] = cursor.getString(5);
data[3] = cursor.getString(7);
list1.add(data);
} else {
String[] data = new String[3];
data[1] = cursor.getString(4);
data[2] = cursor.getString(5);
data[3] = cursor.getString(7);
list1.add(data);
}
} while (cursor.moveToNext());
}
if (cursor != null && !cursor.isClosed()) {
cursor.close();
}
return list1;
}
public static class OpenHelper extends SQLiteOpenHelper {
OpenHelper(Context context) {
super(context, DATABASE_NAME, null, DATABASE_VERSION);
}
#Override
public void onCreate(SQLiteDatabase db) {
db.execSQL("CREATE TABLE IF NOT EXISTS "
+ TABLE_NAME
+ "(id INTEGER PRIMARY KEY AUTOINCREMENT, duration TIME, date DATE, current_time TIME)");
}
#Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
Log.w("HoursPerDay Database",
"Upgrading database, this will drop tables and recreate.");
db.execSQL("DROP TABLE IF EXISTS " + TABLE_NAME);
onCreate(db);
}
}
}
And this is my Activity:
public class HoursPerDay extends Activity{
private String LOG_TAG = "HoursPerDay";
private TextView mDateDisplay;
public String date;
private int mYear;
private int mMonth;
private int mDay;
private int newDay;
private String hpdData;
private HoursPerDayDataHelper hpd;
OpenHelper openHelper = new OpenHelper(HoursPerDay.this);
static final int DATE_DIALOG_ID = 0;
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.hours_per_day);
hpd = new HoursPerDayDataHelper(this);
// capture our View elements
mDateDisplay = (TextView) findViewById(R.id.dateDisplay);
// get the current date
final Calendar c = Calendar.getInstance();
mYear = c.get(Calendar.YEAR);
mMonth = c.get(Calendar.MONTH);
mDay = c.get(Calendar.DAY_OF_MONTH);
// display the current date (this method is below)
}
#Override
protected void onDestroy() {
super.onDestroy();
if (openHelper != null) {
openHelper.close();
}
if (hpd != null) {
hpd.close();
}
}
// the callback received when the user "sets" the date in the dialog
private DatePickerDialog.OnDateSetListener mDateSetListener = new DatePickerDialog.OnDateSetListener() {
public void onDateSet(DatePicker view, int year, int monthOfYear,
int dayOfMonth) {
mYear = year;
mMonth = monthOfYear;
mDay = dayOfMonth;
setBasicContent();
hpd.close();
}
};
protected Dialog onCreateDialog(int id) {
switch (id) {
case DATE_DIALOG_ID:
return new DatePickerDialog(this,
mDateSetListener,
mYear, mMonth, mDay);
}
return null;
}
#Override
public boolean onCreateOptionsMenu(Menu menu){
MenuInflater inflater = getMenuInflater();
inflater.inflate(R.layout.hoursperdaymenu, menu);
return true;
}
#Override
public boolean onOptionsItemSelected(MenuItem item){
switch(item.getItemId()){
case R.id.filter_by_day:
showDialog(DATE_DIALOG_ID);
return true;
case R.id.filter_by_user:
showDialog(DATE_DIALOG_ID);
return true;
default:
return super.onOptionsItemSelected(item);
}
}
public void setBasicContent() {
date = (mMonth + 1) + "/" + newDay + "/" + mYear;
hpdData = this.hpd.selectDuration(date);
mDateDisplay.setText(hpdData);
hpd.close();
}
}
I think there are 2 ways:
in onPause-method and check there isFinishing if yes -> close. Problem: if your app gets killed by app-killer, db remains open.
You open and close the DB each time (methods) you read/write.
EDIT:
Ok, I see why it could be caused. I think you misunderstood the usage of the SQLiteOpenHelper. You never have to call the onCreate-method.
Defently the better way is to make a DBHelper class and use it in a separate calls, lets say SQLDataHandler.
Your activity look good. I changed a few things, look if it helps. I'll mark them:
That's all what should be in the Helper class:
public static class OpenHelper extends SQLiteOpenHelper {
private static final String DATABASE_NAME = "database.db";
private static final int DATABASE_VERSION = 1;
protected static final String TABLE_NAME = "table";
protected String TAG = "HoursPerDayDataHelper";
Just leave it CREATE TABLE it gets only created/called if there isn't one existing.
I have seen errors occurring if the String is passed directly
#Override
public void onCreate(SQLiteDatabase db) {
String query = "CREATE TABLE "
+ TABLE_NAME
+ "(id INTEGER PRIMARY KEY AUTOINCREMENT, duration TIME, date DATE, current_time TIME)";
db.execSQL(query);
}
}
#Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
}
OpenHelper(Context context) {
super(context, DATABASE_NAME, null, DATABASE_VERSION);
}
}
To use it:
Just call in your DataHandler class :
OpenHelper helper = new OpenHelper(ctx);
// SQLiteDatabase db = helper.getReadableDatabase();
SQLiteDatabase db = helper.getWritableDatabase();
All other stuff, like deleting, adding and so on, should be done in a "DataHandler" class.
Just use the same two methods there to get your DB. At the end, when you are finished, you call just in you DataHandler class db.close().
Like this the activity itself never uses de DB directly. Better practice I think ;)
I hope it helps. For any other questions, just ask :)
EDIT2:
First, in general it should work with a inner class.
BUT: In case you want to add another table from another class it won't work anymore. Thats why it's the better way to put it in a separate class from beginning. It's even reusable (with some smal adjustments).
Put the code I posted in your class OpenHelper. Nothing more.
Then, put the data manipulation stuff in a class called something like: DataHandlerDB.
Code example:
package ...;
import java.util.ArrayList;
import java.util.List;
import android.content.ContentValues;
import android.content.Context;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
public class DataHandlerDB {
public static void persistAll(Context ctx, List<Module> moduleList) {
DatabaseHelper helper = new DatabaseHelper(ctx);
SQLiteDatabase db = helper.getWritableDatabase();
ContentValues values = new ContentValues();
for (Module m : moduleList) {
values.put("_id", m.get_id());
values.put("name", m.getModule());
db.insert("module", null, values);
}
db.close();
}
public static List<Module> findAll(Context ctx) {
List<Module> result = new ArrayList<Module>();
DatabaseHelper helper = new DatabaseHelper(ctx);
SQLiteDatabase db = helper.getReadableDatabase();
Cursor c = db.query(ModuleDB.TABLE_NAME, new String[] { ModuleDB.ID,
ModuleDB.MODULE}, null, null, null, null, null);
while (c.moveToNext()) {
Module m = new Module(c.getInt(0), c.getString(1));
result.add(m);
}
c.close();
db.close();
return result;
}
// Update Database entry
public static void update(Context ctx, Module m) {
DatabaseHelper helper = new DatabaseHelper(ctx);
SQLiteDatabase db = helper.getWritableDatabase();
ContentValues values = new ContentValues();
values.put("_id", m.get_id());
values.put("name", m.getModule());
db.update("module", values, null, null);
db.close();
}
public static void delete(Context ctx, Module m) {
DatabaseHelper helper = new DatabaseHelper(ctx);
SQLiteDatabase db = helper.getWritableDatabase();
ContentValues values = new ContentValues();
values.put("_id", m.get_id());
values.put("name", m.getModule());
db.delete("module","_id = m.get_id()", null);
db.close();
}
public static void createDB(Context ctx) {
DatabaseHelper helper = new DatabaseHelper(ctx);
SQLiteDatabase db = helper.getWritableDatabase();
db.close();
}
}
In order to be more efficient the methods are static, you won't need to create objects.
Use it like this: In your activity
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// get the a writable DB, in case it's not existing it gets created.
DataHandlerDB.createDB(this);
// get stuff out of DB
moduleList = DataHandlerDB.findAll(this);
adapter = new ArrayAdapter<Module>(this,
android.R.layout.simple_list_item_1, moduleList);
setListAdapter(adapter);
}
if you open in onCreate, then close in onDestroy
Related
I have an input page which submits 4 strings into the helper. The helper is supposed to get the data and put it into the listView.
When I insert my data, the app doesnt crash but it leads me to the listview page with no rows inflated. The 4 data inputs are all strings and is submitted through a button. I'm not too sure what is wrong with my code. Would appreciate some advice :)
listview code
public class MyActivitiesPage extends AppCompatActivity {
private Cursor model = null;
private ListView list;
private dbHandler helper = null;
private activityAdapter adapter = null;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.my_activities_page);
helper = new dbHandler(this);
list = findViewById(R.id.listView);
model = helper.getall();
adapter = new activityAdapter(this, model, 0);
list.setAdapter(adapter);
}
public static class activityHolder {
private TextView activitytext;
private TextView datetext;
private TextView timetext;
private TextView addresstext;
activityHolder(View row) {
activitytext = row.findViewById(R.id.activitytext);
datetext = row.findViewById(R.id.datetext);
timetext = row.findViewById(R.id.timetext);
addresstext = row.findViewById(R.id.addresstext);
}
void populateFrom(Cursor c, dbHandler helper){
activitytext.setText(helper.getActi(c));
datetext.setText(helper.getDate(c));
timetext.setText(helper.getTime(c));
addresstext.setText(helper.getAddr(c));
}
}
class activityAdapter extends CursorAdapter {
activityAdapter(Context context, Cursor cursor, int flags) {
super(context, cursor, flags);
}
public void bindView(View view, Context context, Cursor cursor) {
activityHolder holder = (activityHolder) view.getTag();
holder.populateFrom(cursor, helper);
}
public View newView(Context context, Cursor cursor, ViewGroup parent) {
LayoutInflater inflater = getLayoutInflater();
View row = inflater.inflate(R.layout.row, parent, false);
activityHolder holder = new activityHolder(row);
row.setTag(holder);
return (row);
}
}
helper code
public class dbHandler extends SQLiteOpenHelper {
private Context context;
private static final String DATABASE_NAME = "handler.db";
private static final int SCHEMA_VERSION = 1;
public dbHandler(#Nullable Context context){
super(context, DATABASE_NAME, null, SCHEMA_VERSION);
this.context = context;
}
public void onCreate(SQLiteDatabase db) {
//Will be called ones when the database is not created
db.execSQL("CREATE TABLE dbTable ( _id INTEGER PRIMARY KEY AUTOINCREMENT, acti TEXT, date TEXT, time TEXT, addr TEXT );");
}
public void onUpgrade (SQLiteDatabase db, int oldVersion, int newVersion) {
//Will not be called upon until SCHEMA_VERSION increases
//For future upgrades
}
public Cursor getall() {
return (getReadableDatabase().rawQuery("SELECT * FROM dbTable", null));
}
public Cursor getById(String id) {
String[] args = {id};
return (getReadableDatabase().rawQuery("SELECT _id, acti, date, time, addr FROM dbTable WHERE _ID = ?", args));
}
public void insert(String acti, String date, String time, String addr) {
ContentValues cv = new ContentValues();
cv.put("acti", acti);
cv.put("date", date);
cv.put("time", time);
cv.put("addr", addr);
long result = getWritableDatabase().insert("dbTable", "firstvalue", cv);
if(result == -1){
Toast.makeText(context, "Failed", Toast.LENGTH_SHORT).show();
}else {
Toast.makeText(context, "Added Successfully!", Toast.LENGTH_SHORT).show();
}
}
public void updateDetails(String id, String acti, String date, String time, String addr) {
ContentValues cv = new ContentValues();
String[] args = {id};
cv.put("acti", acti);
cv.put("date", date);
cv.put("time", time);
cv.put("addr", addr);
getWritableDatabase().update("dbTable", cv, " _ID = ?", args);
}
public String getID(Cursor c) { return (c.getString(0));}
public String getActi(Cursor c) { return (c.getString(1));}
public String getDate(Cursor c) { return (c.getString(2));}
public String getTime(Cursor c) { return (c.getString(3));}
public String getAddr(Cursor c) { return (c.getString(4));}
}```
So my app is a QR Code scanner. Currently it will read a QR code and display it back to user. I want to get it to also save this result to a database and then proceed to read back from it. Currently it does neither of the last two and I'm struggling to figure out which is causing the issue - either saving to the database or reading back from the database.
My Database code is this:
public class Database {
private static final String DATABASE_NAME = "QRCodeScanner";
private static final int DATABASE_VERSION = 1;
private static final String TABLE_NAME = "codes";
private OpenHelper mDbHelper;
private SQLiteDatabase mDb;
private final Context dbContext;
private static final String DATABASE_CREATE =
"CREATE TABLE " + TABLE_NAME + " (" +
"codeid INTEGER PRIMARY KEY AUTOINCREMENT, " +
"code TEXT NOT NULL);";
public Database(Context ctx) {
this.dbContext = ctx;
}
public Database open() throws SQLException {
mDbHelper = new OpenHelper(dbContext);
mDb = mDbHelper.getWritableDatabase();
return this;
}
public void close() {
mDbHelper.close();
}
public boolean createUser(String code) {
ContentValues initialValues = new ContentValues();
initialValues.put("codes", code);
return mDb.insert(TABLE_NAME, null, initialValues) > 0;
}
public ArrayList<String[]> fetchUser(String code) throws SQLException {
ArrayList<String[]> myArray = new ArrayList<String[]>();
int pointer = 0;
Cursor mCursor = mDb.query(TABLE_NAME, new String[] {"codeid", "code",
}, "code LIKE '%" + code + "%'", null,
null, null, null);
int codeNameColumn = mCursor.getColumnIndex("code");
if (mCursor != null){
if (mCursor.moveToFirst()){
do {
myArray.add(new String[3]);
myArray.get(pointer)[0] = mCursor.getString(codeNameColumn);
pointer++;
} while (mCursor.moveToNext());
} else {
myArray.add(new String[3]);
myArray.get(pointer)[0] = "NO RESULTS";
myArray.get(pointer)[1] = "";
}
}
return myArray;
}
public ArrayList<String[]> selectAll() {
ArrayList<String[]> results = new ArrayList<String[]>();
int counter = 0;
Cursor cursor = this.mDb.query(TABLE_NAME, new String[] { "codeid", "codes" }, null, null, null, null, "codeid");
if (cursor.moveToFirst()) {
do {
results.add(new String[3]);
results.get(counter)[0] = cursor.getString(0);
counter++;
} while (cursor.moveToNext());
}
if (cursor != null && !cursor.isClosed()) {
cursor.close();
}
return results;
}
private static class OpenHelper extends SQLiteOpenHelper {
OpenHelper(Context context) {
super(context, DATABASE_NAME, null, DATABASE_VERSION);
}
#Override
public void onCreate(SQLiteDatabase db) {
db.execSQL(DATABASE_CREATE);
}
#Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
db.execSQL("DROP TABLE IF EXISTS " + TABLE_NAME);
onCreate(db);
}
}
}
And my main java code is this.
public class MainActivity extends AppCompatActivity implements View.OnClickListener {
private Button Scan;
private ArrayList<String[]> viewall;
private TextView QR_output;
private IntentIntegrator ScanCode;
private ListView lv;
private ArrayList Search = new ArrayList();
ArrayList<String[]> searchResult;
Database dbh;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// this caused an error on earlier APKs which made the app switch from 17 to 27
setContentView(R.layout.activity_main);
// Defines the Scan button
Scan = findViewById(R.id.Scan);
// defines the output for text
QR_output = findViewById(R.id.QR_Output);
// looks for the user clicking "Scan"
Scan.setOnClickListener(this);
ScanCode = new IntentIntegrator(this);
// Means the scan button will actually do something
Scan.setOnClickListener(this);
lv = findViewById(R.id.list);
dbh = new Database(this);
dbh.open();
}
public void displayAll(View v){
Search.clear();
viewall = dbh.selectAll();
String surname = "", forename = "";
for (int count = 0 ; count < viewall.size() ; count++) {
code = viewall.get(count)[1];
Search.add(surname + ", " + forename);
}
ArrayAdapter<String> arrayAdapter = new ArrayAdapter<String>(
this,
android.R.layout.simple_list_item_1,
Search);
lv.setAdapter(arrayAdapter);
}
// will scan the qr code and reveal its secrets
#Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
IntentResult result = IntentIntegrator.parseActivityResult(requestCode, resultCode, data);
if (result != null) {
// if an empty QR code gets scanned it returns a message to the user
if (result.getContents() == null) {
Toast.makeText(this, "This QR code is empty.", Toast.LENGTH_LONG).show();
} else try {
// converts the data so it can be displayed
JSONObject obj = new JSONObject(result.getContents());
// this line is busted and does nothing
QR_output.setText(obj.getString("result"));
} catch (JSONException e) {
e.printStackTrace();
String codes = result.getContents();
boolean success = false;
success = dbh.createUser(codes);
// outputs the data to a toast
Toast.makeText(this, result.getContents(), Toast.LENGTH_LONG).show();
}
} else {
super.onActivityResult(requestCode, resultCode, data);
}
}
#Override
public void onClick(View view) {
// causes the magic to happen (It initiates the scan)
ScanCode.initiateScan();
}
}
Your issue could well be with the line initialValues.put("codes", code); as according to your table definition there is no column called codes, rather the column name appears to be code
As such using initialValues.put("code", code); may well resolve the issue.
Addititional
It is strongly recommended that you define and subsequently use constants throughout your code for all named
items (tables, columns, views trigger etc) and thus the value will always be identical.
e.g.
private static final String DATABASE_NAME = "QRCodeScanner";
private static final int DATABASE_VERSION = 1;
private static final String TABLE_NAME = "codes";
public static final String COLUMN_CODEID = "codeid"; //<<<<<<<<< example note making public allows the variable to be used elsewhere
public static final String COLUMN_CODE = "code"; //<<<<<<<<<< another example
private OpenHelper mDbHelper;
private SQLiteDatabase mDb;
private final Context dbContext;
private static final String DATABASE_CREATE =
"CREATE TABLE " + TABLE_NAME + " (" +
COLUMN_CODEID + " INTEGER PRIMARY KEY AUTOINCREMENT, " + //<<<<<<<<<<
COLUMN_CODE + " TEXT NOT NULL);"; //<<<<<<<<<<
........ other code omitted for brevity
public boolean createUser(String code) {
ContentValues initialValues = new ContentValues();
initialValues.put(COLUMN_CODE, code); //<<<<<<<<<< CONSTANT USED
return mDb.insert(TABLE_NAME, null, initialValues) > 0;
}
You would also likely encounter fewer issues by not using hard coded column offsets when extracting data from Cursor by rather using the Cursor getColumnIndex method to provide the offset.
e.g. instead of :-
results.get(counter)[0] = cursor.getString(0);
it would be better to use :-
results.get(counter)[0] = cursor.getString(cursor.getColumnIndex(COLUMN_CODEID));
I want to save the Score from a Quiz in a SQLite Database and change an image in another activity if the Score is 5. There is no error shown, but even if I score 5 the image won't change... How can I log the content of my database to check if the score was added or how can I find the mistake?
DB Helper:
public class DbHelper extends SQLiteOpenHelper {
private static final int DATABASE_VERSION = 7;
private static final String DATABASE_NAME = "CE";
public static final String SCORE_TABLE = "score";
public static final String COLUMN_ID = "ID";
public static final String COLUMN_SCORE = "SCORE";
public static final String COLUMN_MARKERID = "MARKERID";
private SQLiteDatabase dbase;
public DbHelper(Context context) {
super(context, DATABASE_NAME, null, DATABASE_VERSION);
}
#Override
public void onCreate(SQLiteDatabase db) {
dbase= db;
String create_query = "CREATE TABLE IF NOT EXITS " + SCORE_TABLE + " ( "
+ COLUMN_ID + " INTEGER PRIMARY KEY AUTOINCREMENT, "
+ COLUMN_SCORE + " INTEGER, "
+ COLUMN_MARKERID + " TEXT) ";
db.execSQL(create_query);
}
public void addScore (DbHelper dbh, Integer score, String markerID) {
dbase = dbh.getWritableDatabase();
ContentValues cv = new ContentValues();
cv.put(COLUMN_SCORE, score);
cv.put(COLUMN_MARKERID, markerID);
dbase.insert(SCORE_TABLE, null, cv);
}
public Cursor getScore(DbHelper dbh) {
dbase = dbh.getReadableDatabase();
String columns[] = {COLUMN_SCORE, COLUMN_MARKERID};
Cursor cursor = dbase.query(SCORE_TABLE, columns, null, null, null, null, null);
return cursor;
}
Write the Score into the Database after completing the Quiz:
public class ResultActivity extends Activity {
String markerID;
int score;
TextView t=(TextView)findViewById(R.id.textResult);
Button saveButton = (Button) findViewById(R.id.saveButton);
Context context = this;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.result_layout);
Bundle b = getIntent().getExtras();
score = b.getInt("score");
markerID = b.getString("markerID");
}
saveButton.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
DbHelper dbh = new DbHelper(context);
dbh.addScore(dbh,score,markerID);
Intent intent = new Intent(ResultActivity.this, Discover.class);
intent.putExtra("MarkerID", markerID);
startActivity(intent);
}
});
}
Discover class -> Check if score is 5 and change image if:
DbHelper dbh = new DbHelper(context);
Cursor cursor = dbh.getScore(dbh);
cursor.moveToFirst();
if (cursor.moveToFirst()) {
do {
if (Integer.parseInt(cursor.getString(0))== 5 && InfoUeberschrift.toString().equals(cursor.getString(1))){
ImageDone.setImageResource(R.drawable.markerdone);
}
}
while(cursor.moveToNext());
}
cursor.close();
}
The SQLiteDatabase insert function returns a long value, so if an error has occurred it returns -1.
'the row ID of the newly inserted row, or -1 if an error occurred'
http://developer.android.com/reference/android/database/sqlite/SQLiteDatabase.html
This can be used to see if the insert is happening correctly.
Or you can wrap in try and catch and print message like so
try {
//code
} catch(SQLiteException ex) {
Log.v("Insert into database exception caught", ex.getMessage());
return -1;
}
}
When I have issues using Java and SQLite i normally do it directly with the SQLite desktop version using Shell, as I find it easier to test out table design.
Hope this helps
Im having trouble getting individual items to delete in a database my application is using. I know the method gets called, but nothing in my list is ever removed. Im not getting any errors which is making it tough to track down. Assistance would be awesome.
public class MainActivity extends Activity{
//Global Variables
ListView lv;
Intent addM, viewM;
public DBAdapter movieDatabase;
String tempTitle, tempYear;
int request_Code = 1;
int request_code2 = 2;
SimpleCursorAdapter dataAdapter;
Cursor cursor;
Button addButton;
long testID;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//creates the database
movieDatabase = new DBAdapter(this);
movieDatabase.open();
//movieDatabase.deleteAllMovies();
//creates the intents to start the sub activities
addM = new Intent(this, AddMovie.class);
viewM = new Intent(this, MovieView.class);
}
//handles the return of the activity addMovie
public void onActivityResult(int requestCode, int resultCode,
Intent data)
{
super.onActivityResult(requestCode, resultCode, data);
if(resultCode == RESULT_OK)
{
switch(requestCode)
{
case 1:
dbAddMovie(data.getStringExtra("title"),
data.getStringExtra("year"));
break;
case 2:
testID = data.getLongExtra("rowid", -1);
dMovie(testID);
break;
}
}
}
//adds item to the movie list
public void dbAddMovie(String mT, String mY)
{
movieDatabase.open();
movieDatabase.insertMovie(mT, mY);
Toast.makeText(this, "Movie: " + mT + " added to database",
Toast.LENGTH_SHORT).show();
}
//deletes an entry into the database
public void dMovie(long rowid)
{
//Toast.makeText(this, "Deleting: " + rowid,
Toast.LENGTH_SHORT).show();
movieDatabase.deleteMovie(rowid);
movieDatabase.getAllMovies();
}
//displays the database as a list
public void displayListView()
{
addButton = (Button) findViewById(R.id.add);
addButton.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
startActivityForResult(addM, 1);
}
});
cursor = movieDatabase.getAllMovies();
//columns to use
String[] columns = new String[]
{
movieDatabase.KEY_TITLE,
};
//xml data to bind the data to
int[] to = new int[]
{
R.id.column2,
};
//adapter to display the database as a list
dataAdapter = new SimpleCursorAdapter(this,
R.layout.complexrow, cursor, columns, to, 0);
//gets the List view resource
lv = (ListView) findViewById(R.id.movielist);
//sets the list view to use the adapter
lv.setAdapter(dataAdapter);
//handles the list click events
lv.setOnItemClickListener(new
AdapterView.OnItemClickListener() {
#Override
public void onItemClick(AdapterView<?> parent, View
v, int position,
long id) {
Cursor cursor = (Cursor)
parent.getItemAtPosition(position);
Bundle mDet = new Bundle();
mDet.putString("title",
cursor.getString(cursor.getColumnIndex(movieDatabase.KEY_TITLE)));
mDet.putString("year",
cursor.getString(cursor.getColumnIndex(movieDatabase.KEY_YEAR)));
mDet.putInt("rId", position);
viewM.putExtras(mDet);
startActivityForResult(viewM, 2);
}
});
//dataAdapter.notifyDataSetChanged();
}
public void onResume()
{
super.onResume();
displayListView();
}
}
and my coresponding dbadapter class
public class DBAdapter {
public static final String KEY_ROWID = "_id";
public static final String KEY_TITLE = "title";
public static final String KEY_YEAR = "year";
private static final String TAG = "DBAdapter";
private static final String DATABASE_NAME = "MovieListDB";
private static final String DATABASE_TABLE = "MoviesTable";
private static final int DATABASE_VERSION = 1;
private static final String DATABASE_CREATE = "create table MoviesTable (_id
integer primary key autoincrement, " +
"title text not null, year not null);";
private final Context context;
private DatabaseHelper DBHelper;
private SQLiteDatabase db;
public DBAdapter(Context ctx)
{
this.context = ctx;
DBHelper = new DatabaseHelper(context);
}
private static class DatabaseHelper extends SQLiteOpenHelper
{
DatabaseHelper(Context context)
{
super(context, DATABASE_NAME, null, DATABASE_VERSION);
}
#Override
public void onCreate(SQLiteDatabase db) {
try{
db.execSQL(DATABASE_CREATE);
} catch (SQLException e)
{
e.printStackTrace();
}
}
#Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
Log.w(TAG, "Upgrading database from version " + oldVersion + " to "
+ newVersion +
"which will destroy all old data");
db.execSQL("DROP TABLE IF EXISTS MoviesTable");
onCreate(db);
}
}
public DBAdapter open() throws SQLException
{
db = DBHelper.getWritableDatabase();
return this;
}
public void close()
{
DBHelper.close();
}
public long insertMovie(String title, String year)
{
ContentValues initialValues = new ContentValues();
initialValues.put(KEY_TITLE, title);
initialValues.put(KEY_YEAR, year);
return db.insert(DATABASE_TABLE, null, initialValues);
}
public boolean deleteMovie(long rowID)
{
return db.delete(DATABASE_TABLE, KEY_ROWID + "='" + rowID+"'", null ) >-1;
}
public Cursor getAllMovies()
{
return db.query(DATABASE_TABLE, new String[] {KEY_ROWID, KEY_TITLE,
KEY_YEAR}, null, null, null, null, null);
}
public Cursor getMovie(long rowID) throws SQLException
{
Cursor mCursor =
db.query(true, DATABASE_TABLE, new String[] {KEY_ROWID,
KEY_TITLE, KEY_YEAR}, KEY_ROWID + "=" + rowID, null, null, null, null, null);
if(mCursor != null)
{
mCursor.moveToFirst();
}
return mCursor;
}
public boolean updateContact(long rowID, String title, String year)
{
ContentValues args = new ContentValues();
args.put(KEY_TITLE, title);
args.put(KEY_YEAR, year);
return db.update(DATABASE_TABLE, args, KEY_ROWID + "=" + rowID, null) > 0;
}
public void deleteAllMovies() {
int doneDelete = 0;
doneDelete = db.delete(DATABASE_TABLE, null, null);
}
}
You're using the position returned from your listview as the row id in your database. This won't necessarily match up with your autoincremented "_id" in your database. position is just what position in the list it is.
You might want to think about using movieDatabase.KEY_ROWID as the key for your intents. Right now I see a mix of "rowid", "rId", "_id", and KEY_ROWID. It would simplify thing to just use the same key everywhere when referring to the same thing.
It looks like you continuously add bundles to the viewM intent. Is that true? If that's not your intent, you should either create a new intent for each click, or remove the previous bundles first.
I'm assuming KEY_ROWID is actually the name of the column? Try the following:
public boolean deleteMovie(long rowID)
{
return db.delete(DATABASE_TABLE, KEY_ROWID + "=?", new String[] { String.valueOf(rowID) }) >-1;
}
The error says: column _id does not exists but the column is in the database (set as primary key) and this one is located in the external SD folder. I'm trying to return the values contained in the database on the initial load of the activity but it seems like the cursor is not returning anything.
public class ComponentsDbAdapter {
public static final String COLUMN_ID = "_id";
public static final String COLUMN_SUBSTRUCTURE = "substructure";
public static final String COLUMN_TYPE = "type";
public static final String COLUMN_ORDERNUM = "ordernum";
public static final String COLUMN_INSTALLATION = "installation";
private static final String TAG = "ComponentsDbAdapter";
private DatabaseHelper mDbHelper;
private SQLiteDatabase mDb;
private static final String DATABASE_PATH = Environment.getExternalStorageDirectory().getAbsoluteFile()+ "/DATABASE_BACKUP/IMPORTED/";
private static final String DATABASE_NAME = "android.db";
private static final String TABLE_NAME = "TAB_WORKSCPE";
private static final int DATABASE_VERSION = 1;
private final Context mCtx;
public ComponentsDbAdapter open() throws SQLException {
mDbHelper = new DatabaseHelper(mCtx);
mDb = mDbHelper.getWritableDatabase();
return this;
}
private static class DatabaseHelper extends SQLiteOpenHelper {
DatabaseHelper(Context context) {
super(context, DATABASE_PATH+DATABASE_NAME, null, DATABASE_VERSION);
}
#Override
public void onCreate(SQLiteDatabase db) {
db.query(TABLE_NAME, new String[] {COLUMN_ID, COLUMN_SUBSTRUCTURE, COLUMN_TYPE, COLUMN_ORDERNUM, COLUMN_INSTALLATION}, null, null, null, null, null);
}
#Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
Log.w(TAG, "Upgrading database from version " + oldVersion + " to " + newVersion + ", which will destroy all old data");
db.execSQL("DROP TABLE IF EXISTS " + TABLE_NAME);
onCreate(db);
}
}
public ComponentsDbAdapter(Context ctx) {
this.mCtx = ctx;
}
public void close() {
if (mDbHelper != null) {
mDbHelper.close();
}
}
public Cursor fetchComponentsByName(String inputText) throws SQLException {
Log.w(TAG, inputText);
Cursor mCursor = null;
if (inputText == null || inputText.length () == 0) {
mCursor = mDb.query(TABLE_NAME, new String[] {COLUMN_ID, COLUMN_SUBSTRUCTURE, COLUMN_TYPE, COLUMN_ORDERNUM, COLUMN_INSTALLATION}, null, null, null, null, null);
} else {
mCursor = mDb.query(true, TABLE_NAME, new String[] {COLUMN_ID, COLUMN_SUBSTRUCTURE, COLUMN_TYPE, COLUMN_ORDERNUM, COLUMN_INSTALLATION}, COLUMN_TYPE + " like '%" + inputText + "%'", null, null, null, null, null);
}
if (mCursor != null) {
mCursor.moveToFirst();
}
return mCursor;
}
public Cursor fetchAllComponents() {
Cursor mCursor = mDb.query(TABLE_NAME, new String[] {COLUMN_ID, COLUMN_SUBSTRUCTURE, COLUMN_TYPE, COLUMN_ORDERNUM, COLUMN_INSTALLATION}, null, null, null, null, null);
if (mCursor != null) {
mCursor.moveToFirst();
}
return mCursor;
}
}
public class AndroidListViewCursorAdaptorActivity extends Activity {
private ComponentsDbAdapter dbHelper;
private SimpleCursorAdapter dataAdapter;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
dbHelper = new ComponentsDbAdapter(this);
dbHelper.open();
//Generate ListView from SQLite Database
displayListView();
}
private void displayListView() {
Cursor cursor = dbHelper.fetchAllComponents();
// The desired columns to be bound
String[] columns = new String[] {
ComponentsDbAdapter.COLUMN_SUBSTRUCTURE,
ComponentsDbAdapter.COLUMN_TYPE,
ComponentsDbAdapter.COLUMN_ORDERNUM,
ComponentsDbAdapter.COLUMN_INSTALLATION
};
// the XML defined views which the data will be bound to
int[] to = new int[] {
R.id.inst,
R.id.subdt,
R.id.type,
R.id.ordernum,
};
// create the adapter using the cursor pointing to the desired data
//as well as the layout information
dataAdapter = new SimpleCursorAdapter(
this,
R.layout.country_info,
cursor,
columns,
to,
0);
ListView listView = (ListView) findViewById(R.id.listView1);
// Assign adapter to ListView
listView.setAdapter(dataAdapter);
listView.setOnItemClickListener(new OnItemClickListener() {
#Override
public void onItemClick(AdapterView<?> listView, View view,
int position, long id) {
// Get the cursor, positioned to the corresponding row in the result set
Cursor cursor = (Cursor) listView.getItemAtPosition(position);
// Get the state's capital from this row in the database.
String compSubdt = cursor.getString(cursor.getColumnIndexOrThrow("subdt"));
Toast.makeText(getApplicationContext(), compSubdt, Toast.LENGTH_SHORT).show();
}
});
EditText myFilter = (EditText) findViewById(R.id.myFilter);
myFilter.addTextChangedListener(new TextWatcher() {
public void afterTextChanged(Editable s) {
}
public void beforeTextChanged(CharSequence s, int start,int count, int after) {
}
public void onTextChanged(CharSequence s, int start,int before, int count) {
dataAdapter.getFilter().filter(s.toString());
}
});
dataAdapter.setFilterQueryProvider(new FilterQueryProvider() {
public Cursor runQuery(CharSequence constraint) {
return dbHelper.fetchComponentsByName(constraint.toString());
}
});
}
}
It doesn't appear from your code that you've created the table yet, so no columns will be found.
You do this within the onCreate method by creating a query to create the table. In your code you appear to be doing a select rather than create.
private static final String TABLE_CREATE = "create table "
+ TABLE_NAME
+ "("
+ COLUMN_ID + " integer primary key autoincrement, "
+ COLUMN_TYPE + " text not null default '', "
+ COLUMN_ORDERNUM + " integer not null default 0, "
+ COLUMN_INSTALLATION + " integer not null default 0, "
+ COLUMN_SUBSTRUCTURE + " text not null default ''"
+ ");";
#Override
public void onCreate(SQLiteDatabase database) {
database.execSQL(TABLE_CREATE);
}
To store this on the external storage, you'll need to override getDatabasePath(...). A similar solution is here https://stackoverflow.com/a/8385537/935779
#Override
public File getDatabasePath(String name) {
// reference where you would like the file to be here.
File result = new File(getExternalFilesDir(null), name);
return result;
}
I believe you'll want to override this with your Application class since it's a member of ContextWrapper.
The method getDatabaseFile(...) is used inside of openOrCreateDatabase(...) to determine the location.
Alternatively you could just override openOrCreateDatabase(...) and set the file location there.
I don't think you can change or even specify the location of the database, only the name.
Leave off the path and don't try to put it in External Storage - let Android determine the path.
Ok, this took me almost week and a lot of stress but here is the solution. I started to go through a lot of tutorials and got it working in this one:
http://www.mysamplecode.com/2012/11/android-database-content-provider.html
I extracted the database from the virtual device and manually added more data. Then copied the database to the desired folder on my device folder (Its just to make sure the database consistency/columns are exactly the same). Then changed MyDatabaseHelper class as follows:
public class MyDatabaseHelper extends SQLiteOpenHelper {
private static final String DATABASE_PATH = Environment.getExternalStorageDirectory().getAbsoluteFile()+ "/MYFOLDER/";
private static final String DATABASE_NAME = "TheWorld.db";
private static final int DATABASE_VERSION = 1;
MyDatabaseHelper(Context context) {
super(context, DATABASE_PATH+DATABASE_NAME, null, DATABASE_VERSION);
}
#Override
public void onCreate(SQLiteDatabase db) {
CountriesDb.onCreate(db);
}
#Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
CountriesDb.onUpgrade(db, oldVersion, newVersion);
}
}
Don't forget to add permissions to your manifest:
<uses-permission
android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
Done!
If you read through the posts above the answer is based on Kirks advice so reading his recommended link helps. I still have more tests to do just in case my database structure was wrong before.