Android Time Ranking - java

I am currently having a problem with the time ranking on my android application. I am working with the sqlite database that will store the time the user finished solving the problem. I will use that sqlite database later to display the rankings of the user that played the game.
For example, the player finished the puzzle for about 1:25 (1 minute and 25 seconds), the app will store 1:25 on the sqlite database. But I'm having a problem on doing that.
I can store it on the sqlite as a string but I can't use ORDER
BY.
I tried storing it as an int but output says: invalid int
I tried storing it as a string while removing the colon (:) but the database on returns "125".
My question is: What is the best way to store that specific time value on sqlite databases?
I read this http://www.sqlite.org/lang_datefunc.html documentation about the Time and Date Functions but it seems it can't be applied on custom time. It only applies on either the date now or current time. Please I need help on this. I'm an android and sqlite amateur. This project is my thesis for this sem.
Any comments and suggestions are accepted. Thanks in advance!
By the way, this is the code that retrieves all the data on my sqlite. It's a testing program, to test if my codes/to modify codes and see if they are working.
try {
String selectQuery = "SELECT * FROM " + DatabaseHelper.TABLE_OUTLET + " WHERE category LIKE '" + cat + "' ORDER BY category ASC";
Cursor cursor = db.rawQuery(selectQuery, null);
if (cursor.getCount() > 0) {
while (cursor.moveToNext()) {
// Read columns data
int outlet_id = cursor.getInt(cursor.getColumnIndex("outlet_id"));
String outlet_name = cursor.getString(cursor.getColumnIndex("outlet_name"));
String outlet_type = cursor.getString(cursor.getColumnIndex("outlet_type"));
String category = cursor.getString(cursor.getColumnIndex("category"));
// dara rows
TableRow row = new TableRow(context);
row.setLayoutParams(new TableLayout.LayoutParams(TableLayout.LayoutParams.MATCH_PARENT,
TableLayout.LayoutParams.WRAP_CONTENT));
String[] colText = {outlet_name, outlet_type, category};
for (String text : colText) {
TextView tv = new TextView(this);
tv.setLayoutParams(new TableRow.LayoutParams(TableRow.LayoutParams.WRAP_CONTENT,
TableRow.LayoutParams.WRAP_CONTENT));
tv.setGravity(Gravity.CENTER);
tv.setTextSize(16);
tv.setPadding(5, 5, 5, 5);
tv.setText(text);
row.addView(tv);
}
tableLayout.addView(row);
}
}
db.setTransactionSuccessful();
} catch (SQLiteException e) {
e.printStackTrace();
} finally {
db.endTransaction();
// End the transaction.
db.close();
// Close database
}
}

1:25 convert this to seconds only.
That is 1 * 60 + 25 = 85. So store 85.
Every time you want to store it do this. And you can do the reverse on restoring.
In example,
sec = 85 % 60 = 25
min = 85 / 60 = 1
You will get 1:25

Following the suggestion of #ShreyashSSarnayak I managed to fix this problem.
This is the resulting code:
try {
String selectQuery = "SELECT * FROM " + DatabaseHelper.TABLE_OUTLET + " WHERE category LIKE '" + cat + "' ORDER BY outlet_type ASC";
Cursor cursor = db.rawQuery(selectQuery, null);
if (cursor.getCount() > 0) {
while (cursor.moveToNext()) {
// Read columns data
int outlet_id = cursor.getInt(cursor.getColumnIndex("outlet_id"));
String outlet_name = cursor.getString(cursor.getColumnIndex("outlet_name"));
int outlet_type = Integer.parseInt(cursor.getString(cursor.getColumnIndex("outlet_type")));
String category = cursor.getString(cursor.getColumnIndex("category"));
// dara rows
TableRow row = new TableRow(context);
row.setLayoutParams(new TableLayout.LayoutParams(TableLayout.LayoutParams.MATCH_PARENT,
TableLayout.LayoutParams.WRAP_CONTENT));
int m, s;
s = outlet_type % 60;
m = outlet_type / 60;
String[] colText = {outlet_name, String.format("%02d:%02d", m, s), category};
for (String text : colText) {
TextView tv = new TextView(this);
tv.setLayoutParams(new TableRow.LayoutParams(TableRow.LayoutParams.WRAP_CONTENT,
TableRow.LayoutParams.WRAP_CONTENT));
tv.setGravity(Gravity.CENTER);
tv.setTextSize(16);
tv.setPadding(5, 5, 5, 5);
tv.setText(text);
row.addView(tv);
}
tableLayout.addView(row);
}
}
db.setTransactionSuccessful();
} catch (SQLiteException e) {
e.printStackTrace();
} finally {
db.endTransaction();
// End the transaction.
db.close();
// Close database
}
}

