Сбой приложения при первой загрузке с использованием SQLiteAssetHelper - PullRequest
0 голосов
/ 29 июня 2018

Я получаю следующую ошибку во время первого запуска после установки приложения, а затем запускаю приложение без проблем.

> > 06-29 14:56:20.811 19467-19467/com.domain.sample E/SQLiteLog: (14) cannot open file at line 36667 of [0c55d17973]
>     (14) os_unix.c:36667: (2) open(/data/user/0/com.domain.sample/databases/quotes.sqlite3) -  06-29
> 14:56:20.812 19467-19467/com.domain.sample E/SQLiteDatabase: Failed to
> open database
> '/data/user/0/com.domain.sample/databases/quotes.sqlite3'.
>     android.database.sqlite.SQLiteCantOpenDatabaseException: unknown error (code 14 SQLITE_CANTOPEN): Could not open database
>         at android.database.sqlite.SQLiteConnection.nativeOpen(Native Method)
>         at android.database.sqlite.SQLiteConnection.open(SQLiteConnection.java:211)
>         at android.database.sqlite.SQLiteConnection.open(SQLiteConnection.java:195)
>         at android.database.sqlite.SQLiteConnectionPool.openConnectionLocked(SQLiteConnectionPool.java:503)
>         at android.database.sqlite.SQLiteConnectionPool.open(SQLiteConnectionPool.java:204)
>         at android.database.sqlite.SQLiteConnectionPool.open(SQLiteConnectionPool.java:196)
>         at android.database.sqlite.SQLiteDatabase.openInner(SQLiteDatabase.java:880)
>         at android.database.sqlite.SQLiteDatabase.open(SQLiteDatabase.java:865)
>         at android.database.sqlite.SQLiteDatabase.openDatabase(SQLiteDatabase.java:766)
>         at android.database.sqlite.SQLiteDatabase.openDatabase(SQLiteDatabase.java:714)
>         at com.domain.sample.database.ExternalDbOpenHelper.checkDataBase(ExternalDbOpenHelper.java:93)
>         at com.domain.sample.database.ExternalDbOpenHelper.createDataBase(ExternalDbOpenHelper.java:70)
>         at com.domain.sample.database.ExternalDbOpenHelper.openDataBase(ExternalDbOpenHelper.java:133)
>         at com.domain.sample.database.ExternalDbOpenHelper.<init>(ExternalDbOpenHelper.java:48)
>         at com.domain.sample.database.ExternalDbOpenHelper.getInstance(ExternalDbOpenHelper.java:32)
>         at com.domain.sample.loader.AbstractQueryLoader.<init>(AbstractQueryLoader.java:14)
>         at com.domain.sample.loader.QuoteGroupLoader.<init>(QuoteGroupLoader.java:17)
>         at com.domain.sample.QuoteGroupActivity.onCreateLoader(QuoteGroupActivity.java:154)
>         at android.support.v4.app.LoaderManagerImpl.createAndInstallLoader(LoaderManagerImpl.java:370)
>         at android.support.v4.app.LoaderManagerImpl.initLoader(LoaderManagerImpl.java:404)
>         at com.domain.sample.QuoteGroupActivity.onCreate(QuoteGroupActivity.java:107)
>         at android.app.Activity.performCreate(Activity.java:7131)
>         at android.app.Activity.performCreate(Activity.java:7122)
>         at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1271)
>         at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2882)
>         at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:3037)
>         at android.app.servertransaction.LaunchActivityItem.execute(LaunchActivityItem.java:78)
>         at android.app.servertransaction.TransactionExecutor.executeCallbacks(TransactionExecutor.java:108)
>         at android.app.servertransaction.TransactionExecutor.execute(TransactionExecutor.java:68)
>         at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1797)
>         at android.os.Handler.dispatchMessage(Handler.java:106)
>         at android.os.Looper.loop(Looper.java:193)
>         at android.app.ActivityThread.main(ActivityThread.java:6642)
>         at java.lang.reflect.Method.invoke(Native Method)
>         at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:493)
>         at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:858) 06-29
> 14:56:20.812 19467-19467/com.domain.sample
> E/class com.domain.sample.database.ExternalDbOpenHelper: Error while
> checking db 06-29 14:56:20.812 19467-19467/com.domain.sample
> W/SQLiteAssetHelper: copying database from assets... 06-29
> 14:56:20.813 19467-19467/com.domain.sample W/SQLiteAssetHelper:
> extracting file: 'quotes.sqlite3'... 06-29 14:56:20.847
> 19467-19467/com.domain.sample W/SQLiteAssetHelper: database copy
> complete 06-29 14:56:20.869 19467-19467/com.domain.sample
> I/SQLiteAssetHelper: successfully opened database quotes.sqlite3 06-29
> 14:56:20.870 19467-19467/com.domain.sample I/Jalal: copyDataBase()
> 06-29 14:56:20.870 19467-19467/com.domain.sample D/AndroidRuntime:
> Shutting down VM
> 
> > --------- beginning of crash 06-29 14:56:20.870 19467-19467/com.domain.sample E/AndroidRuntime: FATAL EXCEPTION: main
>     Process: com.domain.sample, PID: 19467
>     java.lang.Error: Error copying com.domain.sample.database!
>         at com.domain.sample.database.ExternalDbOpenHelper.createDataBase(ExternalDbOpenHelper.java:78)
>         at com.domain.sample.database.ExternalDbOpenHelper.openDataBase(ExternalDbOpenHelper.java:133)
>         at com.domain.sample.database.ExternalDbOpenHelper.<init>(ExternalDbOpenHelper.java:48)
>         at com.domain.sample.database.ExternalDbOpenHelper.getInstance(ExternalDbOpenHelper.java:32)
>         at com.domain.sample.loader.AbstractQueryLoader.<init>(AbstractQueryLoader.java:14)
>         at com.domain.sample.loader.QuoteGroupLoader.<init>(QuoteGroupLoader.java:17)
>         at com.domain.sample.QuoteGroupActivity.onCreateLoader(QuoteGroupActivity.java:154)
>         at android.support.v4.app.LoaderManagerImpl.createAndInstallLoader(LoaderManagerImpl.java:370)
>         at android.support.v4.app.LoaderManagerImpl.initLoader(LoaderManagerImpl.java:404)
>         at com.domain.sample.QuoteGroupActivity.onCreate(QuoteGroupActivity.java:107)
>         at android.app.Activity.performCreate(Activity.java:7131)
>         at android.app.Activity.performCreate(Activity.java:7122)
>         at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1271)
>         at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2882)
>         at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:3037)
>         at android.app.servertransaction.LaunchActivityItem.execute(LaunchActivityItem.java:78)
>         at android.app.servertransaction.TransactionExecutor.executeCallbacks(TransactionExecutor.java:108)
>         at android.app.servertransaction.TransactionExecutor.execute(TransactionExecutor.java:68)
>         at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1797)
>         at android.os.Handler.dispatchMessage(Handler.java:106)
>         at android.os.Looper.loop(Looper.java:193)
>         at android.app.ActivityThread.main(ActivityThread.java:6642)
>         at java.lang.reflect.Method.invoke(Native Method)
>         at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:493)
>         at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:858) 06-29
> 14:56:20.882 19467-19467/com.domain.sample I/Process: Sending signal.
> PID: 19467 SIG: 9

