android - not - sqlite3 commands
Error "Error al abrir la base de datos" al copiar una base de datos sqlite de los activos en Android 4.2 (3)
Mi código funciona bien para Android 2.3 pero no sé por qué no funciona para Android 4.2
Mi registro de excepciones está aquí
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)
Mi 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();
}
}}
Mi 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.
}
Enfrenté este problema, porque en 4.2
, hay soporte para múltiples usuarios, y si está probando con un usuario no administrador, no puede acceder /data/data
path.
En mi caso estoy usando la ruta:
context.getFilesDir().getAbsolutePath().replace("files", "databases") + File.separator
Espero que ayude..
Algunos dispositivos no pueden manejar grandes activos. Lo que hice fue dividir la base de datos en 1 trozo de MiB y los copié en un archivo
Creé un script (para Linux) para dividir fácilmente el archivo
#!/bin/bash
#split database file into chunks not larger than 1 MiB
split -b 1048576 databasename db -d -a1
Los fragmentos se denominarán 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<String> filesl = new ArrayList<String>();
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) {
}
}
}
Tenga en cuenta que Collections.sort ordenará los nombres en el orden correcto si no más de 10 archivos en el caso db0 ... db9 - después de db10 tendrá que escribir su propia clasificación o comarador.
Tuve que modificar checkDataBase () para
private boolean checkDataBase() {
File databasePath = mContext.getDatabasePath(DB_NAME);
return databasePath.exists();
}