Android: How to create my own cursor class? - android

I'm using Sqlite in Android and to get a value from the database I use something like this:
Cursor cursor = sqliteDatabase.rawQuery("select title,category from table", null);
int columnIndexTitle = cursor.getColumnIndex("title");
iny columnIndexCategory = cursor.getColumnIndex("category");
cursor.moveToFirst();
while (cursor.moveToNext()) {
String title = cursor.getString(columnIndexTitle);
String category = cursor.getString(columnIndexCategory);
}
cursor.close();
I want to create my own Cursor so that I can do getColumnIndex() and getString() with one method. Something like this:
String title = cursor.getString("title");
I want to create my own class that extends the cursor that I get from sqliteDatabase.rawQuery, but I'm not sure how to accomplish this. Should I extend SQLiteCursor or how should I do this? Is it even a possible and is it a good idea?

I came across this question looking for the best way to create a custom Cursor to use together with a SQLiteDatabase. In my case I needed an extra attribute to the Cursor to carry an additional piece of information, so my use case is not exactly as in the body of the question. Posting my findings in hope it will be helpful.
The tricky part for me was that the SQLiteDatabase query methods returns a Cursor, and I needed to pass on a custom subclass to Cursor.
I found the solution in the Android API: Use the CursorWrapper class. It seems to be designed exactly for this.
The class:
public class MyCustomCursor extends CursorWrapper {
public MyCustomCursor(Cursor cursor) {
super(cursor);
}
private int myAddedAttribute;
public int getMyAddedAttribute() {
return myAddedAttribute;
}
public void setMyAddedAttribute(int myAddedAttribute) {
this.myAddedAttribute = myAddedAttribute;
}
}
Usage:
public MyCustomCursor getCursor(...) {
SQLiteDatabase DB = ...;
Cursor rawCursor = DB.query(...);
MyCustomCursor myCursor = new MyCustomCursor(rawCursor);
myCursor.setMyAddedAttribute(...);
return myCursor;
}

Creating your own getString will cause a map lookup for each call instead of only for getColumnIndex.
Here's the code for SQLiteCursor.getColumnIndex and AbstractCursor.getColumnIndex. If you have many rows, reducing calls to this function will prevent unnecessary string processing and map lookups.

I wouldn't extend it, I'd make a helper:
class MartinCursor {
private Cursor cursor;
MartinCursor(Cursor cursor) {
this.cursor = cursor;
}
String getString(String column) {
....
}
}
or
class MartinCursorHelper {
static String getString(Cursor cursor, String column) {
....
}
}
Personally, I'd do the latter, unless you hate providing this extra argument all the time.
EDIT: I forgot to mention pydave's important point: If you call this in a loop, you're setting yourself up for a noticeable performance impact. The preferred way is to lookup the index once, cache it, and use that instead.

You should make use of the DatabaseUtils.stringForQuery() static method that is already in Android SDK to easily retrieve a value, this example is for String bot there is also method for Long
stringForQuery(SQLiteDatabase db, String query, String[] selectionArgs)
Utility method to run the query on the db and return the value in the first column of the first row.
Something like
String myString=DatabaseUtils.stringForQuery(getDB(),query,selectionArgs);