Вот класс:

public class ExternalDbOpenHelper extends SQLiteAssetHelper {
    //Path to the device folder with databases
    public static String DB_PATH;
    //Database file name
    public static final String DB_NAME = quotesDatabaseInfo.DB_NAME;
    public static final int DB_VERSION = quotesDatabaseInfo.DB_VERSION;

    private static ExternalDbOpenHelper sInstance;

    public SQLiteDatabase database;
    public Context context;


    public static ExternalDbOpenHelper getInstance(Context context) {

        if (sInstance == null) {
            sInstance = new ExternalDbOpenHelper(
                    context.getApplicationContext(), DB_NAME);
        }

        Log.i("Jalal", "ExternalDbOpenHelper getInstance(Context context)");
        return sInstance;
    }

    private ExternalDbOpenHelper(Context context, String databaseName) {

        super(context, databaseName, null, DB_VERSION);
        this.context = context;
        Log.i("Jalal", "ExternalDbOpenHelper(Context context, String databaseName)");
        //Write a full path to the databases of your application
        String packageName = context.getPackageName();
        //DB_PATH = String.format("//data//data//%s//databases//", packageName);
        DB_PATH = context.getApplicationInfo().dataDir +"/databases/";
        openDataBase();


    }

    public ExternalDbOpenHelper(Context context){
        super(context, DB_NAME, null, DB_VERSION);

        Log.i("Jalal", "ExternalDbOpenHelper(Context context)");
    }

    public SQLiteDatabase getDb() {
        Log.i("Jalal", "SQLiteDatabase getDb()");
        return database;
    }




