1

I do have several apps that are designed the same way. An extended Application holds the database connection and an extended SQLiteOpenHelper has all the code.

This works when used in a standard android project.

It crashes when the extended Application is in a Library Project.

The following code is the extended Application class. It is located in the Library Project. It crashes if it is used in a Library Project but not if it is part of a standard Android app without a Library Project. I tried to move the MyApplication class from the Library Project into the Project (changing the name in the Manifest) - without success.

My guess, but I'm simply fishing here, is that MySQLiteOpenHelper uses the rights/permissions/location of the library project and not of the project itself.

public class MyApplication extends Application {

    private static SQLiteDatabase     sqliteDatabase;
    private static MySQLiteOpenHelper sqliteOpenHelper;

    public static SQLiteDatabase getSqliteDatabase() {
        return sqliteDatabase;
    }

    public static MySQLiteOpenHelper getSqliteOpenHelper() {
        return sqliteOpenHelper;
    }

    @Override
    public void onCreate() {
        super.onCreate();

        MyPreferenceActivity.createSettings(getApplicationContext());

        sqliteOpenHelper = new MySQLiteOpenHelper(getApplicationContext());
        if (sqliteOpenHelper != null) {
            sqliteDatabase = sqliteOpenHelper.getWritableDatabase();
        }
    }
}

This is part of the extended SQLiteOpenHelper class:

public class MySQLiteOpenHelper extends SQLiteOpenHelper {

    private static final int      DATABASEVERSION = 18;
    private static final String   DATABASENAME = "myproduct.db";

    private Context               context;
    private SQLiteDatabase        sqliteDatabase;
    private String                location; 

    public MySQLiteOpenHelper(final Context context) {
        super(context, DATABASENAME, null, DATABASEVERSION);

        this.context = context;
    }

    @Override
    public SQLiteDatabase getWritableDatabase() {
            ...
    try {
        if (Tools.isSDCardWriteable() && (fileSdDbDir.exists() || fileSdDb.exists())) {
            // Database on SD-card works perfect
            // If I use this to create the internal database it fails here too
            location = sdDb;
            sqliteDatabase = SQLiteDatabase.openOrCreateDatabase(location, null);

            int version = sqliteDatabase.getVersion();

            if (version != DATABASEVERSION) {
                sqliteDatabase.beginTransaction();

                try {
                    if (version == 0) {
                        onCreate(sqliteDatabase);
                    } else {
                        onUpgrade(sqliteDatabase, version, DATABASEVERSION);
                    }

                    sqliteDatabase.setVersion(DATABASEVERSION);
                    sqliteDatabase.setTransactionSuccessful();
                } finally {
                    sqliteDatabase.endTransaction();
                }
            }

            onOpen(sqliteDatabase);
        } else {
            // internal database fails to create
            location = intDb;
            sqliteDatabase = super.getWritableDatabase(); <-- fails
        }
    } catch (Exception exception) {
    if (MyPreferenceActivity.DEBUG) Log.d("Exception", exception.getMessage()); <-- crashes
    }

    return sqliteDatabase;
    }

    @Override
    public void onCreate(final SQLiteDatabase sqliteDatabase) {
        //
    }

    @Override
    public void onUpgrade(final SQLiteDatabase sqliteDatabase, final int oldVersion, final int newVersion) {
        //
    }

    ...
}

This is the Manifest of the Library Project:

<?xml version="1.0" encoding="utf-8"?>

<manifest
    xmlns:android="http://schemas.android.com/apk/res/android"
    package="xxx.yyy.zzzlib.android" >

    <uses-sdk
        android:minSdkVersion="7"
        android:targetSdkVersion="11" />
</manifest>

This is part of the projects Manifest:

<?xml version="1.0" encoding="utf-8"?>

    <manifest
        xmlns:android="http://schemas.android.com/apk/res/android"
        android:installLocation="preferExternal" 
        android:versionCode="41"
        android:versionName="5.0.3"
        package="xxx.yyy.zzz.android" >

        <application
            ...
            android:name="xxx.yyy.zzzlib.android.MyApplication" >
            ...
        </application>

        ...

        <uses-sdk
            android:minSdkVersion="7"
            android:targetSdkVersion="11" />
    </manifest> 

I don't have any idea why this doesn't work. Perhaps a Context thing but the error points to the file. I don't have any clues.

Is there something wrong with this approach? It's working perfect in a standard Android project environment.

Many thanks in advance.

EDIT:

Stacktrace shows crash in Log.d but it's the database call that's failing:

