我正在尝试将数据库与APK捆绑在一起。我在我的设备上使用Android Studio 2.2.3和API 24(物理和模拟器)。
基本上我想要做的是,使用我已创建的数据库(使用python创建)并从该数据库为应用程序生成动态HTML。我知道我们无法直接使用此数据库,我们必须将其复制到app / APK。
我在调试日志中收到以下错误:
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'
我尝试在SO上寻找一些解决方案,但他们并没有为我工作。有人说DB大小可能会有问题。我的数据库是15MB,然后我创建了一个只有一个表和行的test.sqlite数据库。其他人说,这是因为SD卡的权限。我不使用SD卡。它仍然没有用。错误保持不变。
我尝试将test.sqlite数据库放在资产和资产/数据库文件夹中,但它仍然会出现同样的错误。
以下是我的代码:
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();
}
}
}
你能在这里指导我吗?我尝试改变实际存在的路径(目录结构,即)。
我错过了什么?
如果在给定路径上不存在空数据库,如何创建空数据库。我认为在这种情况下会创建它,如DatabaseHelper1类注释所述。
答案 0 :(得分:0)
行。当数据库不存在(第一个应用程序启动/安装)时,checkDatabase()似乎抛出了异常。它不应该抛出异常,因为它会调用copyDatabase()。
所以我评论了throw new Error("database does't exist yet.");
我还做了一些其他更改,所以我的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 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.
}
非常感谢。