well, the other post, i mean :
Should there be one SQLiteOpenHelper for each table in the database?
i was doubtful about having one or more Helpers for the application, well, the anwser was 1 helper, many columns.
now the second part, i'm actually making this tutorial:
http://www.vogella.com/articles/AndroidSQLite/article.html
but i would like to do it with more than 1 table on the database, ok, i've done the work and now i'm try to make the so called Data Access Object, "DAO". the question is the same, is better to have a single DAO, or is better to have one for each table (and so one for each table class) OR lastly, a single DAO class with all the "actions" i could use in the application... ?
this is what in the tutorial is called DAO:
package de.vogella.android.sqlite.first;
import java.util.ArrayList;
import java.util.List;
import android.content.ContentValues;
import android.content.Context;
import android.database.Cursor;
import android.database.SQLException;
import android.database.sqlite.SQLiteDatabase;
public class CommentsDataSource {
// Database fields
private SQLiteDatabase database;
private MySQLiteHelper dbHelper;
private String[] allColumns = { MySQLiteHelper.COLUMN_ID,
MySQLiteHelper.COLUMN_COMMENT };
public CommentsDataSource(Context context) {
dbHelper = new MySQLiteHelper(context);
}
public void open() throws SQLException {
database = dbHelper.getWritableDatabase();
}
public void close() {
dbHelper.close();
}
public Comment createComment(String comment) {
ContentValues values = new ContentValues();
values.put(MySQLiteHelper.COLUMN_COMMENT, comment);
long insertId = database.insert(MySQLiteHelper.TABLE_COMMENTS, null,
values);
Cursor cursor = database.query(MySQLiteHelper.TABLE_COMMENTS,
allColumns, MySQLiteHelper.COLUMN_ID + " = " + insertId, null,
null, null, null);
cursor.moveToFirst();
Comment newComment = cursorToComment(cursor);
cursor.close();
return newComment;
}
public void deleteComment(Comment comment) {
long id = comment.getId();
System.out.println("Comment deleted with id: " + id);
database.delete(MySQLiteHelper.TABLE_COMMENTS, MySQLiteHelper.COLUMN_ID
+ " = " + id, null);
}
public List<Comment> getAllComments() {
List<Comment> comments = new ArrayList<Comment>();
Cursor cursor = database.query(MySQLiteHelper.TABLE_COMMENTS,
allColumns, null, null, null, null, null);
cursor.moveToFirst();
while (!cursor.isAfterLast()) {
Comment comment = cursorToComment(cursor);
comments.add(comment);
cursor.moveToNext();
}
// Make sure to close the cursor
cursor.close();
return comments;
}
private Comment cursorToComment(Cursor cursor) {
Comment comment = new Comment();
comment.setId(cursor.getLong(0));
comment.setComment(cursor.getString(1));
return comment;
}
}
Thanks in advance.
This is the class that will help your application to interact with your database. You should have only one DAO class with all methods you need. (Different methods for different tables.)
Check example below:
public void insertInTableA (String[]) //or any args
{
//Logic for table A row insertion
}
public void insertInTableB (String[]) //or any args
{
//Logic for table B row insertion
}
public void dltFromTableA (String where) //or any args
{
//Logic for table A row deletion
}
public void dltFromTableB (String where) //or any args
{
//Logic for table B row deletion
}
//Other function as per requirement
Related
I am learning android programming and I am writing a small SQL to list app to get "comments" out of DB and put them on a list using a custom CursorAdapter. It all seems to be functional BUT on add/delete the list doesn't repopulate (delete seems to not function at all). I am calling changeCursor() but it does not appear as though my list updates. After a fresh run I can see the results of the previous run. Any help is greatly appreciated.
Here is my code:
CommentsDataSource.java:
import android.content.ContentValues;
import android.content.Context;
import android.database.Cursor;
import android.database.SQLException;
import android.database.sqlite.SQLiteDatabase;
import java.util.ArrayList;
import java.util.List;
public class CommentsDataSource {
private MySQLiteHelper SQLhelper;
private SQLiteDatabase database;
public CommentsDataSource (Context context) {
// Create a link to database:
SQLhelper = new MySQLiteHelper(context);
}
public void open() throws SQLException {
database = SQLhelper.getWritableDatabase();
}
public void close () {
database.close();
}
public Comment createComment (String comment) {
ContentValues values = new ContentValues();
values.put(MySQLiteHelper.COLUMN_COMMENT, comment);
long id = database.insert(MySQLiteHelper.TABLE_COMMENTS, null, values);
Cursor cursor = database.rawQuery("SELECT * FROM " + MySQLiteHelper.TABLE_COMMENTS + " WHERE " + MySQLiteHelper.COLUMN_ID + " = " + id, null);
cursor.moveToFirst();
Comment newComment = new Comment();
newComment.setId(cursor.getLong(0));
newComment.setComment(cursor.getString(1));
cursor.close();
return newComment;
}
public void deleteComment (Comment comment) {
long id = comment.getId();
System.out.println("Comment deleted with id: " + id);
database.delete(MySQLiteHelper.TABLE_COMMENTS, MySQLiteHelper.COLUMN_ID
+ " = " + id, null);
}
public List<Comment> getAllComments () {
List<Comment> list = new ArrayList<>();
Cursor cursor = database.rawQuery("SELECT * FROM " + MySQLiteHelper.TABLE_COMMENTS, null);
cursor.moveToFirst();
Comment newComment = new Comment();
while (! cursor.isAfterLast()) {
newComment.setId(cursor.getLong(0));
newComment.setComment(cursor.getString(1));
list.add(newComment);
cursor.moveToNext();
}
cursor.close();
return list;
}
public Cursor getCursor() {
List<Comment> list = new ArrayList<>();
Cursor cursor = database.rawQuery("SELECT * FROM " + MySQLiteHelper.TABLE_COMMENTS, null);
return cursor;
}
}
MainActivity:
public class MainActivity extends ListActivity {
private CommentsDataSource dataSource;
private Context context;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
this.context= this;
dataSource = new CommentsDataSource(this);
dataSource.open();
List<Comment> values = dataSource.getAllComments();
/*ArrayAdapter<Comment> adapter = new ArrayAdapter<Comment>(this, android.R.layout.simple_expandable_list_item_1, values);
setListAdapter(adapter);*/
Cursor cursor = dataSource.getCursor();
ListView lvItems = (ListView) findViewById(android.R.id.list);
CustomCursorAdapter adapter = new CustomCursorAdapter(this, cursor);
lvItems.setAdapter(adapter);
}
public void onClick(View view) {
#SuppressWarnings("unchecked")
Cursor cursor = dataSource.getCursor();
CustomCursorAdapter adapter = new CustomCursorAdapter(context, cursor);
Comment comment = null;
switch (view.getId()) {
case R.id.add:
String[] comments = new String[] { "Cool", "Very nice", "Hate it" };
int nextInt = new Random().nextInt(3);
// save the new comment to the database
comment = dataSource.createComment(comments[nextInt]);
break;
case R.id.delete:
if (adapter.getCount() > 0) {
Cursor d = (Cursor) adapter.getItem(0);
comment = new Comment();
comment.setComment(d.getColumnName(1));
dataSource.deleteComment(comment);
}
break;
}
Cursor newcursor = dataSource.getCursor();
adapter.changeCursor(newcursor);
}
}
customCursorAdapter:
public class CustomCursorAdapter extends CursorAdapter {
public CustomCursorAdapter(Context context, Cursor c) {
super(context, c, 0);
}
#Override
public View newView(Context context, Cursor cursor, ViewGroup parent) {
return LayoutInflater.from(context).inflate(R.layout.list_item, parent, false);
}
#Override
public void bindView(View view, Context context, Cursor cursor) {
TextView type = (TextView) view.findViewById(R.id.setType_textView);
TextView name = (TextView) view.findViewById(R.id.exerciseName_textView);
name.setText(cursor.getString(1));
}
}
The application am working on will initially have one database with a table, say tbl_usr which will have only one record. Basically we are trying to keep one user per device. When the user logs in from the device with an auth code, his details will be fetched from server and stored in database. Next time if he tries to enter different auth code, which is valid but is not in table then he will not be allowed to proceed. Below is a common DBHelper class.
But whatever approach am trying, I am getting databaselocked exception, when tried for the 2nd time login. I've referred various links where in it was suggested to use different instance of database within method, but still it comes with error. Below is my Helper class
public class DBaseHelper extends SQLiteOpenHelper {
private static String CREATE_TABLE;
private static final String DATABASE_NAME="IPDB";
private static String UserMessage="";
private int tableType=0;
private ContentValues cValues;
private Cursor cursor;
public enum TableTypes{
Table1
};
public DBaseHelper(Context context){
super(context,context.getExternalFilesDir(null).getAbsolutePath()+"/"+DATABASE_NAME,null,1);
}
#Override
public void onCreate(SQLiteDatabase db){
TableTypes tableTypes=TableTypes.values()[tableType];
switch (tableTypes){
case Table1:
CREATE_TABLE="CREATE TABLE IF NOT EXISTS tbl_usr....";
break;
default:
break;
}
db.execSQL(CREATE_TABLE);
db.close();
System.out
.println("onCreate Method Done.");
}
#Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion){
/*db.execSQL("DROP TABLE IF EXISTS "+LOGIN_TABLE);*/
onCreate(db);
}
/*this is the method which gets called from other class Like*/
/*helper.insertRecord(tableParams);*/
public HashMap<String,String> insertRecord(HashMap<String,String> dbaseParams){
HashMap<String,String> response=new HashMap<String,String>();
tableType=Integer.parseInt(dbaseParams.get("tableType"));
cValues = new ContentValues();
String TableName="";
TableTypes tableTypes=TableTypes.values()[tableType];
switch (tableTypes){
case Table1:
String AuthCode=dbParams.get("AuthCode");
/*if user exists then check if its the same user*/
if( CheckUserRecordExists(AuthCode) && empty(UserMessage) ){
response.put("isSuccess","true");
return response;
}
else {
if (!empty(UserMessage)) {
response.put("isSuccess", "false");
response.put("message",UserMessage);
return response;
}
/*add new user
Fill cValues declared above*/
TableName = "Table1";
}
break;
default:
break;
}
SQLiteDatabase dataBase = getWritableDatabase();
/*insert data into database*/
try {
dataBase.beginTransaction();
long rowID = dataBase.insertOrThrow(TableName, null, cValues);
dataBase.setTransactionSuccessful();
}
catch(Exception ex){
ex.printStackTrace();
}
finally {
dataBase.close();
}
response.put("isSuccess", "true");
return response;
}
private boolean CheckUserRecordExists(String authCode){
UserMessage="";
SQLiteDatabase dataBase=getReadableDatabase();
/*Exception here when comes for 2nd time after new installation*/
cursor = dataBase.query("Table1", new String[]{"COUNT(*)"}, null, null, null, null, null);
cursor.moveToFirst();
int iCount=cursor.getInt(0);
/*check if any record exist*/
if(iCount>0){
dataBase.close();
if(!cursor.isClosed()) cursor.close();
/*check if the code entered matches with the record existing*/
if(!CheckIsDataAlreadyInDBorNot("Table1","Auth_Code",authCode))
{
UserMessage="Invalid login!";
return false;
}
else return true;
}
else{
dataBase.close();
if(!cursor.isClosed()) cursor.close();
return false;
}
}
private boolean CheckIsDataAlreadyInDBorNot( String TableName,
String dbfield, String fieldValue) {
/*checking if user is same user*/
SQLiteDatabase dataBase=getReadableDatabase();
String[] columns = { dbfield };
String selection = dbfield + " =?";
String[] selectionArgs = { fieldValue };
String limit = "1";
Cursor cursor = dataBase.query(TableName, columns, selection, selectionArgs, null, null, null, limit);
boolean exists = (cursor.getCount() > 0);
cursor.close();
dataBase.close();
return exists;
}
public static boolean empty( final String s ) {
return s == null || s.trim().isEmpty();
}
}
I know its a huge code, but logic is simple. But the problem is database lock. Could someone let me know how I can make sure that database is always in valid state on each operation?
You have beginTransaction() but no matching calls to endTransaction(). An ongoing transaction keeps the database in a locked state and also keeps the internal reference count nonzero, so close() does not yet actually close the database.
The conventional pattern for transactional operations is
db.beginTransaction();
try {
// db operations that can throw
db.setTransactionSuccessful();
} finally {
db.endTransaction();
}
Also in your onCreate() you should not be closing the database since you don't own it.
I am populating contact list details to list view successfully.
My code:
String order = ContactsContract.CommonDataKinds.Phone.DISPLAY_NAME + " ASC";
Cursor curLog = getContentResolver().query(ContactsContract.CommonDataKinds.Phone.CONTENT_URI, null,null,null,order);
How can I avoid the duplicate data In List view as the contact details is repeating if its joined contact i.e. joined with both phone and Google?. The screen is like
I want to select programmatically only 1 name not the both? Any Idea how I can select?
I have used a rough way to avoid this problem which helped me so much and working nicely.
i.e
Use local database (SQLite) to avoid duplicate data by make phone number to unique.
I have made one SQLite DB to handle this problem:
ContactMerger.java:
public class ContactMerger {
private static final String CONTACT_TABLE = "_contact_table";
private static final String CONTACT_ID = "_contactId";
private static final String CONTACT_NAME = "_contactName";
private static final String CONTACT_MOBILE_NUMBER = "_contactNumber";
private static final String CONTACT_DATE = "_contactDate";
private static final int DATABASE_VERSION = 1;
private static final String DATABASE_NAME = "DB_Contact";
private final Context context;
private SQLiteDatabase ourDatabase;
private DbHelper ourHelper;
private class DbHelper extends SQLiteOpenHelper {
public DbHelper(Context context) {
super(context, DATABASE_NAME, null, DATABASE_VERSION);
}
#Override
public void onCreate(SQLiteDatabase db) {
// TODO Auto-generated method stub
String contactQuery = "CREATE TABLE " + CONTACT_TABLE + " ("
+ CONTACT_ID + " INTEGER PRIMARY KEY AUTOINCREMENT, "
+ CONTACT_NAME + " TEXT NOT NULL, " + CONTACT_DATE
+ " TEXT NOT NULL, " + CONTACT_MOBILE_NUMBER
+ " TEXT NOT NULL UNIQUE);";
db.execSQL(contactQuery);
}
#Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
// TODO Auto-generated method stub
db.execSQL("DROP TABLE IF EXISTS " + CONTACT_TABLE);
onCreate(db);
}
}
public ContactMerger(Context context) {
this.context = context;
}
public ContactMerger open() throws SQLException {
ourHelper = new DbHelper(context);
ourDatabase = ourHelper.getWritableDatabase();
return this;
}
public void close() {
ourHelper.close();
}
// Insert Data to Contact Table
public long insertContacts(String name, String number, String date) throws SQLException {
ContentValues cv = new ContentValues();
cv.put(CONTACT_NAME, name);
cv.put(CONTACT_DATE, date);
cv.put(CONTACT_MOBILE_NUMBER, number);
Log.d("Insert Data", cv.toString());
return ourDatabase.insert(CONTACT_TABLE, null, cv);
}
//Get Contact details from Contact Table
public ArrayList<ContactHolder> getContactDetails() throws Exception{
ArrayList<ContactHolder> contactDetails = new ArrayList<ContactHolder>();
String[] columns = new String[] { CONTACT_ID, CONTACT_NAME, CONTACT_DATE, CONTACT_MOBILE_NUMBER };
Cursor c = ourDatabase.query(CONTACT_TABLE, columns, null, null, null,null, null);
int iContactName = c.getColumnIndex(CONTACT_NAME);
int iContactDate = c.getColumnIndex(CONTACT_DATE);
int iContactMobileNumber = c.getColumnIndex(CONTACT_MOBILE_NUMBER);
for (c.moveToFirst(); !c.isAfterLast(); c.moveToNext()) {
ContactHolder data = new ContactHolder();
data.setName(c.getString(iContactName));
data.setDate(c.getString(iContactDate));
data.setNumber(c.getString(iContactMobileNumber));
contactDetails.add(data);
}
return contactDetails;
}
}
Here ContactHolder is just a getter/setter class to handle contact entities.
First I inserted all Contact information once in my MainActivity by the help of a background thread. It prevents to insert the contact info multiple times.
Something like:
private ArrayList<ContactHolder> contactHolder;
private void setCallLogs(Cursor managedCursor) {
contactHolder = new ArrayList<ContactHolder>();
int _number = managedCursor
.getColumnIndex(ContactsContract.CommonDataKinds.Phone.NUMBER);
int _name = managedCursor
.getColumnIndex(ContactsContract.CommonDataKinds.Phone.DISPLAY_NAME);
int _id = managedCursor
.getColumnIndex(ContactsContract.CommonDataKinds.Phone._ID);
while (managedCursor.moveToNext()) {
ContactHolder holder = new ContactHolder();
holder.setNumber(managedCursor.getString(_number));
holder.setName(managedCursor.getString(_name));
holder.setDate(managedCursor.getString(_id));
contactHolder.add(holder);
}
Thread t = new Thread(new Runnable() {
#Override
public void run() {
for(int i=0; i<contactHolder.size(); i++){
try{
ContactMerger merger = new ContactMerger(HomeActivity.this);
merger.open();
merger.insertContacts(contactHolder.get(i).getName(),
contactHolder.get(i).getNumber(),
contactHolder.get(i).getdate());
merger.close();
} catch(Exception e){
e.printStackTrace();
}
}
}
});
t.start();
}
At last I gtt all contact information inside an Asynctask(doInbackground()) and put in adapter/listview in its onPostExecute() method in the class I want to show.
Here:
#Override
protected ArrayList<ContactHolder> doInBackground(String... parameters) {
ArrayList<ContactHolder> filterContacts = new ArrayList<ContactHolder>();
ContactMerger merger = new ContactMerger(Aaja_Contact.this);
merger.open();
try {
filterContacts = merger.getContactDetails();
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
merger.close();
return filterContacts;
}
I believe this may happen if the contact number is stored in two different ways/formats: for example in your case the number for Akshay may be saved as 982-0123456 and 9820123456
Did you try displaying the number along with the Name by including the Number as well in the list view?
You need to retrieve the data from the Cursor to HashSet (which don't allows duplicate itmes) and then pass the HashSet object to your ListView's Adapter
This is a dump solution but it will help you:
ListView listView;
Set<String> listItems;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
listView = (ListView) findViewById(R.id.listView);
listItems = new HashSet<String>();
String order = ContactsContract.CommonDataKinds.Phone.DISPLAY_NAME + " ASC";
Cursor curLog = getContentResolver().query(ContactsContract.CommonDataKinds.Phone.CONTENT_URI, null,null,null,order);
if(curLog != null) {
while(curLog.moveToNext()) {
String str = curLog.getString(curLog.getColumnIndexOrThrow(ContactsContract.CommonDataKinds.Phone.DISPLAY_NAME_PRIMARY));
listItems.add(str);
}
}
String listString = listItems.toString();
listString = listString.substring(1,listString.length()-1);
String[] newList = listString.split(", ");
ArrayAdapter<String> adapter = new ArrayAdapter<String>(MainActivity.this, android.R.layout.simple_list_item_1, newList);
listView.setAdapter(adapter);
}
Good luck..
Since you're querying Phone.CONTENT_URI, I'm assuming you're looking for contacts with phone number.. then you can use ContactsContract.Contacts.CONTENT_URI
String order = ContactsContract.CommonDataKinds.Phone.DISPLAY_NAME + " ASC";
Cursor curLog = getContentResolver().query(ContactsContract.Contacts.CONTENT_URI, null,
ContactsContract.Contacts.HAS_PHONE_NUMBER + "=?", new String[] { "1" }, order);
Its because the listview is showing both normal contacts as well as whatsapp( or like this) linked contacts. Best is to store all the contacts in a Database and then retrieve the contacts using "select distinct..." command of SQL.
String order = ContactsContract.CommonDataKinds.Phone.DISPLAY_NAME + " ASC";
Cursor phones = getContentResolver().query(ContactsContract.CommonDataKinds.Phone.CONTENT_URI, null,null,null, order);
String temp_name="";
while (phones.moveToNext())
{
String name=phones.getString(phones.getColumnIndex(ContactsContract.CommonDataKinds.Phone.DISPLAY_NAME));
if (name.equals(temp_name))
continue;
temp_name=name;
//add name to your list or adapter here`enter code here`
}
phones.close();
When you loop through your contacts, here's something you can do in the looping statement while you add your next object to avoid creating a duplicate contact:
UserList object=new UserList(name,number);
if(arrayList.size()==0)
{
arrayList.add(object);
}
if(arrayList.size()>0) {
position = arrayList.size();
if (!(arrayList.get(arrayList.position - 1).getName().equals(number) ||
arrayList.get(position - 1).getNumber().equals(number)))
{
arrayList.add(object); }
}
Here, in my object of 'UserList' class, the name and number would repeat from the contact list, so this code just checks if the previous object has the same name or number before adding in the new one.
Old question but still relevant. I could not find suitable query to skip dupes with contentresolver but it's possible to compare all contacts for duplicates by phone number.
With com.googlecode.libphonenumber library it's really simple. Method public MatchType isNumberMatch(CharSequence firstNumber, CharSequence secondNumber) compares number, coutry code, mask and return one of MatchType enum value.
This is my problem: I have an SQLite DB set up to store some values. In this case, its pretty simple. Two columns (_id, name). I know data is getting stored in the database and I know the cursor I am using to pass that information is also populating. However, when I try to add a cursor value to an ArrayList, I get an error. This is the code for my problem method:
public void sqltest(LDatabase LConnector){
cursor = LConnector.getAllRecords(); //gets the cursor from my db
try{
cursor.moveToFirst();}
catch (Exception e){
log.i("Failed", "Couldn't moveToFirst"); //I get a successful moveToFirst
}
while(cursor.moveToNext()){
try{
getWork.add(cursor.getString(cursor.getColumnIndex("name")));
} catch (Exception h){
Log.i("FAILED", cursor.getString(cursor.getColumnIndex("name")));
}
}
I set up that last log to tell me the value of the cursor at that position. The values entered for the DB always print out so I know the cursor is being populated. Anyone have any idea what I am doing wrong here?
EDIT: LogCat shows these two lines after this is called when an activity starts:
04-12 23:26:26.606: I/MOVED(9478): MOVED TO FIRST
04-12 23:26:26.606: I/FAILED(9478): test1
There are no more verbose errors that describe it better, that is why I am so confused. It just doesn't get stored to the AssetList at all.
This is the code for the getAllRecords() mehod:
public Cursor getAllLifts() throws SQLiteException
{
open();
return database.rawQuery("SELECT * FROM contacts"+";", null);
}
Additionally, here is the create code + the code used for inserting:
Create:
String createQuery = "CREATE TABLE contacts" +
"(_id auto_increment integer primary key," +
"name text);";
db.execSQL(createQuery); // execute the query
Insert:
open(); // open the database
try{
//add more later to get data to insert from usr, just for testing
database.execSQL("INSERT INTO contacts(name) VALUES('test1')");
database.execSQL("INSERT INTO contacts(name) VALUES('test2')");}
catch (Exception insert){
Log.i("INSERT", "INSERT FAILED");
}
close(); // close the database
EDIT: rest of the activity w/ imports
package com.jydev.llogger2;
import java.util.ArrayList;
import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.view.View.OnClickListener;
import android.content.Context;
import android.database.Cursor;
import android.widget.Button;
import android.widget.EditText;
import android.widget.Toast;
public class Create extends Activity implements OnClickListener {
Button buttonNext; //continue button
Button buttonBack; //back button
EditText nameEditText; //editText w/ workout name
EditText commentEditText; //editText w/ comments
Intent info; //intent used to pass extras
String wName = null;
String wComments = null;
ArrayList<String> getWork;
Cursor cursor;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.create);
commentEditText = (EditText)findViewById(R.id.commentEditText);
buttonBack = (Button)findViewById(R.id.create_back);
buttonNext = (Button)findViewById(R.id.create_continue);
nameEditText = (EditText)findViewById(R.id.nameEditText);
LtDatabase LConnector = new LDatabase(this);
LConnector.insert();
sqltest(LConnector);
}
There are several wrong things. First moveToFirst() returns a boolean so you will never get an exception thrown. Second the while statement will skip one row since you call moveToNext(), thus the first row is skipped. Finally, you get an exception because you did not initialize getwork.
public void sqltest(LDatabase LConnector)
{
cursor = LConnector.getAllRecords(); //gets the cursor from my db
if (cursor.moveToFirst())
{
do
{
try{
getWork.add(cursor.getString(cursor.getColumnIndex("name")));
} catch (Exception h){
Log.i("FAILED", cursor.getString(cursor.getColumnIndex("name")));
}
while (cursor.moveToNext());
}
}
}
In your declaration of getWork
ArrayList<String> getWork = new ArrayList<String>();
for my queries i'm using the following
public ArrayList<T> findAllBy(String selection) {
final SQLiteDatabase db = HELPER.getReadableDatabase();
final ArrayList<T> result = new ArrayList<T>();
final Cursor cursor = db.query(TABLE_NAME, SELECTED_COLS, WHERE, null, null, null, null);
try {
cursor.moveToFirst();
while (!cursor.isAfterLast()) {
T model = CREATEOBJECT(cursor);
result.add(model);
cursor.moveToNext();
}
return result;
} finally {
cursor.close();
db.close();
}
}
Where:
HELPER is a instance extendes from SQLiteOpenHelper
TABLE_NAME is a string with the name of the table
SELECTED_COLS is a String[] array with the columns names i want get
WHERE is a string like "COL_NAME = 'value'" or null
CREATEOBJECT is a method for create Object of type T want in my ArrayList<T>
Example CREATEOBJECT
public T CREATEOBJECT(Cursor cursor) {
new T(
cursor.getInt(0), cursor.getString(1)
);
}
public class T {
private long id;
private String name;
T(int id, String name) {
this.id = id;
this.name = name;
}
//...
}
I have an SQLiteDatabase whose data is managed by a Content Provider. I'm using a tabbed layout. The first tab has the ability to add rows to the database, whereas the second tab shows items from the database. As I add items to the database from the first tab, the changes should be reflected when I move to the other tab.
Data is being added to the database correctly, and upon first opening of the app, all the current data (and anything new added in a previous version of the app) will appear. However, adding new items to the database is not reflected in the ListFragment.
#Override
public void onClick(View v) {
if(v == addSale) {
Item item = new Item(rn(), null, 100);
data.add(item);
total += item.getAmount();
} else if(v == save) {
for(Item i: data) {
ContentValues cv = new ContentValues();
cv.put(DatabaseProvider.COLUMN_COST, i.getAmount());
cv.put(DatabaseProvider.COLUMN_ITEM, i.getItem());
cv.put(DatabaseProvider.COLUMN_PERSON, i.getPerson());
this.getActivity().getContentResolver().insert(DatabaseProvider.CONTENT_URI, cv);
}
total = 0;
data.clear();
} else if(v == clear) {
data.clear();
total = 0;
}
items.notifyDataSetChanged();
totalView.setText(Item.format(total));
}
Here is where I add the items to the database specifically with these lines:
ContentValues cv = new ContentValues();
cv.put(DatabaseProvider.COLUMN_COST, i.getAmount());
cv.put(DatabaseProvider.COLUMN_ITEM, i.getItem());
cv.put(DatabaseProvider.COLUMN_PERSON, i.getPerson());
this.getActivity().getContentResolver().insert(DatabaseProvider.CONTENT_URI, cv);
As I said earlier, items are put into the database correctly, so I'm reasonably sure that this is correct.
Here is the insert method of my DatabaseProvider
#Override
public Uri insert(Uri uri, ContentValues initialValues) {
if (sUriMatcher.match(uri) != TABLE) {
throw new IllegalArgumentException("Invalid URI: " + uri);
}
if (initialValues == null) {
initialValues = new ContentValues();
}
SQLiteDatabase db = dbHelper.getWritableDatabase();
long rowId = db.insert(TABLE_SALES, COLUMN_COST, initialValues);
if (rowId > 0) {
Uri newUri = ContentUris.withAppendedId(CONTENT_URI, rowId);
getContext().getContentResolver().notifyChange(uri, null);
return newUri;
}
throw new SQLException("Failed to insert row into: " + uri);
}
From the various tutorials and other SO questions, it seems as if
getContext().getContentResolver().notifyChange(uri, null);
is the key to getting it to update, and it's there, and is called. But nothing updates.
Finally, my list fragment that display all of the data.
package org.worldsproject.android.barcode;
import org.worldsproject.android.barcode.database.DatabaseProvider;
import android.database.Cursor;
import android.os.Bundle;
import android.support.v4.app.LoaderManager;
import android.support.v4.content.CursorLoader;
import android.support.v4.content.Loader;
import android.support.v4.widget.CursorAdapter;
import android.support.v4.widget.SimpleCursorAdapter;
import com.actionbarsherlock.app.SherlockListFragment;
public class RunningTotal extends SherlockListFragment implements
LoaderManager.LoaderCallbacks<Cursor> {
public static final String TAG = "Running Total";
public static final int RUNNING_TOTAL_ID = 1;
private SimpleCursorAdapter adapter;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
String[] uiBindFrom = { DatabaseProvider.COLUMN_PERSON };
int[] uiBindTo = { R.id.titled };
adapter = new SimpleCursorAdapter(
getActivity().getApplicationContext(), R.layout.list_title,
null, uiBindFrom, uiBindTo,
CursorAdapter.FLAG_REGISTER_CONTENT_OBSERVER);
setListAdapter(adapter);
getLoaderManager().initLoader(RUNNING_TOTAL_ID, null, this);
}
#Override
public Loader<Cursor> onCreateLoader(int arg0, Bundle arg1) {
String[] projection = { DatabaseProvider.COLUMN_ID,
DatabaseProvider.COLUMN_PERSON};
return new CursorLoader(getActivity(), DatabaseProvider.CONTENT_URI,
projection, null, null, null);
}
#Override
public void onLoadFinished(Loader<Cursor> arg0, Cursor cursor) {
// TODO Auto-generated method stub
adapter.swapCursor(cursor);
}
#Override
public void onLoaderReset(Loader<Cursor> arg0) {
// TODO Auto-generated method stub
adapter.swapCursor(null);
}
}
It's nearly a direct clone of http://mobile.tutsplus.com/tutorials/android/android-sdk_loading-data_cursorloader/ and at the moment just displays the name column of each row in the database. It shows previous entries, but as I've said, doesn't update. What step am I missing to get it to update?
I think your
getContext().getContentResolver().notifyChange(uri, null);
may be fine.
I can't see your query function for your DatabaseProvider, but did you remember to set the notificationUri for the cursor you are returning in your query function?
In your query() function of the DatabaseProvider, you should set the notification Uri for the cursor, or else the cursor won't get a notification even if you do a notifyChange():
public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) {
// blah blah
cursor.setNotificationUri(getContext().getContentResolver(), uri);
}
I had a similar problem with a Fragment using a view pager. I realized that I didn't have a ContentObserver registered, so all I did was add this code to the onResume()
getActivity().getContentResolver().registerContentObserver(sUri, true, myObserver);
and have myObserver declared as a member variable:
new ContentObserver(new Handler()) {
#Override
public void onChange(boolean selfChange) {
getActivity().getSupportLoaderManager().restartLoader(); // With appropriate loader args here
}
});
And make sure to unregister is in the onPause();
That seemed to fix my problem, assuming that the ContentProvider is calling notifyChange correctly (It looks like you are, though).
Hope this helps you out!
I think the error is here:
Uri newUri = ContentUris.withAppendedId(CONTENT_URI, rowId);
getContext().getContentResolver().notifyChange(uri, null);
return newUri;
You should call the notifyChange method with the uri of the element you just added, which is newUri.
So the notify becomes:
getContext().getContentResolver().notifyChange(newUri, null);