    //This piece of code will create a com.domain.sample.database if it’s not yet created
    public void createDataBase() {
        Log.i("Jalal", "createDataBase()");
        boolean dbExist = checkDataBase();
        if (!dbExist) {
            this.getReadableDatabase();
            //this.getWritableDatabase();

            try {
                copyDataBase();
            } catch (IOException e) {
                throw new Error("Error copying com.domain.sample.database!");
            }
        } else {
            Log.i(this.getClass().toString(), "Database already exists");
        }
    }

    //Performing a com.domain.sample.database existence check
    private boolean checkDataBase() {
        Log.i("Jalal", "checkDataBase()");

        SQLiteDatabase checkDb = null;

        try {
            String path = DB_PATH + DB_NAME;
            checkDb = SQLiteDatabase.openDatabase(path, null,
                    SQLiteDatabase.OPEN_READONLY);
        } catch (SQLException e) {
            Log.e(this.getClass().toString(), "Error while checking db");
        }
        //Android doesn’t like resource leaks, everything should
        // be closed
        if (checkDb != null) {
            checkDb.close();
        }
        return checkDb != null;
    }

    //Method for copying the com.domain.sample.database
    private void copyDataBase() throws IOException {
        Log.i("Jalal", "copyDataBase()");
        //Open a stream for reading from our ready-made com.domain.sample.database
        //The stream source is located in the assets
        InputStream externalDbStream = context.getAssets().open(DB_NAME);
        //Path to the created empty com.domain.sample.database on your Android device
        String outFileName = DB_PATH + DB_NAME;
        //Now create a stream for writing the com.domain.sample.database byte by byte
        OutputStream localDbStream = new FileOutputStream(outFileName);
        //Copying the com.domain.sample.database
        byte[] buffer = new byte[1024];
        int bytesRead;
        while ((bytesRead = externalDbStream.read(buffer)) > 0) {
            localDbStream.write(buffer, 0, bytesRead);
        }
        //Don’t forget to close the streams
        localDbStream.flush();
        localDbStream.close();
        externalDbStream.close();
    }

    public SQLiteDatabase openDataBase() throws SQLException {
        Log.i("Jalal", "openDataBase()");
        final String  path = DB_PATH + DB_NAME;

        if (database == null) {
            createDataBase();
            database = SQLiteDatabase.openDatabase(path, null,
                    SQLiteDatabase.OPEN_READWRITE);
        }
        return database;
    }

    @Override
    public synchronized void close() {
        Log.i("Jalal", "close");

        if (database != null) {
            database.close();
        }
        super.close();
    }
    // @Override
    // public void onCreate(SQLiteDatabase db) {}
    @Override
    public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {}


  }

1 Ответ

0 голосов
/ 01 июля 2018

Вы, кажется, слишком усложняете ситуацию То есть, если вы используете подкласс SQLiteAssetHelper, вам просто нужно позволить подклассу делать свое дело. т.е. он будет копировать базу данных из папки активов / баз данных по мере необходимости.

В качестве примера для уже существующей базы данных .../assets/databases/Data.db и при условии использования синглтона: -

Подкласс SQLiteAssetHelper (DatabaseOpenHelper в этом примере) может быть: -

public class DatabaseOpenHelper extends SQLiteAssetHelper {
    private static final String DATABASE_NAME = "Data.db";
    private static final int DATABASE_VERSION = 1;

    public DatabaseOpenHelper(Context context) {
        super(context, DATABASE_NAME, null, DATABASE_VERSION);
    }
}

Класс для Singleton может быть: -

public class DatabaseAccess {
    private SQLiteAssetHelper openHelper;
    private static SQLiteDatabase database;
    private static DatabaseAccess instance;


    private DatabaseAccess(Context context) {
        this.openHelper = new DatabaseOpenHelper(context);
    }

    /**
     * Return a singleton instance of DatabaseAccess.
     *
     * @param context the Context
     * @return the instance of DabaseAccess
     */
    public static DatabaseAccess getInstance(Context context) {
        if (instance == null) {
            instance = new DatabaseAccess(context);
            database = instance.openHelper.getWritableDatabase();
        }
        return instance;
    }

    /**
     * Get the SQLiteDatabase
     * @return the SQLiteDatabase
     */
    public SQLiteDatabase getDatabase() {
        return this.database;
    }
}

Код вызова (в данном случае MainActivity) может быть таким: -

public class MainActivity extends AppCompatActivity {
    SQLiteDatabase mDB;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        DatabaseAccess dba = DatabaseAccess.getInstance(this); // get DBAccess Instance
        mDB = dba.getDatabase(); //Get the database
        // use the database (list tables and indexes)
        Cursor csr = mDB.query("sqlite_master",
                null,
                null,
                null  ,
                null,
                null,
                "name"
        );

