I am working on an app in Android Studio with the following code:
//added this first part to show what c is and what happens with it. I don't use a curser that I know of and it doesn't stop my app from running fine, but I don't like having errors.
#Override
public Iterator<StoredMessage> getAllArrivedMessages(
final String clientHandle) {
return new Iterator<StoredMessage>() {
private Cursor c;
private boolean hasNext;
private final String[] selectionArgs = {
clientHandle,
};
{
db = mqttDb.getWritableDatabase();
// anonymous initialiser to start a suitable query
// and position at the first row, if one exists
if (clientHandle == null) {
c = db.query(ARRIVED_MESSAGE_TABLE_NAME,
null,
null,
null,
null,
null,
"mtimestamp ASC");
} else {
c = db.query(ARRIVED_MESSAGE_TABLE_NAME,
null,
MqttServiceConstants.CLIENT_HANDLE + "=?",
selectionArgs,
null,
null,
"mtimestamp ASC");
}
hasNext = c.moveToFirst();
}
#Override
public boolean hasNext() {
if (!hasNext){
c.close();
}
return hasNext;
}
#Override
public StoredMessage next() {
String messageId = c.getString(c.getColumnIndex(MqttServiceConstants.MESSAGE_ID));
String clientHandle = c.getString(c
.getColumnIndex(MqttServiceConstants.CLIENT_HANDLE));
String topic = c.getString(c
.getColumnIndex(MqttServiceConstants.DESTINATION_NAME));
byte[] payload = c.getBlob(c
.getColumnIndex(MqttServiceConstants.PAYLOAD));
int qos = c.getInt(c.getColumnIndex(MqttServiceConstants.QOS));
boolean retained = Boolean.parseBoolean(c.getString(c
.getColumnIndex(MqttServiceConstants.RETAINED)));
boolean dup = Boolean.parseBoolean(c.getString(c
.getColumnIndex(MqttServiceConstants.DUPLICATE)));
// build the result
MqttMessageHack message = new MqttMessageHack(payload);
message.setQos(qos);
message.setRetained(retained);
message.setDuplicate(dup);
// move on
hasNext = c.moveToNext();
return new DbStoredData(messageId, clientHandle, topic, message);
}
I am getting the following error message:
Value must be ≥ 0 but 'getColumnIndex' can be -1
for each of the 'getColumnIndex above.
This is an error code since it is a red circle with an exclamation point in the middle displayed in the "Problems/Current File" window of my Android Studio project.
How do I rewrite them to get rid of the error messages?
Related
I am currently taking each column based on query and modifying variables based on the current position of the cursor. I was wondering if it would be possible to cut down the size of the code by doing something like this where a different function call would be made based on the column within the cursor that is currently being referenced:
do {
Ticket ticket = new Ticket();
for(int i = 0; i < cursor.getColumnCount(); i++)
{
if (cursor.getString(0) != null) {
/*Where the array contains a list of function calls*/
ticket.arrayList(i);
}
}while(cursor.moveToNext());
Below is the code I currently have. From what I know there isn't anything in Java that works like this, but I'm trying to cut down on the number of lines here as I will eventually have close to one hundred columns that will be pulled into the cursor.
public List<Ticket> getTickets(Context context, SQLiteDatabase db)
{
List<Ticket> ticketInfo = new ArrayList<>();
String selectQuery = "SELECT * FROM " + TABLE_TICKET;
Cursor cursor = null;
try {
cursor = db.rawQuery(selectQuery, null);
if (cursor != null) {
try {
if (cursor.moveToFirst()) {
do {
Ticket ticket = new Ticket();
//Set the ticket number
if (cursor.getString(0) != null) {
ticket.setTicketNr(Integer.parseInt(cursor.getString(0)));
}
//Set the ticket id
if (cursor.getString(1) != null) {
ticket.setTicketId(Integer.parseInt(cursor.getString(1)));
}
//
if (cursor.getString(2) != null) {
ticket.setServiceName(cursor.getString(2));
}
//
if (cursor.getString(3) != null) {
ticket.setServiceHouseNr(Integer.parseInt(cursor.getString(3)));
}
//
if (cursor.getString(4) != null) {
ticket.setServiceDirectional(cursor.getString(4));
}
//
if (cursor.getString(5) != null) {
ticket.setServiceStreetName(cursor.getString(5));
}
//
if (cursor.getString(6) != null) {
ticket.setServiceCommunityName(cursor.getString(6));
}
//
if (cursor.getString(7) != null) {
ticket.setServiceState(cursor.getString(7));
}
//
if (cursor.getString(8) != null) {
ticket.setServiceZip1(Integer.parseInt(cursor.getString(8)));
}
//
if (cursor.getString(9) != null) {
ticket.setServiceZip2(Integer.parseInt(cursor.getString(9)));
}
//
if (cursor.getString(10) != null) {
ticket.setTroubleReported(cursor.getString(10));
}
// Adding exercise to list
if (ticket != null) {
ticketInfo.add(ticket);
}
} while (cursor.moveToNext());
} else {
//No results from query
Toast.makeText(context.getApplicationContext(), "No tickets found", Toast.LENGTH_LONG).show();
}
} finally {
if (cursor != null && !cursor.isClosed()) {
cursor.close();
}
}
}
}
catch(SQLiteException exception)//If exception is found
{
Log.d(TAG, "Error", exception);
//Display exception
Toast.makeText(context.getApplicationContext(), exception.toString(), Toast.LENGTH_LONG).show();
}
return ticketInfo;
}
Thank you for any insights into this.
I think this would do it. Just advance the cursor and pass it into the Ticket constructor. You may want to add some error checking.
public class Ticket {
private static class Field {
int intValue;
String stringValue;
final Class type;
Field(Class fieldType){
type = fieldType;
}
void set(String value){
if(type.equals(String.class)){
stringValue = value;
}
else {
intValue = Integer.parseInt(value);
}
}
}
private List<Field> fields = new ArrayList<>();
private Field addField(Field field){
fields.add(field);
return field;
}
// This solution relies on adding fields in the order they'll be retrieved in the cursor.
// Other options are possible such as a map by column index.
private Field ticketNumber = addField(new Field(Integer.class));
private Field serviceName = addField(new Field(String.class));
public Ticket(Cursor cursor){
for(int i=0; i < fields.size(); i++){
Field f = fields.get(i);
f.set(cursor.getString(i));
}
}
}
public int getTicketNumber(){
return ticketNumber.intValue;
}
// Don't know if you need setters
public void setTicketNumber(int value){
ticketNumber.intValue = value;
}
// etc for remaining fields
I would also consider using an ORM to make this stuff easier, rather than dealing with cursors.
I haven't dealt with SQLite databases before last week. I last dealt with SQL many years ago, but I still have the gist of it.
The code below reads 140,000 words from an asset named dictionary.dic and inserts each into a SQLite database along with its status. My expectation was that it would take a good while, but it's been like 25 minutes on a 7" tablet and still not near finished (on P).
Should I say, "Hey, it's 1/7 of a million rows. It's gonna take awhile." But I can read all 140,000 words into an ArrayList<String> in 30 seconds. I realize there's overhead in creating the database, but many, many minutes?
Should I say, "Well, think how long it would take if not using AsyncTask" and accept it since it's a one-time task? But it's really obnoxious, taking so long. It's off-putting.
Should I say, "Why are you using a Scanner? No wonder it's taking so long?" and do some other asset access? Or is that not the real problem?
I also have never used AsyncTask. Am I misusing doInBackground? I've got a lot of code in there; not all MUST go there, but the loop is what it is and there's the hangup.
Is using database.Insert, which is called a "convenience method", what's causing the hangup? Should I be using a Cursor and query instead? I'm not entirely sure how I'd do that. Got my idea from Deitel's "Address Book" app in "Android for Programmers--App Driven...", but his database is empty at the outset.
I've given this plenty of thought. I just need someone with experience to look and say, "Well, HERE'S your problem." I can't justify starting redoing all the things I've thought of without some guidance about whether any of it is going to help.
public class DatabaseConnector //extends ArrayList<String>
{
public static Cursor cursor ;
Scanner scDict;
InputStream stream = null;
Context mContext;
AssetManager mAssets;
public static final String DATABASE_NAME = "Dictionary";
public static final String TABLE_NAME = "wordlist";
public static final String WORD_COLUMN_NAME = "word";
public static final String STATUS_COLUMN_NAME = "status";
public static final String [] columns = new String[]{WORD_COLUMN_NAME, STATUS_COLUMN_NAME};
private DatabaseOpenHelper ___databaseOpenHelper; // creates the database
private SQLiteDatabase ___database; // for interacting with the database
public DatabaseConnector(Context _context, AssetManager assets)
{
mContext = _context;
mAssets = assets;
___databaseOpenHelper = new DatabaseOpenHelper(_context, DATABASE_NAME, null, 1);
Log.w("DB connected", ___databaseOpenHelper.getDatabaseName());
createDbIfNecessary();
};
public void open() throws SQLException // opens/creates
{
___database = ___databaseOpenHelper.getWritableDatabase(); // create OR open
}
public void createDbIfNecessary(){
this.open();
if(getDbCount() < 140000){
try { stream = mAssets.open("dictionary.dic"); }
catch (IOException e) { System.out.println(Arrays.toString(e.getStackTrace())); }
MainActivity.setLblProgress("This one-time task takes awhile: loading letter... ");
LoadWords loadWords = new LoadWords();
loadWords.execute((Object[]) null);
this.close();
}
}
public void close(){
if(___database != null)
___database.close();
}
public int getDbCount(){
this.open();
return ___database.query(TABLE_NAME, columns, null, null, null, null, null).getCount();
}
public long insertWord(String _word)
{
ContentValues
__newWord;
__newWord = new ContentValues();
__newWord.put(WORD_COLUMN_NAME, _word);
__newWord.put(STATUS_COLUMN_NAME, true);
long __row = ___database.insert(TABLE_NAME, null, __newWord);
return __row; // -1 if can't insert
}
//////////////////////////////////////////////////////////////////////////////////////////////////
private class DatabaseOpenHelper extends SQLiteOpenHelper
{
public DatabaseOpenHelper(Context _context, String _name, CursorFactory _factory, int _version)
{ super(_context, _name, _factory, _version); }
#Override public void onCreate(SQLiteDatabase _db)
{
_db.execSQL( "CREATE TABLE " + TABLE_NAME +
"("
+ WORD_COLUMN_NAME + " TEXT primary key , " //not null, "
+ STATUS_COLUMN_NAME + " BOOLEAN" +
");"
); // execute query to create the ___database
}
} // end class DatabaseOpenHelper
//////////////////////////////////////////////////////////////////////////////////////////////////
private class LoadWords extends AsyncTask<Object, Integer, Void>
{
#Override
protected Void doInBackground(Object... params) {
long k = 0;
scDict = new Scanner(stream).useDelimiter("\r\n");
long count = getDbCount();
Log.w("Start load at " , "" + count);
String s = "";
while(k++ < count){
s = scDict.next();
}
Log.w("Add after " , s);
while (scDict.hasNext())
{
s = scDict.next();
publishProgress((Integer)(int)s.charAt(0));
insertWord(s) ;
return null;
}
protected void onProgressUpdate(Integer... progress) {
int c = (int)progress[0];
MainActivity.setLastLetterProcessed((char) c);
}
#Override
protected void onPostExecute(Void x)
{
MainActivity.popupMessage("Database has been created", mContext);
}
}
} // end class DatabaseConnector
You are attempting to do 140,000 individual database transactions. That might take weeks.
Instead, either wrap your entire thing in a single transaction, or batch the inserts into transactions (e.g., every 1000 words). You can create your own transaction bounds using this pseudo-Java:
db.beginTransaction();
try {
// do your SQL work here
db.setTransactionSuccesful();
}
catch (Exception e) {
// logging, event bus message to UI, whatever
}
finally {
db.endTransaction();
}
Thanks to #Commonsware, the 140,000 records now load in under a minute, as opposed to under an HOUR. All I did was use his "p-code" to surround my insert with a 1000-count loop:
protected Void doInBackground(Object... params) {
...
scDict = new Scanner(stream).useDelimiter("\r\n");
long count = getDbCount();
while (k++ < count)
s = scDict.next();
while (scDict.hasNext())
{
//////////////////////////////////////////////////////// start insert
int ki = 0;
try
{
___database.beginTransaction();
while (ki < MAX_TRANSACTIONS && scDict.hasNext())
{
//////////////////////////////////////////////////////// end
insertWord(scDict.next());
//////////////////////////////////////////////////////// start insert
++ki;
}
___database.setTransactionSuccessful();
}
catch(Exception e){ Log.w("Exception",e);}
finally
{
___database.endTransaction();
publishProgress((Integer) (int) s.charAt(0));
}
//////////////////////////////////////////////////////// end
}
return null;
}
...
}
I have a sortid row in my table to have a custom order of my data.
in my SQLiteOpenHelper class, i have a drop() function which calls .update(), and in loadCities() the rows are queried but i get the old sortId.
here are parts of my code:
public class TimesHelper extends SQLiteOpenHelper {
private static final String TABLE_CREATE = "CREATE TABLE " + TABLE_NAME
+ " (" + KEY__ID + " INTEGER PRIMARY KEY AUTOINCREMENT,"
+ [...] + KEY_SORTID + " INTEGER);";
private SQLiteDatabase mDB;
private Handler mHandler;
public SQLiteDatabase getDB() {
if (mDB == null) {
mDB = getWritableDatabase();
} else {
mHandler.removeCallbacks(mClose);
}
//useful, but i am not sure wether this is good practise, please comment...
mHandler.postDelayed(mClose, 500);
return mDB;
}
private Runnable mClose = new Runnable() {
#Override
public void run() {
if (mDB != null) {
mDB.close();
mDB = null;
}
}
};
public void drop(int from, int to) {
List<Integer> keys = new ArrayList<Integer>(Times.Cities.keySet());
Integer key = keys.get(from);
keys.remove(key);
keys.add(to, key);
getDB().beginTransaction();
for (Integer i : keys) {
ContentValues val = new ContentValues();
val.put(KEY_SORTID, keys.indexOf(i));
getDB().update(TABLE_NAME, val,
KEY__ID + "=" + i, null);//returning 1
}
getDB().endTransaction();
loadCities();
}
public void loadCities() {
HashMap<Integer, Times> cities = Times.Cities;
cities.clear();
Cursor c = getDB().query(TABLE_NAME, null, null, null, null, null,
KEY_SORTID);
c.moveToFirst();
if (c.isAfterLast()) {
c.close();
return;
}
do {
int s = c.getInt(c.getColumnIndex(KEY_SORTID));
int id = c.getInt(c.getColumnIndex(KEY__ID));
//here i still have the old values...
//do whatever
}
} while (c.moveToNext());
c.close();
}
}
i tried anything, but without success...
Metin Kale
You forgot setTransactionSuccessful(). Calling endTransaction() without it rolls back any changes done within the transaction.
The preferred, exception-safe pattern to handle transactions is
beginTransaction();
try {
// db operations...
setTransactionSuccessful(); // didn't throw so far
} finally {
endTransaction(); // rollback or commit
}
I want to use the Settings.Global class which is introduced in Android 4.2.
I am using the following code:
Log.d("Global.Setting", getGlobalSettingValues(Global.AIRPLANE_MODE_RADIOS))
private String getGlobalSettingValues(String key){
return Settings.Global.getString(mcontex.getContentResolver(), key);
}
expected Output:
AIRPLANE_MODE_RADIOS=cell,bluetooth,wifi,nfc,wimax.
But it shows following error:
java.lang.NoClassDefFoundError
/**
* #return A comma separated list of radios that need to be disabled when airplane mode is on
*/
private String getGlobalSettingValues() {
ContentResolver cr = getContentResolver();
Cursor globalCursor = null;
String aeroplaneMode = null;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
globalCursor = cr.query(Settings.Global.CONTENT_URI, null, null, null, null);
if(globalCursor != null && globalCursor.moveToFirst()){
aeroplaneMode = Settings.Global.getString(getApplicationContext().getContentResolver(),Settings.Global.AIRPLANE_MODE_RADIOS);
}
}
if (globalCursor != null) {
globalCursor.close();
}
return aeroplaneMode;
}
See if This Link gives you a pointer.
Code extract from the above link for quick reference:
private static boolean isAirplaneModeOn(Context context) {
if (apiVersion < 17) {
return Settings.System.getInt(context.getContentResolver(), Settings.System.AIRPLANE_MODE_ON, 0) == 1;
} else {
return Settings.Global.getInt(context.getContentResolver(), Settings.Global.AIRPLANE_MODE_ON, 0) == 1;
}
}
Follow the link for a detailed explanation on how to do it.
I'm trying to make a custom search bar for my app and it works fine but I need to include a if/else statement to let my user know that the user he searched for does not exist.I tried implementing a if/else statement but when I try to search a user that's in my database it shows me my else statement of "Username not found" when I know I'm putting in the right Username.
From the code I provided I would like to know what am I doing wrong?
search.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
String Username = sbar.getText().toString();
String foundplayer = db.getUsername();
if (Username.equals(foundplayer)) {
ResultFrame.setVisibility(View.VISIBLE);
ResultText.setText(foundplayer);
Toast.makeText(getApplicationContext(), "Player Found", Toast.LENGTH_LONG).show();
} else {
Toast.makeText(getApplicationContext(), "Username Not Found", Toast.LENGTH_LONG).show();
}
}
});
public String getUsername() {
String[] userName = new String[] { "USERNAME" };
Cursor c = db.query( "NFDB", null, null, null, null, null, null, null);
String result = "";
int iName = c.getColumnIndex("USERNAME");
for (c.moveToFirst(); !c.isAfterLast(); c.moveToNext()) {
result = c.getString(iName);
}
return result;
}
First make sure that your getUsername() method returning value..
If it returns value then do like this.
String foundplayer = db.getUsername();
this will take sometime to process as it involves getting the data from Database so your compiler will not wait for it to complete because of that compiler will go to if condition before completing getUsername() method. so you will get your foundplayer null
so it will never satisfy the condition.
so call that in a separate thread and let the other statement wait until it completes
String foundplayer = "";
Thread t=new Thread()
{
public void run()
{
foundplayer = db.getUsername();
}
};
t.start();
t.join();
or
equals is case sensitive try equalsIgnoreCase
Your getUsername function actually returns some random user name from the database.
You have to give it the user name to be searched for as a parameter:
public String getUsername(String searchName) {
Cursor c = db.query("NFDB",
new String[] { "USERNAME" },
"USERNAME = ?",
new String[] { searchName },
null, null, null);
if (c.moveToNext())
return c.getString(0);
else
return "";
}
For that matter, it's quite pointless to return the user name from that function, because the caller already knows what name was searched for. You should better return a boolean.