4

My app makes use of SQLiteDatabase to save two arraylists to separate tables.

I have noticed that since implementing the database, whenever it updates the database (involving dropping the tables, recreating them, then populating them with the arraylists) the app briefly freezes and I get the following message in logcat: "I/Choreographer: Skipped 236 frames! The application may be doing too much work on its main thread."

To confirm it was the updating, I removed the code used to update the database. Upon doing that, I no longer got the warning message, and my app didn't freeze.

This is the code inside my custom DB helper, which extends SQLiteOpenHelper, that is used to update the table:

 public void insertData(ArrayList<SavedWifiHotspot> hotspots, ArrayList<MarkerOptions> markers) {
    Log.d("insert LocationsDB", "Data inserted");
    SQLiteDatabase db = this.getWritableDatabase();
    ContentValues hotspotValues = new ContentValues();
    ContentValues markerValues = new ContentValues();
    for(SavedWifiHotspot hotspot : hotspots) {
        hotspotValues.put("Ssid", hotspot.getSsid());
        hotspotValues.put("Password", hotspot.getPassword());
        hotspotValues.put("LocationName", hotspot.getHotspotLoc());
        hotspotValues.put("Lat", hotspot.getLatitude());
        hotspotValues.put("Lng", hotspot.getLongitude());
        db.insert(HOTSPOT_TABLE_NAME, null, hotspotValues);
    }
    for(MarkerOptions marker : markers) {
        markerValues.put("LocationName", marker.getTitle());
        markerValues.put("Lat", marker.getPosition().latitude);
        markerValues.put("Lng", marker.getPosition().longitude);
        db.insert(LOCATION_TABLE_NAME, null, markerValues);
    }
}

And this is the code used to clear the tables before they are updated:

public void clearData() {
    Log.d("clear LocationsDB", "Tables cleared");
    SQLiteDatabase db=this.getWritableDatabase();

    String dropHSTable = "DROP TABLE IF EXISTS "
            + HOTSPOT_TABLE_NAME + ";";

    String dropLocTable = "DROP TABLE IF EXISTS "
            + LOCATION_TABLE_NAME + ";";

    db.execSQL(dropHSTable);
    db.execSQL(dropLocTable);

    createTables(db);
}

How should I go about updating my database in the background? I've read about threads, should I use a thread to do this?

Edit: This is the error, in reference to my comment.

FATAL EXCEPTION: AsyncTask #5
Process: com1032.cw2.fm00232.fm00232_assignment2, PID: 8830
java.lang.RuntimeException: An error occured while executing doInBackground()
  at android.os.AsyncTask$3.done(AsyncTask.java:300)
  at java.util.concurrent.FutureTask.finishCompletion(FutureTask.java:355)
  at java.util.concurrent.FutureTask.setException(FutureTask.java:222)
  at java.util.concurrent.FutureTask.run(FutureTask.java:242)
  at android.os.AsyncTask$SerialExecutor$1.run(AsyncTask.java:231)
  at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1112)
  at java.util.concurrent.ThreadPoolExe
  at java.lang.Thread.run(Thread.java:818)cutor$Worker.run(ThreadPoolExecutor.java:587)
Caused by: java.util.ConcurrentModificationException
  at java.util.ArrayList$ArrayListIterator.next(ArrayList.java:573)
  at com1032.cw2.fm00232.fm00232_assignment2.LocationsDB$3.doInBackground(LocationsDB.java:124)
  at at com1032.cw2.fm00232.fm00232_assignment2.LocationsDB$3.doInBackground(LocationsDB.java:119)
  at android.os.AsyncTask$2.call(AsyncTask.java:288)
  at java.util.concurrent.FutureTask.run(FutureTask.java:237)
  at android.os.AsyncTask$SerialExecutor$1.run(AsyncTask.java:231)
  at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1112)
  at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:587)
  at java.lang.Thread.run(Thread.java:818)

