0

I am trying to bundle a DB with an APK. I am using Android Studio 2.2.3 and API 24 on my device(physical and emulator).

Basically what I want to do is, use my already created DB(created using python) and generate dynamic HTML for the app from that DB. I read we can't use this DB directly and we must copy it to app/APK.

I get the following error in debug log:

    E/SQLiteLog: (14) cannot open file at line 32456 of [bda77dda96]
E/SQLiteLog: (14) os_unix.c:32456: (2) open(/data/data/com.sunnah.sunnahapp/databases/test.sqlite) - 
E/SQLiteDatabase: Failed to open database '/data/data/com.sunnah.sunnahapp/databases/test.sqlite'.
                  android.database.sqlite.SQLiteCantOpenDatabaseException: unknown error (code 14): Could not open database
                      at android.database.sqlite.SQLiteConnection.nativeOpen(Native Method)
                      at android.database.sqlite.SQLiteConnection.open(SQLiteConnection.java:209)
                      at android.database.sqlite.SQLiteConnection.open(SQLiteConnection.java:193)
                      at android.database.sqlite.SQLiteConnectionPool.openConnectionLocked(SQLiteConnectionPool.java:463)
                      at android.database.sqlite.SQLiteConnectionPool.open(SQLiteConnectionPool.java:185)
                      at android.database.sqlite.SQLiteConnectionPool.open(SQLiteConnectionPool.java:177)
                      at android.database.sqlite.SQLiteDatabase.openInner(SQLiteDatabase.java:808)
                      at android.database.sqlite.SQLiteDatabase.open(SQLiteDatabase.java:793)
                      at android.database.sqlite.SQLiteDatabase.openDatabase(SQLiteDatabase.java:696)
                      at android.database.sqlite.SQLiteDatabase.openDatabase(SQLiteDatabase.java:671)
                      at com.sunnah.sunnahapp.DatabaseHelper1.openDataBase(DatabaseHelper1.java:133)
                      at com.sunnah.sunnahapp.DatabaseHelper1.<init>(DatabaseHelper1.java:38)
                      at com.sunnah.sunnahapp.MainActivity.onCreate(MainActivity.java:56)
                      at android.app.Activity.performCreate(Activity.java:6736)
                      at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1119)
                      at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2636)
                      at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2744)
                      at android.app.ActivityThread.-wrap12(ActivityThread.java)
                      at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1493)
                      at android.os.Handler.dispatchMessage(Handler.java:102)
                      at android.os.Looper.loop(Looper.java:154)
                      at android.app.ActivityThread.main(ActivityThread.java:6195)
                      at java.lang.reflect.Method.invoke(Native Method)
                      at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:874)
                      at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:764)
D/AndroidRuntime: Shutting down VM
E/AndroidRuntime: FATAL EXCEPTION: main
                  Process: com.sunnah.sunnahapp, PID: 14478
                  java.lang.RuntimeException: Unable to start activity ComponentInfo{com.sunnah.sunnahapp/com.sunnah.sunnahapp.MainActivity}: android.database.sqlite.SQLiteCantOpenDatabaseException: unknown error (code 14): Could not open database
                      at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2683)
                      at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2744)
                      at android.app.ActivityThread.-wrap12(ActivityThread.java)
                      at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1493)
                      at android.os.Handler.dispatchMessage(Handler.java:102)
                      at android.os.Looper.loop(Looper.java:154)
                      at android.app.ActivityThread.main(ActivityThread.java:6195)
                      at java.lang.reflect.Method.invoke(Native Method)
                      at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:874)
                      at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:764)
                   Caused by: android.database.sqlite.SQLiteCantOpenDatabaseException: unknown error (code 14): Could not open database
                      at android.database.sqlite.SQLiteConnection.nativeOpen(Native Method)
                      at android.database.sqlite.SQLiteConnection.open(SQLiteConnection.java:209)
                      at android.database.sqlite.SQLiteConnection.open(SQLiteConnection.java:193)
                      at android.database.sqlite.SQLiteConnectionPool.openConnectionLocked(SQLiteConnectionPool.java:463)
                      at android.database.sqlite.SQLiteConnectionPool.open(SQLiteConnectionPool.java:185)
                      at android.database.sqlite.SQLiteConnectionPool.open(SQLiteConnectionPool.java:177)
                      at android.database.sqlite.SQLiteDatabase.openInner(SQLiteDatabase.java:808)
                      at android.database.sqlite.SQLiteDatabase.open(SQLiteDatabase.java:793)
                      at android.database.sqlite.SQLiteDatabase.openDatabase(SQLiteDatabase.java:696)
                      at android.database.sqlite.SQLiteDatabase.openDatabase(SQLiteDatabase.java:671)
                      at com.sunnah.sunnahapp.DatabaseHelper1.openDataBase(DatabaseHelper1.java:133)
                      at com.sunnah.sunnahapp.DatabaseHelper1.<init>(DatabaseHelper1.java:38)
                      at com.sunnah.sunnahapp.MainActivity.onCreate(MainActivity.java:56)
                      at android.app.Activity.performCreate(Activity.java:6736)
                      at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1119)
                      at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2636)
                      at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2744) 
                      at android.app.ActivityThread.-wrap12(ActivityThread.java) 
                      at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1493) 
                      at android.os.Handler.dispatchMessage(Handler.java:102) 
                      at android.os.Looper.loop(Looper.java:154) 
                      at android.app.ActivityThread.main(ActivityThread.java:6195) 
                      at java.lang.reflect.Method.invoke(Native Method) 
                      at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:874) 
                      at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:764) 