Came across this looking for a different solution, but just want to add this since I believe the answers are unsatisfactory.
You can easily create your own cursor class. In order to allow functions requiring Cursor to accept it, it must extend AbstractCursor. To overcome the issue of system not using your class, you simply make your class a wrapper.
There is a really good example here.
https://android.googlesource.com/platform/packages/apps/Contacts/+/8df53636fe956713cc3c13d9051aeb1982074286/src/com/android/contacts/calllog/ExtendedCursor.java
public class ExtendedCursor extends AbstractCursor {
/** The cursor to wrap. */
private final Cursor mCursor;
/** The name of the additional column. */
private final String mColumnName;
/** The value to be assigned to the additional column. */
private final Object mValue;
/**
* Creates a new cursor which extends the given cursor by adding a column with a constant value.
*
* #param cursor the cursor to extend
* #param columnName the name of the additional column
* #param value the value to be assigned to the additional column
*/
public ExtendedCursor(Cursor cursor, String columnName, Object value) {
mCursor = cursor;
mColumnName = columnName;
mValue = value;
}
#Override
public int getCount() {
return mCursor.getCount();
}
#Override
public String[] getColumnNames() {
String[] columnNames = mCursor.getColumnNames();
int length = columnNames.length;
String[] extendedColumnNames = new String[length + 1];
System.arraycopy(columnNames, 0, extendedColumnNames, 0, length);
extendedColumnNames[length] = mColumnName;
return extendedColumnNames;
}
That's the general idea of how it will work.
Now to the meat of the problem. To prevent the performance hit, create a hash to hold the column indices. This will serve as a cache. When getString is called, check the hash for the column index. If it does not exist, then fetch it with getColumnIndex and cache it.
I'm sorry I can't add any code currently, but I'm on mobile so I'll try to add some later.

Related

Android - Cursor not properly working

I have an app that inserts data into a database with 2 tables (project and alvara)
The insertion method for the second table depends on what type the first table gets. (1 or 2) for resumed idea.
This is a method that I made for looking into the second table with cursor. If it finds, it sets in setters from alvara_db class. And later on, I use getters to show info on textviews in another activity. The issue is that it's not setting info at all. Is anything wrong in my Cursor?
Thanks in advance!
public ArrayList<alvara_db> getAlvaras(){
SQLiteDatabase db = this.getReadableDatabase();
ArrayList<alvara_db> projects = new ArrayList<>();
String[] project = new String[]{String.valueOf(tipoprojetoid)};
Cursor cur = db.rawQuery("SELECT placa, proj_exec, resp_tec, rtnum, parecer FROM alvara WHERE projetoid = ?", project);
cur.moveToFirst();
alvara_db alvaras = new alvara_db();
alvaras.setPlaca(cur.getInt(cur.getColumnIndex("placa")));
alvaras.setProj_exec(cur.getInt(cur.getColumnIndex("proj_exec")));
alvaras.setResp_tec(cur.getString(cur.getColumnIndex("resp_tec")));
alvaras.setRtnum(cur.getInt(cur.getColumnIndex("rtnum")));
alvaras.setParecer(cur.getString(cur.getColumnIndex("parecer")));
projects.add(alvaras);
return projects;
}
Fragment where I call getAlvaras method:
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.detalhes_projeto_alvara);
db.getAlvaras();
placa = alvaras.getPlaca();
resp_tec = alvaras.getResp_tec();
proj_exec = alvaras.getProj_exec();
rtnum = alvaras.getRtnum();
}
You are not using the returned value from db.getAlvaras().
Instead the alvaras variable in your second snippet is something that is likely not initialized - the code you posted does not show that exactly.
(In addition, you might want to check the return value of moveToFirst() in case the query matches no rows, and add a do-while loop to retrieve more than one row.)

How to load database data, and store it into array list, and show it into ListVIew

So, I want to build an app to view some data from database. I already have the database, also already made some entities that have exactly same properties name with the column names in database. And also I put the database into database directory by copying from assets folder.
What I want to achieve is, I want to pull some data, and put it into array list, so I can show it in ListView in fragment.
Is there any convenient way to pull some data without querying (like loadAll() function) ?
For now, I'm using cursor to save the pulled data using query, and assign its properties one by one using set function like setName(String name).
After that, I show the list using CursorAdapter.
It would be like this
public class FrameCursor extends CursorWrapper{
/**
* Creates a cursor wrapper.
*
* #param cursor The underlying cursor to wrap.
*/
public FrameCursor(Cursor cursor) {
super(cursor);
}
public ZFrame getFrame(){
if(isBeforeFirst() || isAfterLast()){
return null;
}
ZFrame frame = new ZFrame();
ZFrameDao frameDao = new ZFrameDao();
int frameEdition = getInt(getColumnIndex(COLUMN_FRAME_EDITION));
int frameId = getInt(getColumnIndex(COLUMN_FRAME_ID));
int frameNumber = getInt(getColumnIndex(COLUMN_FRAME_NUMBER));
int frameType = getInt(getColumnIndex(COLUMN_FRAME_TYPE));
int frameBookmark = getInt(getColumnIndex(COLUMN_FRAME_BOOKMARK));
int frameGlyph = getInt(getColumnIndex(COLUMN_FRAME_GLYPH));
int frameLesson = getInt(getColumnIndex(COLUMN_FRAME_LESSON));
String frameAllReading = getString(getColumnIndex(COLUMN_FRAME_ALL_READING));
String frameReadingNumber = getString(getColumnIndex(COLUMN_FRAME_READING_NUMBER));
String frameReference = getString(getColumnIndex(COLUMN_FRAME_REFERENCE));
String frameWritingNumber = getString(getColumnIndex(COLUMN_FRAME_WRITING_NUMBER));
frame.setZEDITION(frameEdition);
frame.setZFRAME_ID(frameId);
frame.setZFRAME_NUMBER(frameNumber);
frame.setZFRAME_TYPE(frameType);
frame.setZBOOKMARK(frameBookmark);
frame.setZGLYPH((long)frameGlyph);
frame.setZLESSON((long)frameLesson);
frame.setZALL_READING_NUMBER(frameAllReading);
frame.setZREADING_NUMBER(frameReadingNumber);
frame.setZREFERENCE(frameReference);
frame.setZWRITING_NUMBER(frameWritingNumber);
return frame;
}
}
It would be consume lot of work for doing this for every table.
So anyone could help me?
Why not using CursorLoader ? Use CursorLoader to handle the cursor query issue, and it works perfectly with CursorAdapter, Here is the google's guide
Maybe this is what you need, a light weight orm api? You can go here for more information.

