Search code examples
androidsqliteandroid-asynctasksqliteopenhelper

Updating Database in Background task (Android)


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.


Solution

  • 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();
    
    
    }