E/MQSEventManagerDelegate: failed to get MQSService.
I/Process: Sending signal. PID: 14478 SIG: 9
Disconnected from the target VM, address: 'localhost:8602', transport: 'socket'

I tried looking for some solution on SO but they didn't work for me. Some said DB size may be issue. My DB was 15MB then I created a test.sqlite DB with just one table and row. Others said, it is because of permissions on SD card. I don't use SD card. It still din't work. The error remains same.

I tried placing my test.sqlite DB in assets and assets/database folder but it still give same error.

Below is my my code:

DatabaseHelper1.java

package com.sunnah.sunnahapp;

import android.content.Context;
import android.content.ContextWrapper;
import android.database.sqlite.SQLiteException;
import android.database.SQLException;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;

import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import android.util.Log;

public class DatabaseHelper1 extends SQLiteOpenHelper {

    //The Android's default system path of your application database.
    private static String DB_PATH;  //"/data/data/YOUR_PACKAGE/databases/";
    private static String DB_NAME = "test.sqlite";

    private SQLiteDatabase myDataBase;
    private final Context myContext;

    /**
     * Constructor
     * Takes and keeps a reference of the passed context in order to access to the application assets and resources.
     * @param context
     */
    public DatabaseHelper1(Context context) {

        super(context, DB_NAME, null, 1);
        this.myContext = context;

        //Write a full path to the databases of your application
        String packageName = context.getPackageName();
        DB_PATH = String.format("/data/data/%s/databases/", packageName);
        openDataBase();

    }

    /**
     * Creates a empty database on the system and rewrites it with your own database.
     * */
    public void createDataBase() throws IOException {
        Log.d("Create DB Func", "My Message");

        boolean dbExist = checkDataBase();
        Log.d("Past DB Create", "My Message 1");

        if(dbExist){
            Log.e(this.getClass().toString(), "Copying error");
        }else{

            //By calling this method and empty database will be created into the default system path
            //of your application so we are gonna be able to overwrite that database with our database.
            this.getReadableDatabase();
            Log.d("getDatabase Func", "My Messag2e");

            try {
                Log.d("b4 Copy DB Func", "My Messag2e");
                copyDataBase();
                Log.d("after copy dB Func", "My Messag2e");
            } catch (IOException e) {

                throw new Error("Error copying database");
            }
        }

    }

    /**
     * Check if the database already exist to avoid re-copying the file each time you open the application.
     * @return true if it exists, false if it doesn't
     */
    private boolean checkDataBase(){

        SQLiteDatabase checkDB = null;

        try{
            String myPath = DB_PATH + DB_NAME;
            checkDB = SQLiteDatabase.openDatabase(myPath, null, SQLiteDatabase.OPEN_READONLY);

        } catch(SQLiteException e){
            Log.e(this.getClass().toString(), "Error while checking db");
            //database does't exist yet.

        }

        if(checkDB != null){

            checkDB.close();

        }

        return checkDB != null ? true : false;
    }