Convert results of SQLite Cursor to my object

I've executed some queries on my SQLite DB on Android.
Main instruction used is this:
Cursor cursor = myDB.rawQuery(select, null);
Now I want the results of this query to be converted to a generic class I've created in the project. If you think I want an ORM system, you are right, but all I've found now are ORM systems that want to query the DB, save objects in the DB and manage the DB itself.
Instead now I need a 'simple' ORM feature that can do exactly what the google.gson library does with JSON strings, it converts JSON strings to custom objects, and I want the same but for converting SQLite cursors to my Classes.
Any suggestion how to do this?
I don't think there is an automated way to do this. Just populate the object yourself. For example, if your cursor was just an id and a name and you wanted to create a Person object:
Person populatePerson(Cursor cursor)
{
try
{
int idIndex = cursor.getColumnIndexOrThrow("_id");
int nameIndex = cursor.getColumnIndexOrThrow("name");
long id = cursor.getLong(idIndex);
String name = cursor.getString(nameIndex);
return new Person(id, name);
}
catch (Exception e)
{
e.printStackTrace();
}
}
You could wrap this function in your own Cursor class to make the process transparent.
Check out MicroOrm
private static class SomeObject {
#Column(SOME_FIELD)
private String mSomeField;
}
SomeObject object = microOrm.fromCursor(cursor, SomeObject.class);

What to do with Cursor after a SQLite query?

This is my first time using a database and I'm not really sure how this works. I made the database and made a query that returns a cursor and... now what? What is a cursor, really? Can I just use that to navigate through my data or do I have to put it in an ArrayList or ListActivity or what?
You need to iterate the cursor to get your results.
Use cursor.moveToFirst() and/or cursor.moveToNext() (with a while loop). Then you can use the getX() method, like cursor.getInt() or cursor.getString().
For example, ir your are expecting one result from your query:
if (cursor.moveToFirst()) {
String name = cursor.getString(cursor.getColumnIndex('NAME'));
int age = cursor.getInt(cursor.getColumnIndex('AGE'));
} else {
// oops nothing found!
}
First call cursor.moveToFirst(). Each time you call cursor.moveToNext() it will move to the next row. Make sure when you are done with your cursor you call cursor.deactivate() or you will get errors in your log cat.
Iterate over the returned Cursor instance
public List<Object[]> cursorToTableRows(Cursor cursor) {
List<Object[]> result = new ArrayList<Object[]>(cursor.getCount());
cursor.move(0);
cursor.moveToNext();
while (cursor.isAfterLast() == false) {
Object[] tableRow = new Object[cursor.getColumnCount()];
for(int i=0; i<cursor.getColumnNames().length; i++) {
int columnIndex = cursor.getColumnIndex(cursor.getColumnName(i));
String columnValue = cursor.getString(columnIndex);
tableRow[i] = columnValue;
}
result.add(tableRow);
cursor.moveToNext();
}
cursor.close();
return result;
}
Then create the desired objects.
public List<Vehicle> getVehicles() {
List<Vehicle> vehicles = new ArrayList<Vehicle>();
Cursor cursor = null;
List<Object[]> objects = cursorToTableRows(cursor);
for(Object[] row : objects) {
int i=0;
Vehicle vehicle = new Vehicle(row[i++].toString(), row[i++].toString()));
vehicles.add(vehicle)
}
return vehicles;
}
from Developer.android: This interface provides random read-write access to the result set returned by a database query.
In other words: query returns you a set of data represented by a cursor. First you need to make sure you got a valid cursor (not null) and then try to move it to desired position in the data set (use moveToXXX methods). In order to obtain data pointed by cursor use getXXX methods. When done using it make sure to call close to release resources.
According to this link it looks like you can iterate through the query return using something like:
cursor.next();
And grab the data at the location you are looking for using:
cursor.getString(0)
After you successfully have your Cursor setup, you would typically want to display that to a view in some form.
Have a look at the following answer for a detailed, but simple example of using a Cursor Adapter to pair up your newly-minted Cursor with your desired XML View:
https://stackoverflow.com/a/20532937/293280

Is there a way to access the calendar's entries without using gdata-java-client?

