Question

I am trying to get some methods concerning databases working when referenced from two different activities which causes me to re-initialize the whole database with different contexts in the same class. It works from one activity but when the database is closed, initialized and opened in the other activity it causes an IllegalArgumentException. This makes no sense to me as I didn't change any methods connected directly to the database and it was working flawlessly when the code was separated in two classes.

Stacktrace:

05-09 20:44:42.661: E/AndroidRuntime(953): FATAL EXCEPTION: main
05-09 20:44:42.661: E/AndroidRuntime(953): java.lang.RuntimeException: Unable to resume activity {maturaarbeit.nicola_pfister.marks/maturaarbeit.nicola_pfister.marks.Marks}: java.lang.IllegalArgumentException: the bind value at index 1 is null
05-09 20:44:42.661: E/AndroidRuntime(953):  at android.app.ActivityThread.performResumeActivity(ActivityThread.java:2575)
05-09 20:44:42.661: E/AndroidRuntime(953):  at android.app.ActivityThread.handleResumeActivity(ActivityThread.java:2603)
05-09 20:44:42.661: E/AndroidRuntime(953):  at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2089)
05-09 20:44:42.661: E/AndroidRuntime(953):  at android.app.ActivityThread.access$600(ActivityThread.java:130)
05-09 20:44:42.661: E/AndroidRuntime(953):  at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1195)
05-09 20:44:42.661: E/AndroidRuntime(953):  at android.os.Handler.dispatchMessage(Handler.java:99)
05-09 20:44:42.661: E/AndroidRuntime(953):  at android.os.Looper.loop(Looper.java:137)
05-09 20:44:42.661: E/AndroidRuntime(953):  at android.app.ActivityThread.main(ActivityThread.java:4745)
05-09 20:44:42.661: E/AndroidRuntime(953):  at java.lang.reflect.Method.invokeNative(Native Method)
05-09 20:44:42.661: E/AndroidRuntime(953):  at java.lang.reflect.Method.invoke(Method.java:511)
05-09 20:44:42.661: E/AndroidRuntime(953):  at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:786)
05-09 20:44:42.661: E/AndroidRuntime(953):  at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:553)
05-09 20:44:42.661: E/AndroidRuntime(953):  at dalvik.system.NativeStart.main(Native Method)
05-09 20:44:42.661: E/AndroidRuntime(953): Caused by: java.lang.IllegalArgumentException: the bind value at index 1 is null
05-09 20:44:42.661: E/AndroidRuntime(953):  at android.database.sqlite.SQLiteProgram.bindString(SQLiteProgram.java:164)
05-09 20:44:42.661: E/AndroidRuntime(953):  at android.database.sqlite.SQLiteProgram.bindAllArgsAsStrings(SQLiteProgram.java:200)
05-09 20:44:42.661: E/AndroidRuntime(953):  at android.database.sqlite.SQLiteDirectCursorDriver.query(SQLiteDirectCursorDriver.java:47)
05-09 20:44:42.661: E/AndroidRuntime(953):  at android.database.sqlite.SQLiteDatabase.rawQueryWithFactory(SQLiteDatabase.java:1314)
05-09 20:44:42.661: E/AndroidRuntime(953):  at android.database.sqlite.SQLiteDatabase.queryWithFactory(SQLiteDatabase.java:1161)
05-09 20:44:42.661: E/AndroidRuntime(953):  at android.database.sqlite.SQLiteDatabase.query(SQLiteDatabase.java:1032)
05-09 20:44:42.661: E/AndroidRuntime(953):  at maturaarbeit.nicola_pfister.marks.database.DBAdapter.getAverage(DBAdapter.java:200)
05-09 20:44:42.661: E/AndroidRuntime(953):  at maturaarbeit.nicola_pfister.marks.MyMenu.getData(MyMenu.java:71)
05-09 20:44:42.661: E/AndroidRuntime(953):  at maturaarbeit.nicola_pfister.marks.Marks.onResume(Marks.java:52)
05-09 20:44:42.661: E/AndroidRuntime(953):  at android.app.Instrumentation.callActivityOnResume(Instrumentation.java:1184)
05-09 20:44:42.661: E/AndroidRuntime(953):  at android.app.Activity.performResume(Activity.java:5082)
05-09 20:44:42.661: E/AndroidRuntime(953):  at android.app.ActivityThread.performResumeActivity(ActivityThread.java:2565)
05-09 20:44:42.661: E/AndroidRuntime(953):  ... 12 more

Exception throwing method in Marks (menu.onResume):

private final Context context;

private DatabaseHelper DBHelper;
private SQLiteDatabase db;

MyMenu menu = new MyMenu(this);

    ListView lView;
    View view;

    private String TAG = "Marks";
    @Override
        protected void onResume() {
            super.onResume();
            menu.closedb();
            menu.opendb(TAG);
            menu.getData(lView, view);

        }

Involved methods in MyMenu (exception on db.open()):

DBAdapter db;