    /**
     * Copies your database from your local assets-folder to the just created empty database in the
     * system folder, from where it can be accessed and handled.
     * This is done by transfering bytestream.
     * */
    private void copyDataBase() throws IOException{

        //Open your local db as the input stream
        InputStream myInput = myContext.getAssets().open(DB_NAME);

        // Path to the just created empty db
        String outFileName = DB_PATH + DB_NAME;

        //Open the empty db as the output stream
        OutputStream myOutput = new FileOutputStream(outFileName);

        //transfer bytes from the inputfile to the outputfile
        byte[] buffer = new byte[1024];
        int length;
        while ((length = myInput.read(buffer))>0){
            myOutput.write(buffer, 0, length);
        }

        //Close the streams
        myOutput.flush();
        myOutput.close();
        myInput.close();

    }

    public void openDataBase() throws SQLException {

        //Open the database
        String myPath = DB_PATH + DB_NAME;
        myDataBase = SQLiteDatabase.openDatabase(myPath, null, SQLiteDatabase.OPEN_READONLY);

    }

    @Override
    public synchronized void close() {

        if(myDataBase != null)
            myDataBase.close();

        super.close();

    }

    @Override
    public void onCreate(SQLiteDatabase db) {

    }

    @Override
    public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {

    }

    // Add your public helper methods to access and get content from the database.
    // You could return cursors by doing "return myDataBase.query(....)" so it'd be easy
    // to you to create adapters for your views.

}

MainActivity.java:

package com.sunnah.sunnahapp;

import android.graphics.Bitmap;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.webkit.WebView;
import android.webkit.WebViewClient;


//DB PART
import com.sunnah.sunnahapp.R;
import com.sunnah.sunnahapp.R.layout;
import com.sunnah.sunnahapp.DatabaseHelper1;

import android.app.Activity;
import android.app.ListActivity;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.os.Bundle;
import android.view.View;
import android.widget.AdapterView;
import android.widget.AdapterView.OnItemClickListener;
import android.widget.ArrayAdapter;
import android.widget.ListView;
import android.widget.TextView;
import android.widget.Toast;

import java.io.IOException;

//DB PART END





public class MainActivity extends AppCompatActivity {

    private WebView webView;
    private SQLiteDatabase database;
    //private static final String DB_NAME = "bukhari.sqlite";

     @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);


        setContentView(R.layout.activity_main);
        webView = (WebView) findViewById(R.id.webView);
        webView.setWebViewClient(new MainActivity.myWebClient());


         // Database Part START
         //Our key helper

         DatabaseHelper1 dbOpenHelper; //= new DatabaseHelper1();
         dbOpenHelper = new DatabaseHelper1(this);

         try {
             dbOpenHelper.createDataBase();
         } catch (IOException ioe)    {
             throw new Error("Unable to create database");

         }

         //database = dbOpenHelper.openDataBase();
         //That’s it, the database is open!

        //Database Part END

        webView.getSettings().setJavaScriptEnabled(true);
        webView.loadUrl("file:///android_asset/www/index.html");
    }


    public class myWebClient extends WebViewClient
    {
        @Override
        public void onPageStarted(WebView view, String url, Bitmap favicon) {
            super.onPageStarted(view, url, favicon);
        }

        @Override
        public boolean shouldOverrideUrlLoading(WebView view, String url) {

            view.loadUrl(url);
            return true;
        }
    }

    @Override
    // This method is used to detect back button
    public void onBackPressed() {
        if(webView.canGoBack()) {
            webView.goBack();
        } else {
            // Let the system handle the back button
            super.onBackPressed();
        }
    }
}

Can you please guide me here. I tried changing path that actually existed(directory structure, that is). What am I missing? How do I create an empty DB if it doesn't exist on the given path. I thought it would be created in that case, as mentioned by the DatabaseHelper1 class comments.

Vishal Yadav
  • 3,642
  • 3
  • 25
  • 42
Abu Qatada
  • 211
  • 3
  • 16

1 Answers1

0

OK. It seems the checkDatabase() was throwing exception when the DB didn't exist(first app start/install). It should not throw exception because it would then call copyDatabase().

