I have this simple application that I'm currently writing as practice. Its purpose is to allow the user to send a quote and the author of that quote on a server (in this case a Parse.com backend I have registered) and then show those quotes to other users of the app randomly. So by opening the app, you get a random comment that someone has posted.
The way I'm trying to accomplish this is:
On start-up, the app connects to the Parse.com backend and downloads all the currently available quotes (I call those Inanity objects because the quotes are supposedly enlightened but should actually be stupid and nonsensical - anyway, doesn't matter). This is the code:
query.findInBackground(new FindCallback<ParseObject>() {
SQLi sqlite = new SQLi(MainActivity.this);
SQLiteDatabase dbz = sqlite.getWritableDatabase();
#Override
public void done(List<ParseObject> list, ParseException e) {
//sqlite.dbDelete();
if (e == null) {
int size = list.size();
for (int i = 0; i < size; i++) {
ParseObject object = list.get(i);
String author = object.get("author").toString();
String content = object.get("content").toString();
Inanity inan = new Inanity(content, author, 1);
Log.d("FOR LOOP" + i, inan.toString());
sqlite.insertInanity(dbz, inan);
}
}
}
});
Pretty simple. (dbz is an SQLiteDatabase acquired by calling getWritableDatabase(), by the way). The code below is the code for the SQLiteOpenHelper insertInanity() method that I use to put the retrieved data from the server in the local SQLite database:
public void insertInanity(SQLiteDatabase db, Inanity inanity) {
ContentValues values = new ContentValues();
values.put(CONTENT_INANITIES, inanity.getContent());
values.put(AUTHOR_INANITIES, inanity.getAuthor());
values.put(UPVOTE_INANITIES, inanity.getUpvotes());
db.insert(TABLE_INANITIES, null, values);
}
I pass an SQLiteDatabase object to the method simply to avoid having to call getWriteableDatabase() - I had some trouble with recurring calls if I kept doing that.
After writing the server data on the local SQLite database, the user is taken to an Activity that starts showing the quotes and the author of the quotes in a couple of TextViews. This is the code the retrieves a quote/author object from the SQLite database:
public Inanity retrieveInanity(int id) {
SQLiteDatabase db = this.getReadableDatabase();
Cursor cursor = db.query(TABLE_INANITIES, new String[] {
CONTENT_INANITIES, AUTHOR_INANITIES, UPVOTE_INANITIES },
ID_INANITIES + " = " + id, null, null, null, null);
if (cursor == null || cursor.getCount() == 0) {
return new Inanity("a", "b", 1);
}
else {
cursor.moveToFirst();
String contentL = cursor.getString(cursor
.getColumnIndex(CONTENT_INANITIES));
String authorL = cursor.getString(cursor
.getColumnIndex(AUTHOR_INANITIES));
int upvotesL = cursor.getInt(cursor
.getColumnIndex(UPVOTE_INANITIES));
Inanity inanity = new Inanity(contentL, authorL, upvotesL);
return inanity;
}
}
Finally, the quote to be displayed is randomly selected from the locally stored results thusly ("a" is an int variable declared earlier by the way)
final SQLi sql = new SQLi(this);
a = sql.getRowCount() + 1;
Button button = (Button) findViewById(R.id.button1);
button.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
Random rand = new Random();
int e = rand.nextInt(a);
if (e != 0) {
Inanity inanity = sql.retrieveInanity(e);
String content = inanity.getContent();
String author = inanity.getAuthor();
ImageView imageView = (ImageView) findViewById(R.id.downloaded);
TextView contentView = (TextView) findViewById(R.id.content);
TextView authorView = (TextView) findViewById(R.id.author);
Picasso.with(ShowActivity.this)
.load("http://img1.etsystatic.com/000/0/5356113/il_fullxfull.314192275.jpg")
.into(imageView);
contentView.setAlpha(0.9f);
authorView.setAlpha(0.9f);
Animation alpha = new AlphaAnimation(0.1f, 1.0f);
alpha.setDuration(2000);
contentView.setText(content);
authorView.setText(author);
contentView.startAnimation(alpha);
authorView.startAnimation(alpha);
}
else {
Toast.makeText(ShowActivity.this,
"Cursor trouble in wonderland!", Toast.LENGTH_LONG)
.show();
}
}
});
}
The getRowCount() method of the SQLi class is this:
public int getRowCount() {
int count = 1;
SQLiteDatabase db = getReadableDatabase();
Cursor cursor = db.rawQuery("SELECT * FROM " + TABLE_INANITIES, null);
if (cursor != null && cursor.getCount() > 0 && cursor.moveToFirst()) {
count = cursor.getCount();
}
return count;
}
For the most part. this works great. So, what's the problem, I hear you ask? Well, since I want to refresh the quotes every time the application starts up and get fresh ones from the server, the way I'm trying to accomplish that is by deleting the contents of the Inanity table of the database and re-populate them on start-up. So, I have created this method in the SQLi database helper class that's called dbDelete() which I call right at the start of the done() method of the FindCallback class of the Parse.com library (although I have commented this out from this code, it works swimmingly: it deletes the contents of the database just fine). Unfortunately, when I do that, it appears that the local SQLite database is not repopulated on app startup for some infernal reason, so I keep getting the placeholder "a", "b" and 1 values that are returned when the retrieveInanity() method cannot find cursor contents. Here is the dbDelete() method, which is quite simple:
public void dbDelete() {
SQLiteDatabase db = this.getWritableDatabase();
db.delete(TABLE_INANITIES, null, null);
}
I have been trying to solve this for quite some time and it's driving me crazy. I understand that the question is pretty convoluted, big and that it doesn't contain any catchy NullPointerExceptions/logcat action but any help would be appreciated. I must be missing something obvious related to the SQLite database use but I simply can't figure it out.
I wrote a similar app (one that made calls to a remote database and updated the info on local db). You should try using db.insertOrThrow. You will need to wrap the method in a Try...Catch statement. It will try to insert rows, and will throw an exception when a row already exists. You can then ignore the errors by leaving the Catch part blank. This will avoid the deletion and rebuild of the table.
try {
db.insertOrThrow(TABLE_INANITIES, null, values);
} catch SQLException s {
\\do nothing, as we don't care about existing rows
}
If you set up the quote server to have unique identifiers for the quote, then the local copy, your SQLite DB, will not insert duplicate entries. For example, your quote DB table on the server would look something like this
ID | Quote | Author
1 | blah | J. smith
Where the column ID is set as the unique identifier (or unique key). When your app calls the server and queries the remote DB, your local DB has only records that don't exist added to it.
You also want to make sure, I believe, that you update your cursor adapter in onResume().
Related
MainData myDBHlpr = new MainData(getActivity());
Cursor csr = myDBHlpr.getAllQuestions(UsageSettings.this);
while (csr.moveToFirst()) {
mMostMessagesSent.setText(csr.getString(csr.getColumnIndex("Reviews")));
mMostMessagesSent.setTextColor(Color.WHITE);
}
I checked row count and it showed 16
Then i checked
Log.d("TAG", csr.getString(cst.getColumnIndex("Reviews")));
And it showed error saying Log needs a message(Which means its null)
But why is it showing null even when the table has 16rows and column name exists
HELPER METHOD
public Cursor getAllQuestions(UsageSettings usageSettings) {
return this.getWritableDatabase().query(TABLE_NAME,null,null,null,null,null,null);
}
I have a little problem with SQLite and ContentProviders. First of all here is my current code:
public void loadFavoriteMovies(){
movieList.clear();
Cursor cursor = getContentResolver().query(FavoriteMoviesContract.MovieEntry.CONTENT_URI,
null, null, null, FavoriteMoviesContract.MovieEntry._ID);
if(cursor != null){
while(cursor.moveToNext()) {
MovieResults.Movie movie = new MovieResults.Movie(
cursor.getString(cursor.getColumnIndex(FavoriteMoviesContract.MovieEntry.COLUMN_TITLE)),
cursor.getString(cursor.getColumnIndex(FavoriteMoviesContract.MovieEntry.COLUMN_IMAGE)),
cursor.getString(cursor.getColumnIndex(FavoriteMoviesContract.MovieEntry.COLUMN_BACKDROP)),
cursor.getString(cursor.getColumnIndex(FavoriteMoviesContract.MovieEntry.COLUMN_RELEASE)),
Double.valueOf(cursor.getString(cursor.getColumnIndex(FavoriteMoviesContract.MovieEntry.COLUMN_RATING))),
cursor.getString(cursor.getColumnIndex(FavoriteMoviesContract.MovieEntry.COLUMN_PLOT)),
Integer.valueOf(cursor.getString(cursor.getColumnIndex(FavoriteMoviesContract.MovieEntry.COLUMN_ID)))
);
movieList.add(movie);
}
cursor.close();
}
Log.v("Size of Favorites:", String.valueOf(movieList.size()));
mAdapter.setmMovieList(movieList);
}
What I'm trying to do is I'm reading the SQLite database and putting the values there into a Movie object. But my problem is I want to do this operation using CursorLoader. How can I achieve this?
My main aim for doing this is because Cursor does not automatically update the value when I delete an item from the database and I need to reload the activity which the items are displayed. If anyone can offer an alternative way, that'd also be appreciated.
I'm working in an application that read phone contacts and use them in my application (Call history, Favorite contacts and All contacts).
My UI consist of tab host control and user can swap between them, as I want my data to be shared across all my activities and also to be saved in only one place.
So I have created a singleton class called data controller, and when I open the application I show loading screen until all data loaded.
The problem now that user is complaining because of waiting a lot of time about (1 minute) every time they open the application when he has a very large amount of contacts, so how can I optimize my code in a good way?
EDIT
This is the method that I'm using to get all contacts:
public static ArrayList<ContactInfo> getAllContactWithNumberAndNameAndPhoto(
Context context, boolean starred) {
ArrayList<ContactInfo> retList = new ArrayList<ContactInfo>();
ContentResolver cr = context.getContentResolver();
Cursor cur = null;
if (starred == true) {
cur = cr.query(ContactsContract.Contacts.CONTENT_URI, null,
"starred=?", new String[] { "1" }, null);
} else {
cur = cr.query(ContactsContract.Contacts.CONTENT_URI, null, null,
null, null);
}
if (cur.getCount() > 0) {
while (cur.moveToNext()) {
ContactInfo item = new ContactInfo();
String id = cur.getString(cur
.getColumnIndex(ContactsContract.Contacts._ID));
String name = cur
.getString(cur
.getColumnIndex(ContactsContract.Contacts.DISPLAY_NAME));
Uri photo = PhoneUtils.getPhotoUriFromID(context, id);
String starredValue = cur.getString(cur
.getColumnIndex(ContactsContract.Contacts.STARRED));
boolean isFav = false;
if (starredValue.equals("1"))
isFav = true;
if (Integer
.parseInt(cur.getString(cur
.getColumnIndex(ContactsContract.Contacts.HAS_PHONE_NUMBER))) > 0) {
Cursor pCur = cr.query(
ContactsContract.CommonDataKinds.Phone.CONTENT_URI,
null,
ContactsContract.CommonDataKinds.Phone.CONTACT_ID
+ " = ?", new String[] { id }, null);
while (pCur.moveToNext()) {
String phoneNo = pCur
.getString(pCur
.getColumnIndex(ContactsContract.CommonDataKinds.Phone.NUMBER));
item.addPhone(removeCharactersFromPhoneNumber(phoneNo));
}
pCur.close();
if (photo != null) {
item.setPhoto(photo.toString());
}
item.setName(name);
item.setFavorite(isFav);
item.setRecent(false);
retList.add(item);
}
}
cur.close();
}
return retList;
}
Please let me know if I can optimize this method.
Im surprised it takes that long to load the contacts from the device.
Have you profiled the app to see where the time is actually spent? Something seems wrong here.
If it truly takes that long to load from the system providers (due to the OS) you could cache the results (i.e. put in your own SQL db) so you can load quickly on each app visit (< 1 sec) and refresh from device in the background.
I guess that the bottleneck of your method is photo loading. Try to load everything except photos, and then show your activity, concurrently loading photos.
Also you can try to create your own app table that contains just the data you need. So you'll do less selects while loading contacts. But you would have to synchronize your table. You can do so concurrently.
I'm trying to compare the value of Markers with an id in my pre-made SQLite database. The reason being is so that I can have specific details for specific markers.
For example:
If the marker clicked has id = 1 then I wish to search the database for id '1' and then grab the details from that row. I thought it was simple enough to just loop through the database, but this doesn't seem to be working. My current code is:
final Cursor dbId = monDatabase.database.rawQuery("SELECT _id from monuments", null);
int idColumnCount = dbId.getColumnCount();
dbId.moveToFirst();
while(dbId.isAfterLast() == false) {
for(int f = 0; f < idColumnCount; f++) {
mainMarkerId = dbId.getInt(0);
map.setOnInfoWindowClickListener(new OnInfoWindowClickListener() {
#Override
public void onInfoWindowClick(Marker marker) {
if(marker.getId().equals("m" + mainMarkerId)){
Log.v("marker.getId()", "the id is: " + marker.getId());
selectDesc = monDatabase.database.rawQuery("SELECT description from monuments WHERE _id = " + mainMarkerId, null);
selectDesc.moveToFirst();
_description = selectDesc.getString(0);
Intent descriptionIntent = new Intent(MainActivity.this, DisplayData.class);
descriptionIntent.putExtra(EXTRA_MESSAGE, _description);
startActivity(descriptionIntent);
markerId.moveToNext();
}
}
});
dbId.moveToNext();
}
}
I'm unsure if it is possible this way, or maybe I have to use a Hashmap or something.
Any help is much appreciated,
Thanks!
EDIT:
I forgot to mention that I have all the Markers already displayed on the maps, so it's more of a case of being able to get the description for that location and then passing it through to a new Intent.
SOLUTION:
Just thought I'd post the solution on here if anyone else needed it. I didn't need a Hashtable, all I used was:
selectDesc = monDatabase.database.rawQuery("SELECT description from monuments WHERE title = '" + marker.getTitle() + "'", null);
No need for all the for loops or anything. Very very simple!
your best bet is to create a HashMap of the database elements using the database id as the key and the marker as the value so then you can pull from your database and have what marker you need
I'm developing a small application so I can get more experience of Android development. I wrote a method that list all of contact number and total messages (just like GoSMS or default SMS Application). But problem that I'm facing right now is slow performance. Below is what I have done to get the result.
Sample result: Mr AAA (10000)
Steps:
Get all SMS thread ID
Loop and get total messages belong to each thread ID.
Get the contact number belong to that thread.
Use that number and PhoneLookup to get contact name.
Here is the method:
public void populateContactList()
{
// Get all sms threads
Cursor smsAddressCursor = getContentResolver().query(
SMSCVar.CONTENT_URI,
new String[] { "DISTINCT "+SMSCVar.THREAD_ID},
null,
null,
null);
while(smsAddressCursor.moveToNext())
{
Contact c = new Contact();
// Get thread_id
String thread_id = smsAddressCursor.getString(smsAddressCursor.getColumnIndex(SMSCVar.THREAD_ID));
// Get total messages
Cursor totalMessage = getContentResolver().query(
SMSCVar.CONTENT_URI,
new String[] {"count("+SMSCVar.BODY+")"},
SMSCVar.THREAD_ID+" = ?",
new String[] {thread_id},
null);
totalMessage.moveToNext();
c.setNumberOfSMS(totalMessage.getInt(0));
totalMessage.close();
// Get number
Cursor number = getContentResolver().query(
SMSCVar.CONTENT_URI,
new String[] {SMSCVar.ADDRESS},
SMSCVar.THREAD_ID+" = ?",
new String[] {thread_id},
null);
number.moveToNext();
String pNumber = number.getString(0);
number.close();
// Get contact name
Uri uriPhonenumber = Uri.withAppendedPath(ContactsContract.PhoneLookup.CONTENT_FILTER_URI
, Uri.encode(pNumber));
Cursor contactDisplayName = getContentResolver().query(
uriPhonenumber,
new String[] {ContactsContract.PhoneLookup.DISPLAY_NAME},
null,
null,
null);
// If cursor is not null and has at least one result
if(!contactDisplayName.isNull(0) && contactDisplayName.getCount() > 0)
{
// Get contact name for display
contactDisplayName.moveToNext();
c.setContactName(contactDisplayName
.getString(contactDisplayName
.getColumnIndex(ContactsContract.PhoneLookup.DISPLAY_NAME)));
}
else
{
// Get contact number for display
c.setContactName(pNumber);
}
// Don't get confuse here, setContactNumber method is not used for display purpose.
c.setContactNumber(pNumber);
contactListAdapter.add(c);
contactDisplayName.close();
}
smsAddressCursor.close();
}
I've coded to close cursor properly but I still get GARBAGE COLLECTOR messages and slow retrieving time (5 seconds). My SMS is over 11000 messages.
So please assist me!
Thank you!
P/S: I'm not an English native speaker so I've tried my best to make my question easy to understand.