11-04 17:53:56.143: E/AndroidRuntime(1127): FATAL EXCEPTION: main
11-04 17:53:56.143: E/AndroidRuntime(1127): java.lang.RuntimeException: Unable to create application xxx.yyy.zzzlib.android.MyApplication: java.lang.NullPointerException: println needs a message
11-04 17:53:56.143: E/AndroidRuntime(1127):     at android.app.ActivityThread.handleBindApplication(ActivityThread.java:3275)
11-04 17:53:56.143: E/AndroidRuntime(1127):     at android.app.ActivityThread.access$2200(ActivityThread.java:117)
11-04 17:53:56.143: E/AndroidRuntime(1127):     at android.app.ActivityThread$H.handleMessage(ActivityThread.java:969)
11-04 17:53:56.143: E/AndroidRuntime(1127):     at android.os.Handler.dispatchMessage(Handler.java:99)
11-04 17:53:56.143: E/AndroidRuntime(1127):     at android.os.Looper.loop(Looper.java:130)
11-04 17:53:56.143: E/AndroidRuntime(1127):     at android.app.ActivityThread.main(ActivityThread.java:3683)
11-04 17:53:56.143: E/AndroidRuntime(1127):     at java.lang.reflect.Method.invokeNative(Native Method)
11-04 17:53:56.143: E/AndroidRuntime(1127):     at java.lang.reflect.Method.invoke(Method.java:507)
11-04 17:53:56.143: E/AndroidRuntime(1127):     at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:839)
11-04 17:53:56.143: E/AndroidRuntime(1127):     at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:597)
11-04 17:53:56.143: E/AndroidRuntime(1127):     at dalvik.system.NativeStart.main(Native Method)
11-04 17:53:56.143: E/AndroidRuntime(1127): Caused by: java.lang.NullPointerException: println needs a message
11-04 17:53:56.143: E/AndroidRuntime(1127):     at android.util.Log.println_native(Native Method)
11-04 17:53:56.143: E/AndroidRuntime(1127):     at android.util.Log.d(Log.java:137)
11-04 17:53:56.143: E/AndroidRuntime(1127):     at xxx.yyy.zzzlib.android.MySQLiteOpenHelper.getWritableDatabase(MySQLiteOpenHelper.java:1493)
11-04 17:53:56.143: E/AndroidRuntime(1127):     at xxx.yyy.zzzlib.android.MyApplication.onCreate(MyApplication.java:27)
11-04 17:53:56.143: E/AndroidRuntime(1127):     at android.app.Instrumentation.callApplicationOnCreate(Instrumentation.java:969)
11-04 17:53:56.143: E/AndroidRuntime(1127):     at android.app.ActivityThread.handleBindApplication(ActivityThread.java:3272)
Harald Wilhelm
  • 6,656
  • 11
  • 67
  • 85
  • What's on this line: `11-04 17:53:56.143: E/AndroidRuntime(1127): at xxx.yyy.zzzlib.android.MySQLiteOpenHelper.getWritableDatabase(MySQLiteOpenHelper.java:1493)` that calls Log? – Pedantic Nov 04 '11 at 18:31
  • It's the line calling "super.getWritableDatabase()" that's failing. The crash comes from the Log.d not having any data in the exception message but it's the database call that fails. I did edit my post. It shows most of the extended getWritableDatabase() now and the stack trace. Thanks a lot. – Harald Wilhelm Nov 04 '11 at 18:43
  • So you call getWritableDatabase(), it fails and throws an exception that you catch, and then you see the above stacktrace in the logs? – Pedantic Nov 04 '11 at 18:47
  • Exactly. As I said, it's the database call that's failing. And that's my problem I try to describe. This exact code design (complete MyApplication, MySQLiteOpenHelper.getWriteableDatabase(), ...) is part of over 10 apps. It's only failing in one app where this code (MyApplication, MySQLiteOpenhelper) is part of the Library and in the standalone Android project. – Harald Wilhelm Nov 04 '11 at 19:17
  • Ok, whats the exception you're catching and eating? Put a breakpoint in your catch block and debug, or just remove catch block entirely and let the crash and post the (now) unhandled exception. – Pedantic Nov 04 '11 at 19:47
  • The exception messagetext says "Could not open database". I will use your tipps and look if something more meaningful comes in. – Harald Wilhelm Nov 04 '11 at 19:58

1 Answers1

0

1) Post more of the stack trace. Very little context. 2) 'if (sqliteOpenHelper != null) {' is not needed. It will never be null. 3) Overriding getWritableDatabase doesn't make any sense, at least in this context. I would not do this. 4) Your helper class's onCreate is empty. I assume you did that before publicly posting?

If your helper class attempts to open the database from the path given in the LIBRARY's package, I could see how that would be trouble. Not sure how file permissions work with library projects.

Kevin Galligan
  • 16,159
  • 5
  • 42
  • 62
  • Thanks for your answer. Yes, code is removed from onCreate()/onUpgrade(). I use getWriteableDatabase() because above the try{} block is an if() statement where I select between a database on SD-card and the internal database. Shown is the part for the internal database. This one crashes. If I use the external storage the database becomes created. I will edit my question and add stack trace and the if in a minute. – Harald Wilhelm Nov 04 '11 at 18:00
  • Please post this line: MySQLiteOpenHelper.java:1493 and surrounding lines. The actual exception has nothing to do with the database. It *may* be initially caused by the database, but your actual exception is due to null in a log statement. – Kevin Galligan Nov 04 '11 at 18:21
  • Yes, it's Log.d that's crashing but it is the super.getWritableDatabase that's failing. I did edit my question to show most of the things. The interesting part is a.) When creating the database on SD-card it works, b.) if calling super.getWritableDatabase() to create a database on internal storage in the else part it fails, c.) if using the part for the external database to create the internal database it fails here too. So it must have to do with the internal storage. This happens only if the code is in a Library Project... – Harald Wilhelm Nov 04 '11 at 18:46
  • You need that actual stack trace. My guess would be file permissions issue. Also, make sure whatever "Context" you're passing in comes from the running app and not somehow from the library code. that should get the right package/path (maybe). – Kevin Galligan Nov 04 '11 at 19:14
  • Context comes from onCreate() within Application and is retrieved with getApplicationContext(). This context is passed to the constructor of SQLiteOpenHelper and used everywhere there. Application and SQLiteOpenHelper are in a Library Project. Application is fully qualified in the Manifest of the App using that Library (e.g. android:name="xxx.yyy.zzzlib.android.MyApplication"). This might be part of the problem. – Harald Wilhelm Nov 04 '11 at 20:18