        StringBuilder tables = new StringBuilder();
        StringBuilder indexes = new StringBuilder();
        while (csr.moveToNext()) {
            String current_name = csr.getString(csr.getColumnIndex("name"));
            String current_type = csr.getString(csr.getColumnIndex("type"));
            switch (current_type) {
                case "table":
                    tables.append("\n\t").append(current_name);
                    break;
                case "index":
                    indexes.append("\n\t").append(current_name);
                    break;
            }
        }
        csr.close();
        Log.d("DB TABLES","Tables in the Database are :-" + tables.toString());
        Log.d("DB INDEXES","Indexes in the Database are:-" + indexes);
    }

    @Override
    protected void onDestroy() {
        if (mDB.isOpen()) {
            mDB.close();
        }
        super.onDestroy();
    }
}

Предполагая, что в настоящее время не существует, вывод будет выглядеть следующим образом: -

07-01 00:55:18.567 1436-1436/? W/SQLiteAssetHelper: copying database from assets...
    database copy complete
07-01 00:55:18.571 1436-1439/? D/dalvikvm: GC_CONCURRENT freed 227K, 10% free 6168K/6791K, paused 1ms+0ms, total 8ms
07-01 00:55:18.615 1436-1436/? I/SQLiteAssetHelper: successfully opened database Data.db
07-01 00:55:18.615 1436-1436/? D/DB TABLES: Tables in the Database are :-
        android_metadata
        data
07-01 00:55:18.619 1436-1436/? D/DB INDEXES: Indexes in the Database are:- 

Если база данных существует, то результат будет выглядеть следующим образом: -

07-01 00:56:52.502 1483-1483/? I/SQLiteAssetHelper: successfully opened database Data.db
07-01 00:56:52.502 1483-1483/? D/DB TABLES: Tables in the Database are :-
        android_metadata
        data
07-01 00:56:52.502 1483-1483/? D/DB INDEXES: Indexes in the Database are:-
  • т.е. нет W/SQLiteAssetHelper: copying database from assets... database copy complete

Если файл ресурса не существует или возникла другая проблема, то результат будет выглядеть следующим образом: -

07-01 00:58:57.004 1552-1552/? W/SQLiteAssetHelper: copying database from assets...
07-01 00:58:57.004 1552-1552/? D/AndroidRuntime: Shutting down VM
07-01 00:58:57.004 1552-1552/? W/dalvikvm: threadid=1: thread exiting with uncaught exception (group=0xa6223288)
07-01 00:58:57.004 1552-1552/? E/AndroidRuntime: FATAL EXCEPTION: main
    java.lang.RuntimeException: Unable to start activity ComponentInfo{examples.mjt.sqliteassethelperexample/examples.mjt.sqliteassethelperexample.MainActivity}: com.readystatesoftware.sqliteasset.SQLiteAssetHelper$SQLiteAssetException: Missing databases/Dataaaaa.db file (or .zip, .gz archive) in assets, or target folder not writable
        at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2059)
        at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2084)
        at android.app.ActivityThread.access$600(ActivityThread.java:130)
        at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1195)
        at android.os.Handler.dispatchMessage(Handler.java:99)
        at android.os.Looper.loop(Looper.java:137)
        at android.app.ActivityThread.main(ActivityThread.java:4745)
        at java.lang.reflect.Method.invokeNative(Native Method)
        at java.lang.reflect.Method.invoke(Method.java:511)
        at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:786)
        at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:553)
        at dalvik.system.NativeStart.main(Native Method)
     Caused by: com.readystatesoftware.sqliteasset.SQLiteAssetHelper$SQLiteAssetException: Missing databases/Dataaaaa.db file (or .zip, .gz archive) in assets, or target folder not writable
        at android.content.res.AssetManager.openAsset(Native Method)
        at android.content.res.AssetManager.open(AssetManager.java:315)
        at android.content.res.AssetManager.open(AssetManager.java:289)
        at com.readystatesoftware.sqliteasset.SQLiteAssetHelper.copyDatabaseFromAssets(SQLiteAssetHelper.java:436)
        at com.readystatesoftware.sqliteasset.SQLiteAssetHelper.createOrOpenDatabase(SQLiteAssetHelper.java:400)
        at com.readystatesoftware.sqliteasset.SQLiteAssetHelper.getWritableDatabase(SQLiteAssetHelper.java:176)
        at examples.mjt.sqliteassethelperexample.DatabaseAccess.open(DatabaseAccess.java:45)
        at examples.mjt.sqliteassethelperexample.MainActivity.onCreate(MainActivity.java:33)
        at android.app.Activity.performCreate(Activity.java:5008)
        at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1079)
        at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2023)
        at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2084) 
        at android.app.ActivityThread.access$600(ActivityThread.java:130) 
        at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1195) 
        at android.os.Handler.dispatchMessage(Handler.java:99) 
        at android.os.Looper.loop(Looper.java:137) 
        at android.app.ActivityThread.main(ActivityThread.java:4745) 
        at java.lang.reflect.Method.invokeNative(Native Method) 
        at java.lang.reflect.Method.invoke(Method.java:511) 
        at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:786) 
        at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:553) 
        at dalvik.system.NativeStart.main(Native Method) 
  • В этом случае вместо Data.db файл был изменен на Dataaaaa.b , чтобы вызвать эту проблему.

