从资产复制sqlite数据库时出现“无法打开数据库”错误在Android 4.2中

我的代码工作正常对于Android 2.3但我不知道为什么它不适用于Android 4.2

我的exception日志就在这里

01-17 09:54:04.411: E/SQLiteLog(24202): (14) cannot open file at line 30176 of [00bb9c9ce4] 01-17 09:54:04.411: E/SQLiteLog(24202): (14) os_unix.c:30176: (2) open(/data/data/com.example.myapp/databases/myapp.db) - Failed to open database '/data/data/com.example.mypapp/databases/myapp.db'. 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:804) at android.database.sqlite.SQLiteDatabase.open(SQLiteDatabase.java:789) at android.database.sqlite.SQLiteDatabase.openDatabase(SQLiteDatabase.java:694) at android.database.sqlite.SQLiteDatabase.openDatabase(SQLiteDatabase.java:669) at com.example.myapp.DataBaseHelper.checkDataBase(DataBaseHelper.java:80) at com.example.myapp.DataBaseHelper.createDataBase(DataBaseHelper.java:47) at com.example.myapp.MainActivity.onCreate(MainActivity.java:61) at android.app.Activity.performCreate(Activity.java:5104) at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1080) at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2144) at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2230) at android.app.ActivityThread.access$600(ActivityThread.java:141) at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1234) at android.os.Handler.dispatchMessage(Handler.java:99) at android.os.Looper.loop(Looper.java:137) at android.app.ActivityThread.main(ActivityThread.java:5039) 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:793) at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:560) at dalvik.system.NativeStart.main(Native Method) 

我的MainActivity.java

 public class MainActivity extends Activity { public void onCreate(Bundle icicle) { super.onCreate(icicle); setContentView(R.layout.activity_main); DataBaseHelper myDbHelper; myDbHelper = new DataBaseHelper(this); try { myDbHelper.createDataBase(); } catch (IOException ioe) { throw new Error("Unable to create database"); } try { myDbHelper.openDataBase(); } catch (SQLException sqle) { throw sqle; } finally { myDbHelper.close(); } }} 

我的DatabaseHelper.java

 public class DataBaseHelper extends SQLiteOpenHelper{ //The Android's default system path of your application database. private static String DB_PATH = "/data/data/com.example.myapp/databases/"; private static String DB_NAME = "myapp.db"; 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 DataBaseHelper(Context context) { super(context, DB_NAME, null, 1); this.myContext = context; } /** * Creates a empty database on the system and rewrites it with your own database. * */ public void createDataBase() throws IOException{ boolean dbExist = checkDataBase(); if(dbExist){ //do nothing - database already exist }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(); try { copyDataBase(); } 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.i("Hellloooo", e.getMessage()); //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. } 

我不得不修改checkDataBase()

  private boolean checkDataBase() { File databasePath = mContext.getDatabasePath(DB_NAME); return databasePath.exists(); } 

我遇到了这个问题,因为在4.2 ,有多用户支持,如果你用非管理员用户测试,你就不能访问/data/data路径..

在我的情况下,我正在使用路径:

 context.getFilesDir().getAbsolutePath().replace("files", "databases") + File.separator 

希望有所帮助..

有些设备无法处理大型资产。 我所做的是将数据库拆分为1个MiB块并将它们复制到一个文件中

我创建了一个脚本(用于Linux)来轻松拆分文件

 #!/bin/bash #split database file into chunks not larger than 1 MiB split -b 1048576 databasename db -d -a1 

这些块将命名为db0,db1,db2,…,dbN

 /** * Copies database from assets to a file if not exists. * * @param context Application's Context */ public static void copyDatabase(Context context) { File target = context.getDatabasePath(databasename"); if (target.exists()) { return; } target.getParentFile().mkdirs(); InputStream is = null; OutputStream os = null; try { List filesl = new ArrayList(); String[] files = context.getAssets().list(""); if (files != null) { for (int i = 0; i < files.length; i++) { if (Pattern.matches("^db\\d.db$", files[i])) { filesl.add(files[i]); } } } files = null; Collections.sort(filesl); if (filesl.isEmpty()) { return; } os = new FileOutputStream(target); for (int i = 0; i < filesl.size(); i++) { is = context.getAssets().open(filesl.get(i), AssetManager.ACCESS_BUFFER); byte[] buffer = new byte[5096]; int read; while (true) { read = is.read(buffer, 0, buffer.length); if (read < 0) break; os.write(buffer, 0, read); } is.close(); } } catch (Exception e) { if (target.exists()) { target.delete(); } e.printStackTrace(); } finally { if (is != null) try { is.close(); } catch (Exception e) { } if (os != null) try { os.close(); } catch (Exception e) { } } } 

请注意, Collections.sort将按正确的顺序对名称进行排序,如果不是更多的10个文件 ,则db0 ... db9 - 在db10之后,您将不得不编写自己的排序或协作器。