Is it possible to get the calendar's entries from the phone offline? It seem the only way is to use gdata-java-client.
Josef and Isaac's solutions for accessing the calendar only work in Android 2.1 and earlier. Google have changed the base content URI in 2.2 from "content://calendar" to "content://com.android.calendar". This change means the best approach is to attempt to obtain a cursor using the old base URI, and if the returned cursor is null, then try the new base URI.
Please note that I got this approach from the open source test code that Shane Conder and Lauren Darcey provide with their Working With The Android Calendar article.
private final static String BASE_CALENDAR_URI_PRE_2_2 = "content://calendar";
private final static String BASE_CALENDAR_URI_2_2 = "content://com.android.calendar";
/*
* Determines if we need to use a pre 2.2 calendar Uri, or a 2.2 calendar Uri, and returns the base Uri
*/
private String getCalendarUriBase() {
Uri calendars = Uri.parse(BASE_CALENDAR_URI_PRE_2_2 + "/calendars");
try {
Cursor managedCursor = managedQuery(calendars, null, null, null, null);
if (managedCursor != null) {
return BASE_CALENDAR_URI_PRE_2_2;
}
else {
calendars = Uri.parse(BASE_CALENDAR_URI_2_2 + "/calendars");
managedCursor = managedQuery(calendars, null, null, null, null);
if (managedCursor != null) {
return BASE_CALENDAR_URI_2_2;
}
}
} catch (Exception e) { /* eat any exceptions */ }
return null; // No working calendar URI found
}
These answers are good, but they all involve hard-coding the Calendar URI (which I've seen in three different incarnations across different Android devices).
A better way to get that URI (which hard-codes the name of a class and a field instead) would be something like this:
Class<?> calendarProviderClass = Class.forName("android.provider.Calendar");
Field uriField = calendarProviderClass.getField("CONTENT_URI");
Uri calendarUri = (Uri) uriField.get(null);
This isn't perfect (it will break if they ever remove the android.provider.Calendar class or the CONTENT_URI field) but it works on more platforms than any single URI hard-code.
Note that these reflection methods will throw exceptions which will need to be caught or re-thrown by the calling method.
Currently, this is not possible without using private APIs (see Josef's post.) There is a Calendar provider, but it is not public yet. It could change anytime and break your app.
Though, it probably will not change (I don't think they will change it from "calendar"), so you might be able to use it. But my recommendation is to use a separate class like this:
public class CalendarProvider {
public static final Uri CONTENT_URI = Uri.parse("content://calendar");
public static final String TITLE = "title";
public static final String ....
And use those instead of the strings directly. This will let you change it very easily if/when the API changes or it is made public.
You can use the calendar content provider (com.android.providers.calendar.CalendarProvider). Example:
ContentResolver contentResolver = context.getContentResolver();
Cursor cursor = contentResolver.query(Uri.parse("content://calendar/events"), null, null, null, null);
while(cursor.moveToNext()) {
String eventTitle = cursor.getString(cursor.getColumnIndex("title"));
Date eventStart = new Date(cursor.getLong(cursor.getColumnIndex("dtstart")));
// etc.
}
edit: you might want to put this in a wrapper (see Isaac's post) as it's currently a private API.
You can use the CalendarContract from here: https://github.com/dschuermann/android-calendar-compatibility
It is the same API class as available on Android 4, but made to work with Android >= 2.2.
About the API that can change... The whole ContentProvider approach won't change that quickly so can already overcome a lot of problems by only updating the strings. Therefor create constants you reuse over the whole project.
public static final String URI_CONTENT_CALENDAR_EVENTS = "content://calendar/events";
ContentResolver contentResolver = context.getContentResolver();
Cursor cursor = contentResolver.query(Uri.parse(URI_CONTENT_CALENDAR_EVENTS), null, null, null, null);
//etc
If you want a proper private API you'll have to create a pojo and some services like this:
public class CalendarEvent {
private long id;
private long date;
//etc...
}
public interface CalendarService {
public Set<CalendarEvent> getAllCalendarEvents();
public CalendarEvent findCalendarEventById(long id);
public CalendarEvent findCalendarEventByDate(long date);
}
and so on. This way you'll only have to update the CalendarEvent object and this service in case the API changes.
Nick's solution involves managedQuery, which is not defined in the Context class. Many times when you are running things in the background you would want to use a context object. Here's a modified version:
public String getCalendarUriBase() {
return (android.os.Build.VERSION.SDK_INT>=8)?
"content://com.android.calendar":
"content://calendar";
}
The catch for null should not be carried out here since there might be more exceptions even if the managedQuery succeeded earlier.

Categories

Resources