I'll suggest you to convert your time to seconds and store it as integer.

Use INTEGER as database type instead. Check here available types. Then you need to convert the INTEGER value to time format using standard java clases or any other if you like.
Using INTEGER you will be also able to sort them easily.

Related

Improve performance of loading 100,000 records from database

We created a program to make the use of the database easier in other programs. So the code im showing gets used in multiple other programs.
One of those other programs gets about 10,000 records from one of our clients and has to check if these are in our database already. If not we insert them into the database (they can also change and have to be updated then).
To make this easy we load all the entries from our whole table (at the moment 120,000), create a class for every entry we get and put all of them into a Hashmap.
The loading of the whole table this way takes around 5 minutes. Also we sometimes have to restart the program because we run into a GC overhead error because we work on limited hardware. Do you have an idea of how we can improve the performance?
Here is the code to load all entries (we have a global limit of 10.000 entries per query so we use a loop):
public Map<String, IMasterDataSet> getAllInformationObjects(ISession session) throws MasterDataException {
IQueryExpression qe;
IQueryParameter qp;
// our main SDP class
Constructor<?> constructorForSDPbaseClass = getStandardConstructor();
SimpleDateFormat itaTimestampFormat = new SimpleDateFormat("yyyyMMddHHmmssSSS");
// search in standard time range (modification date!)
Calendar cal = Calendar.getInstance();
cal.set(2010, Calendar.JANUARY, 1);
Date startDate = cal.getTime();
Date endDate = new Date();
Long startDateL = Long.parseLong(itaTimestampFormat.format(startDate));
Long endDateL = Long.parseLong(itaTimestampFormat.format(endDate));
IDescriptor modDesc = IBVRIDescriptor.ModificationDate.getDescriptor(session);
// count once before to determine initial capacities for hash map/set
IBVRIArchiveClass SDP_ARCHIVECLASS = getMasterDataPropertyBag().getSDP_ARCHIVECLASS();
qe = SDP_ARCHIVECLASS.getQueryExpression(session);
qp = session.getDocumentServer().getClassFactory()
.getQueryParameterInstance(session, new String[] {SDP_ARCHIVECLASS.getDatabaseName(session)}, null, null);
qp.setExpression(qe);
qp.setHitLimitThreshold(0);
qp.setHitLimit(0);
int nrOfHitsTotal = session.getDocumentServer().queryCount(session, qp, "*");
int initialCapacity = (int) (nrOfHitsTotal / 0.75 + 1);
// MD sets; and objects already done (here: document ID)
HashSet<String> objDone = new HashSet<>(initialCapacity);
HashMap<String, IMasterDataSet> objRes = new HashMap<>(initialCapacity);
qp.close();
// do queries until hit count is smaller than 10.000
// use modification date
boolean keepGoing = true;
while(keepGoing) {
// construct query expression
// - basic part: Modification date & class type
// a. doc. class type
qe = SDP_ARCHIVECLASS.getQueryExpression(session);
// b. ID
qe = SearchUtil.appendQueryExpressionWithANDoperator(session, qe,
new PlainExpression(modDesc.getQueryLiteral() + " BETWEEN " + startDateL + " AND " + endDateL));
// 2. Query Parameter: set database; set expression
qp = session.getDocumentServer().getClassFactory()
.getQueryParameterInstance(session, new String[] {SDP_ARCHIVECLASS.getDatabaseName(session)}, null, null);
qp.setExpression(qe);
// order by modification date; hitlimit = 0 -> no hitlimit, but the usual 10.000 max
qp.setOrderByExpression(session.getDocumentServer().getClassFactory().getOrderByExpressionInstance(modDesc, true));
qp.setHitLimitThreshold(0);
qp.setHitLimit(0);
// Do not sort by modification date;
qp.setHints("+NoDefaultOrderBy");
keepGoing = false;
IInformationObject[] hits = null;
IDocumentHitList hitList = null;
hitList = session.getDocumentServer().query(qp, session);
IDocument doc;
if (hitList.getTotalHitCount() > 0) {
hits = hitList.getInformationObjects();
for (IInformationObject hit : hits) {
String objID = hit.getID();
if(!objDone.contains(objID)) {
// do something with this object and the class
// here: construct a new SDP sub class object and give it back via interface
doc = (IDocument) hit;
IMasterDataSet mdSet;
try {
mdSet = (IMasterDataSet) constructorForSDPbaseClass.newInstance(session, doc);
} catch (Exception e) {
// cause for this
String cause = (e.getCause() != null) ? e.getCause().toString() : MasterDataException.ERRMSG_PART_UNKNOWN;
throw new MasterDataException(MasterDataException.ERRMSG_NOINSTANCE_POSSIBLE, this.getClass().getSimpleName(), e.toString(), cause);
}
objRes.put(mdSet.getID(), mdSet);
objDone.add(objID);
}
}
doc = (IDocument) hits[hits.length - 1];
Date lastModDate = ((IDateValue) doc.getDescriptor(modDesc).getValues()[0]).getValue();
startDateL = Long.parseLong(itaTimestampFormat.format(lastModDate));
keepGoing = (hits.length >= 10000 || hitList.isResultSetTruncated());
}
qp.close();
}
return objRes;
}
Loading 120,000 rows (and more) each time will not scale very well, and your solution may not work in the future as the record size grows. Instead let the database server handle the problem.
Your table needs to have a primary key or unique key based on the columns of the records. Iterate through the 10,000 records performing JDBC SQL update to modify all field values with where clause to exactly match primary/unique key.
update BLAH set COL1 = ?, COL2 = ? where PKCOL = ?; // ... AND PKCOL2 =? ...
This modifies an existing row or does nothing at all - and JDBC executeUpate() will return 0 or 1 indicating number of rows changed. If number of rows changed was zero you have detected a new record which does not exist, so perform insert for that new record only.
insert into BLAH (COL1, COL2, ... PKCOL) values (?,?, ..., ?);
You can decide whether to run 10,000 updates followed by however many inserts are needed, or do update+optional insert, and remember JDBC batch statements / auto-commit off may help speed things up.