For reference, line 124 is:

for(MarkerOptions marker: markers[0]) {

And line 119 is:

new AsyncTask<ArrayList<MarkerOptions>, Void, Void>() {

Edit2: Fixed the above problem. My app was invoking the insert data method using empty lists. So I've added .empty check before the insert data method.

Isengrim
  • 59
  • 1
  • 11

1 Answers1

3

An async task sounds like a good idea.

public void insertData(ArrayList<SavedWifiHotspot> hotspots, ArrayList<MarkerOptions> markers) {
Log.d("insert LocationsDB", "Data inserted");

new AsyncTask<ArrayList<SavedWifiHotspot>, Void, Void>() {

        @Override
        protected Void doInBackground(ArrayList<SavedWifiHotspot>... hotspots) {
            ContentValues hotspotValues = new ContentValues();
            for(SavedWifiHotspot hotspot : hotspots[0]) {
                hotspotValues.put("Ssid", hotspot.getSsid());
                hotspotValues.put("Password", hotspot.getPassword());
                hotspotValues.put("LocationName", hotspot.getHotspotLoc());
                hotspotValues.put("Lat", hotspot.getLatitude());
                hotspotValues.put("Lng", hotspot.getLongitude());
                db.insert(HOTSPOT_TABLE_NAME, null, hotspotValues);
            }

        }
    }.execute(hotspots);

new AsyncTask<ArrayList<MarkerOptions>, Void, Void>() {

        @Override
        protected Void doInBackground(ArrayList<MarkerOptions>... options) {
            ContentValues hotspotValues = new ContentValues();
            for(MarkerOptions marker: options[0]) {
                markerValues.put("LocationName", marker.getTitle());
                markerValues.put("Lat", marker.getPosition().latitude);
                markerValues.put("Lng", marker.getPosition().longitude);
                db.insert(LOCATION_TABLE_NAME, null, markerValues);
            }

        }
    }.execute(options);
}


public void clearData() {
    Log.d("clear LocationsDB", "Tables cleared");
    new AsyncTask<Void, Void, Void>() {
        @Override
        protected Void doInBackground(Void... params) {
            SQLiteDatabase db=this.getWritableDatabase();

           String dropHSTable = "DROP TABLE IF EXISTS "
               + HOTSPOT_TABLE_NAME + ";";

           String dropLocTable = "DROP TABLE IF EXISTS "
               + LOCATION_TABLE_NAME + ";";

           db.execSQL(dropHSTable);
           db.execSQL(dropLocTable);
           createTables(db);
        }
   }.execute();


}
Cory Roy
  • 5,099
  • 2
  • 27
  • 47
  • Is this something fairly easy to learn how to use? Basically this app I'm making is for coursework, so I'm a little limited for time. – Isengrim May 19 '16 at 16:36
  • It's pretty much done for you. If it crashes post the stacktrace and I can help you sort it out. – Cory Roy May 19 '16 at 16:51
  • Seems to work just fine. I am having a problem with closing my database. Usually I close the databases when the activity pauses, but this now seems to cause insertData to try and access a closed database, even if I check us db.isOpen in an if statement before invoking insertData. Also this warning is showing over the doInBackground methods "Unchecked generics array creation for varargs parameter". – Isengrim May 19 '16 at 18:45
  • Are you opening a reference to the db in the `onResume` method? – Cory Roy May 19 '16 at 19:46
  • Yeah, in my onResume I have `if(!db.isOpen())` to check if the db is open, and if not then it executes `db = locDB.getReadableDatabase();`. – Isengrim May 19 '16 at 19:50
  • Look at this http://stackoverflow.com/questions/10677781/how-to-open-close-sqlite-db-in-android-properly for how to deal with opening and closing the database. You should be using a helper class. – Cory Roy May 19 '16 at 19:55
  • Thanks for your help :) I'll check that out. – Isengrim May 19 '16 at 20:15