7

Getting this error when I use SQLiteDatabase as a Closeable

I've got a sample project to recreate it:

https://github.com/blundell/SQLDatabaseError

With a class that extends SQLiteOpenHelper:

public class DatabaseHelper extends SQLiteOpenHelper {

....

public void openAndCloseDatabase() {
    SQLiteDatabase database = getWritableDatabase();

    close(database);
}

private void close(Closeable database) {
    try {
        if (database != null) {
            database.close();
        }
    } catch (Exception e) {
        Log.e("Error", "Oh no!", e);
    }
}

}

Stack trace:

12-14 12:23:43.719: E/AndroidRuntime(5179): FATAL EXCEPTION: main
12-14 12:23:43.719: E/AndroidRuntime(5179): java.lang.IncompatibleClassChangeError: interface not implemented
12-14 12:23:43.719: E/AndroidRuntime(5179):   at com.blundell.sqldatabasecursorerror.DatabaseHelper.close(DatabaseHelper.java:35)
12-14 12:23:43.719: E/AndroidRuntime(5179):     at com.blundell.sqldatabasecursorerror.DatabaseHelper.openAndCloseDatabase(DatabaseHelper.java:29)
12-14 12:23:43.719: E/AndroidRuntime(5179):     at com.blundell.sqldatabasecursorerror.MainActivity.onCreate(MainActivity.java:13)
12-14 12:23:43.719: E/AndroidRuntime(5179):     at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1047)
12-14 12:23:43.719: E/AndroidRuntime(5179):     at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:1623)
12-14 12:23:43.719: E/AndroidRuntime(5179):     at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:1675)
12-14 12:23:43.719: E/AndroidRuntime(5179):     at android.app.ActivityThread.access$1500(ActivityThread.java:121)
12-14 12:23:43.719: E/AndroidRuntime(5179):     at android.app.ActivityThread$H.handleMessage(ActivityThread.java:943)
12-14 12:23:43.719: E/AndroidRuntime(5179):     at android.os.Handler.dispatchMessage(Handler.java:99)
12-14 12:23:43.719: E/AndroidRuntime(5179):     at android.os.Looper.loop(Looper.java:130)
12-14 12:23:43.719: E/AndroidRuntime(5179):     at android.app.ActivityThread.main(ActivityThread.java:3701)
12-14 12:23:43.719: E/AndroidRuntime(5179):     at java.lang.reflect.Method.invokeNative(Native Method)
12-14 12:23:43.719: E/AndroidRuntime(5179):     at java.lang.reflect.Method.invoke(Method.java:507)
12-14 12:23:43.719: E/AndroidRuntime(5179):     at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:866)
12-14 12:23:43.719: E/AndroidRuntime(5179):     at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:624)
12-14 12:23:43.719: E/AndroidRuntime(5179):     at dalvik.system.NativeStart.main(Native Method)

API:

http://developer.android.com/reference/android/database/sqlite/SQLiteDatabase.html

http://developer.android.com/reference/android/database/sqlite/SQLiteClosable.html

http://developer.android.com/reference/java/io/Closeable.html

This should work shouldn't it?

 public final class SQLiteDatabase extends SQLiteClosable
 >>
 public abstract class SQLiteClosable extends Object implements Closeable
 >>
 public interface Closeable

Doesn't work:

  • Xperia Play Android 2.3.4
  • Motorola Xoom Android 4.0.4

Does work:

  • Samsung Galaxy Nexus Android 4.2
Blundell
  • 75,855
  • 30
  • 208
  • 233
  • `public class DatabaseHelper extends SQLiteOpenHelper implements Closeable` is possible too. – waqaslam Dec 14 '12 at 12:44
  • 1
    (not an Android developer) IncompatibleClassChangeError sounds like you're compiling and running against different versions of the library. – ignis Dec 14 '12 at 12:45
  • See the accepted answer here: http://stackoverflow.com/questions/1980452/what-causes-java-lang-incompatibleclasschangeerror – Anders R. Bystrup Dec 14 '12 at 12:45
  • 1
    @ignis and Anders: yeah, but those interfaces have been in there since API Level 1, and I would think that Android guarantees binary upwards compatibility. Weird... – Thilo Dec 14 '12 at 12:49

1 Answers1

7

Okay, found the problem in the Change Notes for 4.1 (API Level 16):

android.database.sqlite.SQLiteClosable implements java.io.Closeable only from API Level 16. Before that, they were unrelated (even though both interfaces existed since the beginning).

So you should use SQLiteClosable directly if you want your code to be backwards compatible.

It is also probably worthwhile to install the SDK for the lowest version you want to support and try to build with it, then this would have been detected by the compiler.

Thilo
  • 257,207
  • 101
  • 511
  • 656
  • Would be nice if this was noted in the Javadoc for SQLiteClosable somewhere, too. – Thilo Dec 14 '12 at 13:14
  • Thanks, good spot! Shame it's from api16 more safety check code to write. Yeah Lint normally catches obvious API level reqs, but as you say they are both available from API 1. Might create a Lint warning and push it up. – Blundell Dec 14 '12 at 13:39
  • Just been bitten by this again! This time with `java.util.Scanner` http://developer.android.com/sdk/api_diff/19/changes/java.util.Scanner.html – Blundell Dec 17 '13 at 12:43
  • 2
    How come Lint API checker didn't catch this? – AlikElzin-kilaka Feb 24 '14 at 11:00