My cursor gets only last entry from sql database, but I need all of them

every one. I'm trying to get data from database, but it shows me only the last entry. The same code works for other entries, but for long it doesn't. I have searched a lot and always answer is teh same I have, so I don't know what to do. My code:
hepublic void loadRevenueDate() {
String dateFormat = "yyyy/MM/dd";
SimpleDateFormat formater = new SimpleDateFormat(dateFormat);
long l=0;
Calendar cal = Calendar.getInstance();
// Open database
db.open();
Cursor c = db.getRevData();
if (c.moveToFirst()) {
do {
l = c.getLong(3);
} while (c.moveToNext());
}
cal.setTimeInMillis(l);
String date = formater.format(cal.getTime());
revDate.setText(date);
// Close the cursor
c.close();
c = null;
// And close the database
db.close();
}
Please help me. Thanks in advance.
The cursor correctly reads over all the records - but you overwrite and ignore previous values when re-assigning to the l variable.
Do the thing "per row" inside the loop or otherwise save the relevant data into a collection such as an ArrayList. I imagine your UI needs to be redesigned, but this won't "skip" any values:
List<String> dates = new ArrayList<String>();
if (c.moveToFirst()) {
do {
// Stuff that happens for EACH record
long l = c.getLong(3);
cal.setTimeInMillis(l);
String date = formater.format(cal.getTime());
// .. and actually save the data for use later
dates.add(date)
} while (c.moveToNext());
}
// To make the text pretty (e.g. without surrounding braces),
// search for "java list join" or otherwise consume the List in the UI
// in a meaningful fashion.
revDate.setText("" + dates);
From the code it looks like you're just iterating over the values until you get to the last one. If you want to do something with all the values, you need to add something into the loop:
public void loadRevenueDate() {
String dateFormat = "yyyy/MM/dd";
SimpleDateFormat formater = new SimpleDateFormat(dateFormat);
long l=0;
Calendar cal = Calendar.getInstance();
// Open database
db.open();
Cursor c = db.getRevData();
long accum = 0;
if (c.moveToFirst()) {
do {
l = c.getLong(3);
// Do something here. For example:
accum += l;
} while (c.moveToNext());
}
cal.setTimeInMillis(l);
String date = formater.format(cal.getTime());
revDate.setText(date);
// Close the cursor
c.close();
c = null;
// And close the database
db.close();
}
The issue here is that you loop through all the result rows, assigning the long value from each row to 'l' over and over (overwriting the previous value), before you do anything with 'l'. So by the time you come to use 'l' (once the looping has finished), it will only contain the value from the final row.
If you wish to do something with each value you assign to 'l', you need to do it within the do...while loop. I.e.
if (c.moveToFirst()) {
do {
l = c.getLong(3);
//Do the actual thing with this row's 'l' here
//E.g.
System.out.println("l is currently set to: " + l);
} while (c.moveToNext());
}
Hope that makes sense.