public void opendb(String caller) {
        db = new DBAdapter(context);
        this.caller = caller; //Is used to distinguish which activity called the class; not involved in this problem
        db.open();

    }

    public void closedb() {
        db.clean();
        db.close();
    }

@SuppressWarnings("deprecation")
public String getData(final ListView lView, final View view) {
    SimpleCursorAdapter adapter = null;
    if (caller == "Main") {
        adapter = new SimpleCursorAdapter(context, 
                android.R.layout.simple_list_item_activated_1, 
                db.getAllSubjects(), 
                new String[] { "subject" }, 
                new int[] { android.R.id.text1 });
    } else if (caller == "Marks") {
        adapter = new SimpleCursorAdapter(context, 
                android.R.layout.simple_list_item_activated_1, 
                db.getMarks(subject), 
                new String[] {"value"}, 
                new int[] { android.R.id.text1 });

        Cursor cursor = db.getAverage(selection);
        average = cursor.getDouble(cursor.getColumnIndexOrThrow(DBAdapter.KEY_VALUE));

        if (average != 0.0) {
            TextView text = new TextView(context);
            text =  (TextView)view.findViewById(R.id.marks_average);
            text.setText(context.getString(R.string.average) + " " + average);
            text.setBackgroundColor(context.getResources().getColor(R.color.lightgrey));
        } else {
            TextView text = new TextView(context);
            text =  (TextView)view.findViewById(R.id.marks_average);
            text.setText("");
            text.setBackgroundColor(context.getResources().getColor(android.R.color.transparent));
        }
    }

Involved methods in DBAdapter (works fine with one activity, throws exception on getAverage()):

public DBAdapter open() throws SQLException {
        db = DBHelper.getWritableDatabase();
        return this;
    }

    public void close() {
        DBHelper.close();
    }

    public boolean clean () {
        db.delete(DATABASE_TABLE_SUBJECTS, KEY_SUBJECT +"=?", new String[] {""});
        db.delete(DATABASE_TABLE_MARKS, KEY_VALUE + "=?", new String[] {""});
        return true;
    }

    public Cursor getAverage(String subject) throws SQLException {
    Cursor mCursor =
            db.query(true, DATABASE_TABLE_AVERAGE, new String[] {
                    KEY_ROWID,
                    KEY_SUBJECT,
                    KEY_VALUE
            },
            KEY_SUBJECT + "=?",
            new String[] {subject}, null, null, null, null);
    if (mCursor != null) {
        mCursor.moveToFirst();
    }
    return mCursor;
}

I initially thought that the error lies in the open() method but on reviewing the stacktrace I found that the exception is thrown by getAverage() which confuses me because it is not even called at this point. I assume there is something missing for the correct initialization of the DBAdapter class in the Marks activity but can't find the culprit. If you need any more information or code for solving the puzzle don't hesitate to let me know. Thanks for your very much appreciated help!

Was it helpful?

Solution

May I see the menu.getData(lView, view) method?

Anyway I suggest not to return Cursor objects from Database Helper class. Why? Because, there might be multiple calls, maybe parallel calls to database. Every time you need something from database you need to open it, and then close. If one part of your application will close database, and other part haven't done its work yet - and will try to access something from data from database or try to get data from Cursor object - it will certainly crash.

My solution is to make some Application Model <=> Database model object that will map data from bi-directional.

For example:

public LinkedList<Person> getPersonList(){
     Cursor result = db.query(...);

     LinkedList<Person> people = null;

     if(result.moveToFirst()){
         people = new LinkedList<Person>();
         Person person = null;

         do{
              person = new Person(); 

              person.setBirthDate(result.getInt(0));
              /*some more attributes mapping*/

              people.add(person); 
         }while(result.hasNext());
    }

    return people;
}

OTHER TIPS

The problem looks to me that you are creating different instances of SQLiteOpenHelper class for each context. My suggestion for a better design pattern, would be to use a thread-safe singleton instance for this class:

public class MyDbHelper extends SQLiteOpenHelper {

static volatile OnePageDbHelper sDefaultInstance;

private static final int DATABASE_VERSION = 1;
protected static final String DATABASE_NAME = "XXX.db";

/**
 * Synchronized singleton instance access to application's {@link SQLiteOpenHelper} object.
 */
public static MyDbHelper getDefault(Context pContext) {
    if (sDefaultInstance == null) {
        synchronized (MyDbHelper.class) {
            if (sDefaultInstance == null) {
                sDefaultInstance = new MyDbHelper(pContext.getApplicationContext());
            }
        }
    }
    return sDefaultInstance;
}

/**
 * Private constructor means object can only be instantiated within this class.
 *
 * @see {@link #getDefault(Context)}.
 */
private OnePageDbHelper(Context context) {
    super(context, DATABASE_NAME, null, DATABASE_VERSION);
}

@Override
public void onCreate(SQLiteDatabase db) {
    // TODO - Execute SQL statements to create all tables.

}

@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
    // TODO - Remove all tables if exist.

}

}

Which you would then access like:

MyDbHelper.getInstance().performMyDbOperations();
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top