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.