How to randomly set text to buttons from SQLite without repetition?

I have a db imported to assets, and i read from it and randomly set text to buttons and a texview, with code below:
mDbHelper.open();
Cursor c = mDbHelper.getTestData();
List<Answer> labels = new ArrayList<Answer>();
labels.add(new Answer(c.getString(2), true));
labels.add(new Answer(c.getString(3), false));
labels.add(new Answer(c.getString(4), false));
labels.add(new Answer(c.getString(5), false));
Collections.shuffle(labels);
question.setText(c.getString(1));
bOdgovor1.setText(labels.get(0).option);
bOdgovor1.setTag(labels.get(0));
bOdgovor1.setOnClickListener(clickListener);
bOdgovor2.setText(labels.get(1).option);
bOdgovor2.setTag(labels.get(1));
bOdgovor2.setOnClickListener(clickListener);
bOdgovor3.setText(labels.get(2).option);
bOdgovor3.setTag(labels.get(2));
bOdgovor3.setOnClickListener(clickListener);
bOdgovor4.setText(labels.get(3).option);
bOdgovor4.setTag(labels.get(3));
bOdgovor4.setOnClickListener(clickListener);
Here's my TestAdapter code for db:
public Cursor getTestData()
{;
try
{
String sql ="SELECT * FROM tblPitanja ORDER BY RANDOM() LIMIT 1";
Cursor mCur = mDb.rawQuery(sql, null);
if (mCur!=null)
{
mCur.moveToNext();
}
return mCur;
}
catch (SQLException mSQLException)
{
Log.e(TAG, "getTestData >>"+ mSQLException.toString());
throw mSQLException;
}
}
It works perfectly when it comes to setting questions to buttons, but questions repeat. How to avoid that?
there are more approaches to solve your problem:
execute the sql-statement (without limiting) at the beginning and move to the next entry of the cursor when a quesion is answered correctly
buffer the questions which where already answered
the second approach could be done as follows:
first, change your method and sql, including a where-clause:
public Cursor getTestData(String whereClause)
{;
try
{
String sql ="SELECT * FROM tblPitanja WHERE 1 = 1 " + whereClause + " ORDER BY RANDOM() LIMIT 1";
[...]
second, buffer the already answered questions in your game-class:
add a LinkedList to your game-class
LinkedList<Long> mAnsweredQuestions = new LinkedList<Long>();
add already answered questions to the LinkedList:
Cursor c = mDbHelper.getTestData(generateWhereClause());
mAnsweredQuestions.add(c.getLong(0));
List<Answer> labels = new ArrayList<Answer>();
[...]
add a function which generates the where-clause:
private String generateWhereClause(){
StringBuilder result = new StringBuilder();
for (Long l : mAnsweredQuestions){
result.append(" AND " + YOURID + " <> " + l);
}
return result.toString();
}
You could just save the Cursor objects into an ArrayList and then use contains to find out if that question was alredy asked. (Note: contains useses the equals method)
public class YourClass {
java.util.ArrayList<Long> cursorList = new java.util.ArrayList<Long>();
public void YourMethod {
Cursor c = mDbHelper.getTestData();
long l = c.getLong(0);
while(cursorList.contains(l))
{
Cursor c = mDbHelper.getTestData();
l = c.getLong(0);
}
cursorList.add(l);
}
}
The solution depends on your application logic. One of possible solutions is to remove "LIMIT 1" from your query and load all questions randomly sorted.
If your app does something like "show me next random question I have never seen before", you have to keep the list of already visited questions (in memory, or in DB - again depends on your application logic. For DB solution is as simple as add visited column to your database.
For this problem, Better you maintain the list of question(object or id) your asked. Before display any question to the user check the current question is in your array list or not. If current qus is in arraylist then you can call getTestData(), otherwise you can display the question and add id or qus object to the array list.

how to get 150k followersIDs from User?

I am trying to get all the followersIDs from an a twitter account with about 150.000 followers. I later want to map their location, but first I need all those IDs.
at the moment I am using this code:
long lCursorIDs = -1;
long[] fArray = new long[100];
do
{
fArray = twitter.getFollowersIDs(name, lCursorIDs).getIDs();
} while (twitter.getFollowersIDs(name, lCursorIDs).hasNext ());
try
{
PrintWriter pr = new PrintWriter(filenameOutput);
for (int i=0; i<fArray.length ; i++)
{
pr.println(fArray[i]);
}
pr.close();
System.out.println("Follower IDs collected and saved to file: " + filenameOutput );
}
catch (Exception e)
{
e.printStackTrace();
System.out.println("No such file exists.");
}
This works for User with less followers. but with that many it always returns an error message - rate limit exceeded.
I was thinking about getting only a certain number of followersIDs per hour, but I am not sure how to do that and not start every hour from the beginning with the first follower. also, I am not sure how many followers I can get with one request. maybe it is 100, as with the "lookupUser" method but I am not sure.. any ideas/suggestions?
EDIT: ok, I just tried to get the followerIDs of an account with 2700 followers and it stored them correctly in the text file. It also only "cost" one request. than I changed the account name to an account with 15500 followers and it crashes again with an rate limit exceeded message. I don´t get why since it´s only roughly 6 times as many followers but all the remaining requests get spend.. any ideas on what I´m doing wrong?
the answer:
int numberOfFollowers;
numberOfFollowers = user.getFollowersCount();
//CREATE ARRAYS FOR FOLLOWER IDS
long cursor = -1;
long[] fArray = new long[numberOfFollowers];
long[] local = new long[5000];
IDs ids = twitter.getFollowersIDs(name, cursor);
int j = 0;
int x = 5000;
int durchgang = 1;
int d_anzahl = 1 + numberOfFollowers / 5000;
//STROE FOLLOWER IDS IN ARRAYS
do
{
ids = twitter.getFollowersIDs(name, cursor);
local = twitter.getFollowersIDs(name, cursor).getIDs();
System.out.println("Durchgang: " + durchgang + " / " + d_anzahl );
System.arraycopy(local, 0, fArray, j * x , local.length);
j++;
durchgang++;
cursor = ids.getNextCursor();
} while (ids.hasNext());
this gets an array with all follower IDs of any twitter User. It calculates the number of loops needed to get all follower IDs and copys each array of 5000 IDs into new array which has all IDs at the end.

Can anyone help with a java problem regarding record stores?

I have this code. And basically this returns the correct data without the town qualities. When I add the town qualities the method returns nothing, not even the orginal data that it has been and I dont know why. Can anyone see a problem?
protected void listRecords() {
mListForm.deleteAll(); // clear the form
try {
RecordStore rs = RecordStore.openRecordStore("Details", true);
RecordEnumeration re = rs.enumerateRecords(null, new RecordSorter(), false);
while (re.hasNextElement()) {
byte [] recordBuffer = re.nextRecord();
String record = new String(recordBuffer);
// extract the name and the age from the record
int endOfName = record.indexOf(";");
int endOfDesc = record.indexOf(";" , endOfName + 1);
int endOfTown = record.indexOf (";", endOfDesc + 1);
String name = record.substring(0, endOfName);
String desc = record.substring(endOfName + 1, endOfDesc);
String town = record.substring(endOfDesc +1, endOfTown);
mListForm.append(name + " aged: "+ desc + " " + town);
}
rs.closeRecordStore();
}
catch(Exception e){
mAlertConfirmDetailsSaved.setString("Couldn't read details");
System.err.println("Error accessing database");
}
mDisplay.setCurrent(mListForm);
}
Have you tried running it in the debugger? Is the exception happening? Are the three semicolons present in the record? Is there a limit on mDisplay's string size? When setCurrent is called, is the mListForm correct?
In other words, what have you done so far and where is it definitely right, and where does it become wrong?

Categories

Resources