Как видите, нет необходимости писать собственный код для проверки и копирования базы данных.


Кроме того, вы не получите сбивающий с толку дамп, когда проверка уже существующей базы данных не найдет базу данных согласно: -

06-29 14:56:20.811 19467-19467/com.domain.sample E/SQLiteLog: (14) cannot open file at line 36667 of [0c55d17973]
    (14) os_unix.c:36667: (2) open(/data/user/0/com.domain.sample/databases/quotes.sqlite3) -  06-29 14:56:20.812 19467-19467/com.domain.sample E/SQLiteDatabase: Failed to open database '/data/user/0/com.domain.sample/databases/quotes.sqlite3'.
    android.database.sqlite.SQLiteCantOpenDatabaseException: unknown error (code 14 SQLITE_CANTOPEN): Could not open database
        at android.database.sqlite.SQLiteConnection.nativeOpen(Native Method)
        at android.database.sqlite.SQLiteConnection.open(SQLiteConnection.java:211)
        at android.database.sqlite.SQLiteConnection.open(SQLiteConnection.java:195)
        at android.database.sqlite.SQLiteConnectionPool.openConnectionLocked(SQLiteConnectionPool.java:503)
        at android.database.sqlite.SQLiteConnectionPool.open(SQLiteConnectionPool.java:204)
        at android.database.sqlite.SQLiteConnectionPool.open(SQLiteConnectionPool.java:196)
        at android.database.sqlite.SQLiteDatabase.openInner(SQLiteDatabase.java:880)
        at android.database.sqlite.SQLiteDatabase.open(SQLiteDatabase.java:865)
        at android.database.sqlite.SQLiteDatabase.openDatabase(SQLiteDatabase.java:766)
        at android.database.sqlite.SQLiteDatabase.openDatabase(SQLiteDatabase.java:714)
        at com.domain.sample.database.ExternalDbOpenHelper.checkDataBase(ExternalDbOpenHelper.java:93)
        at com.domain.sample.database.ExternalDbOpenHelper.createDataBase(ExternalDbOpenHelper.java:70)
        at com.domain.sample.database.ExternalDbOpenHelper.openDataBase(ExternalDbOpenHelper.java:133)
        at com.domain.sample.database.ExternalDbOpenHelper.<init>(ExternalDbOpenHelper.java:48)
        at com.domain.sample.database.ExternalDbOpenHelper.getInstance(ExternalDbOpenHelper.java:32)
        at com.domain.sample.loader.AbstractQueryLoader.<init>(AbstractQueryLoader.java:14)
        at com.domain.sample.loader.QuoteGroupLoader.<init>(QuoteGroupLoader.java:17)
        at com.domain.sample.QuoteGroupActivity.onCreateLoader(QuoteGroupActivity.java:154)
        at android.support.v4.app.LoaderManagerImpl.createAndInstallLoader(LoaderManagerImpl.java:370)
        at android.support.v4.app.LoaderManagerImpl.initLoader(LoaderManagerImpl.java:404)
        at com.domain.sample.QuoteGroupActivity.onCreate(QuoteGroupActivity.java:107)
        at android.app.Activity.performCreate(Activity.java:7131)
        at android.app.Activity.performCreate(Activity.java:7122)
        at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1271)
        at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2882)
        at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:3037)
        at android.app.servertransaction.LaunchActivityItem.execute(LaunchActivityItem.java:78)
        at android.app.servertransaction.TransactionExecutor.executeCallbacks(TransactionExecutor.java:108)
        at android.app.servertransaction.TransactionExecutor.execute(TransactionExecutor.java:68)
        at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1797)
        at android.os.Handler.dispatchMessage(Handler.java:106)
        at android.os.Looper.loop(Looper.java:193)
        at android.app.ActivityThread.main(ActivityThread.java:6642)
        at java.lang.reflect.Method.invoke(Native Method)
        at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:493)
        at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:858) 06-29 14:56:20.812 19467-19467/com.domain.sample
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...