So I commented out throw new Error("database does't exist yet.");

I also made some other changes so my DatabaseHelper1.java now loos like this, in case it helps someone:

package com.sunnah.sunnahapp;

import android.content.Context;
import android.content.ContextWrapper;
import android.database.sqlite.SQLiteException;
import android.database.SQLException;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;

import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.File;
import android.util.Log;

public class DatabaseHelper1 extends SQLiteOpenHelper {

    //The Android's default system path of your application database.
    private static String DB_PATH = "data/data/com.sunnah.sunnahapp/databases/";  //"/data/data/YOUR_PACKAGE/databases/";
    private static String DB_NAME = "test.sqlite";

    private SQLiteDatabase myDataBase;
    private final Context myContext;

    /**
     * Constructor
     * Takes and keeps a reference of the passed context in order to access to the application assets and resources.
     * @param context
     */
    public DatabaseHelper1(Context context) {

        super(context, DB_NAME, null, 1);
        this.myContext = context;

        //Write a full path to the databases of your application
        //String packageName = context.getPackageName();
        //DB_PATH = String.format("/data/data/%s/databases/", packageName);
        //openDataBase();

    }

    /**
     * Creates a empty database on the system and rewrites it with your own database.
     * */
    public void createDataBase() throws IOException {

        boolean dbExist;
        try {

            dbExist = checkDataBase();


        } catch (SQLiteException e) {

            e.printStackTrace();
            throw new Error("database dose not exist");

        }


        if(dbExist){
            //do nothing - database already exist
        }else{
            try {

                copyDataBase();


            } catch (IOException e) {

                e.printStackTrace();
                throw new Error("Error copying database");

            }
            //By calling this method and empty database will be created into the default system path
            //of your application so we are gonna be able to overwrite that database with our database.
            this.getReadableDatabase();
        }

    }

    /**
     * Check if the database already exist to avoid re-copying the file each time you open the application.
     * @return true if it exists, false if it doesn't
     */
    private boolean checkDataBase(){

        SQLiteDatabase checkDB = null;

        try{
            String myPath = DB_PATH + DB_NAME;
            checkDB = SQLiteDatabase.openDatabase(myPath, null, SQLiteDatabase.OPEN_READONLY);

        } catch(SQLiteException e){
            //Log.e(this.getClass().toString(), "Error while checking db");
            //database does't exist yet.
            //throw new Error("database does't exist yet.");

        }

        if(checkDB != null){

            checkDB.close();

        }

        return checkDB != null ? true : false;
    }

    /**
     * Copies your database from your local assets-folder to the just created empty database in the
     * system folder, from where it can be accessed and handled.
     * This is done by transfering bytestream.
     * */
    private void copyDataBase() throws IOException{

        //Open your local db as the input stream
        InputStream myInput = myContext.getAssets().open(DB_NAME);

        // Path to the just created empty db
        String outFileName = DB_PATH + DB_NAME;

        File databaseFile = new File( DB_PATH);
        // check if databases folder exists, if not create one and its subfolders
        if (!databaseFile.exists()){
            databaseFile.mkdir();
        }
        //Open the empty db as the output stream
        OutputStream myOutput = new FileOutputStream(outFileName);

        //transfer bytes from the inputfile to the outputfile
        byte[] buffer = new byte[1024];
        int length;
        while ((length = myInput.read(buffer))>0){
            myOutput.write(buffer, 0, length);
        }

        //Close the streams
        myOutput.flush();
        myOutput.close();
        myInput.close();

    }

    public void openDataBase() throws SQLException {

        //Open the database
        String myPath = DB_PATH + DB_NAME;
        myDataBase = SQLiteDatabase.openDatabase(myPath, null, SQLiteDatabase.OPEN_READONLY);

    }

    @Override
    public synchronized void close() {

        if(myDataBase != null)
            myDataBase.close();

        super.close();

    }

    @Override
    public void onCreate(SQLiteDatabase db) {

    }

    @Override
    public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {

    }

    // Add your public helper methods to access and get content from the database.
    // You could return cursors by doing "return myDataBase.query(....)" so it'd be easy
    // to you to create adapters for your views.

}

Thank you very much.

Abu Qatada
  • 211
  • 3
  • 16