Страница 1 из 1
SQLite на SD карте
Добавлено: 31 янв 2012, 17:01
tyapavel
Как-то мне понадобилось создать в приложении базу данных и сохранить её на карте памяти. Использовать для этого SQLiteOpenHelper не получилось, потому что он создаёт базу не на карте, а в телефоне, и скопировать её потом без root проблематично. damager82 помог разобраться с SQLiteDatabase.openOrCreateDatabase(), огромное ему спасибо. Я написал класс - MyDataBaseOpenHelper, выкладываю может кому пригодится.
Код: Выделить всё
import java.io.File;
import android.content.Context;
import android.database.sqlite.SQLiteDatabase;
import android.os.Environment;
public abstract class MyDataBaseOpenHelper {
private SQLiteDatabase db;
private SQLiteDatabase readDb;
private SQLiteDatabase writeDb;
private File dbFile;
private String dbName;
private boolean isExistDB;
public MyDataBaseOpenHelper(Context context, String dbName) {
super();
this.isExistDB = false;
this.dbName = dbName;
this.prepareBD();
if(!isExistDB){
onCreate(db);
}
this.setReadDb();
this.setWriteDb();
}
private void prepareBD() {
try{
File sdCard = Environment.getExternalStorageDirectory();
File directory = new File(sdCard.getAbsolutePath () + "/" + dbName);
directory.mkdirs();
dbFile = new File(directory, dbName);
if(dbFile.exists())isExistDB=true;
this.db = SQLiteDatabase.openOrCreateDatabase(dbFile, null);
}catch (Exception e) {
this.db = SQLiteDatabase.openOrCreateDatabase(dbName, null);
}
}
abstract public void onCreate(SQLiteDatabase db);
abstract public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion);
public void close(){
db.close();
writeDb.close();
readDb.close();
}
public SQLiteDatabase openMyDb() {
SQLiteDatabase db = SQLiteDatabase.openOrCreateDatabase(dbFile, null);
return db;
}
public SQLiteDatabase getReadableDatabase(){
return this.readDb;
}
private void setReadDb(){
SQLiteDatabase resdDb = SQLiteDatabase.openDatabase(dbFile.getAbsolutePath(), null, SQLiteDatabase.OPEN_READONLY);
this.readDb = resdDb;
}
public SQLiteDatabase getWritableDatabase(){
return this.writeDb;
}
private void setWriteDb(){
SQLiteDatabase resdDb = SQLiteDatabase.openDatabase(dbFile.getAbsolutePath(), null, SQLiteDatabase.OPEN_READWRITE);
this.writeDb = resdDb;
}
}
Вопрос у меня вот какой! За объектом SQLiteOpenHelper следит активность в которой он создан вметоде onCreate(), и переопределять onDestroy() не нужно, наверно для этого и передаётся в конструкторе SQLiteOpenHelper Context. Объект моего класса обязательно надо закрыть явно в onDestroy() Activity. Какой метод в Context отвечает за закрытие открытых баз данных. Извините если что-то не так. Я не профессионал.
Re: SQLite на SD карте
Добавлено: 01 фев 2012, 16:35
damager82
tyapavel писал(а):За объектом SQLiteOpenHelper следит активность в которой он создан в методе onCreate()
Я не встречал у Activity возможности следить за SQLiteOpenHelper. Вы уверены?
Activity умеет управлять объектом Cursor, если вызвать метод startManagingCursor.
Re: SQLite на SD карте
Добавлено: 01 фев 2012, 20:42
tyapavel
Сделал два простеньких тестовых приложени. Одно с родным SQLiteOpenHelper другое с моим MyDataBaseOpenHelper
1.)
Код: Выделить всё
import android.app.Activity;
import android.content.ContentValues;
import android.content.Context;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;
import android.os.Bundle;
public class MainActivity extends Activity {
DbHelper dbHelper;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
dbHelper = new DbHelper(this, "Test");
}
class DbHelper extends SQLiteOpenHelper{
public DbHelper(Context context, String name) {
super(context, name, null, 1);
}
@Override
public void onCreate(SQLiteDatabase db) {
db.execSQL("create table TestDataDase(_id integer primary key autoincrement, firstname text, lastname text)");
ContentValues cv = new ContentValues();
cv.put("firstname", "Fyodor");
cv.put("lastname","Lastochkin");
db.insert("TestDataDase", null, cv);
}
@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
db.execSQL("DROP TABLE IF EXISTS TestDataDase");
onCreate(db);
}
}
}
2.)
Код: Выделить всё
import android.app.Activity;
import android.content.ContentValues;
import android.content.Context;
import android.database.sqlite.SQLiteDatabase;
import android.os.Bundle;
public class MainActivity extends Activity {
DbHelper dbHelper;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
dbHelper = new DbHelper(this, "Test");
}
class DbHelper extends MyDataBaseOpenHelper{
public DbHelper(Context context, String name) {
super(context, name);
}
@Override
public void onCreate(SQLiteDatabase db) {
db.execSQL("create table TestDataDase(_id integer primary key autoincrement, firstname text, lastname text)");
ContentValues cv = new ContentValues();
cv.put("firstname", "Fyodor");
cv.put("lastname","Lastochkin");
db.insert("TestDataDase", null, cv);
}
@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
db.execSQL("DROP TABLE IF EXISTS TestDataDase");
onCreate(db);
}
}
}
В первомслучае программа закрывается нормально, а во втором LogCat выводит
02-01 17:21:10.564: E/Database(282): close() was never explicitly called on database '/mnt/sdcard/Test/Test'
02-01 17:16:27.914: E/Database(282): android.database.sqlite.DatabaseObjectNotClosedException: Application did not close the cursor or database object that was opened here
А если во втором случае переопределить onDestroy() и вызвать dbHelper.close(), то всё нормально!
Re: SQLite на SD карте
Добавлено: 02 фев 2012, 00:59
damager82
Все верно.
В первом случае подключение не открывается.
А во втором вы в конструкторе суперкласса открываете подключения и их надо закрывать перед выходом из приложения.
Re: SQLite на SD карте
Добавлено: 02 фев 2012, 14:30
tyapavel
Понятно. Изменил суперкласс. Теперь Actyvity.onDestroy() можно не переопределять.
Код: Выделить всё
import java.io.File;
import android.content.Context;
import android.database.sqlite.SQLiteDatabase;
import android.os.Environment;
public abstract class MyDataBaseOpenHelper {
private SQLiteDatabase db;
private File dbFile;
private String dbName;
private boolean isExistDB;
public MyDataBaseOpenHelper(Context context, String dbName) {
super();
this.isExistDB = false;
this.dbName = dbName;
this.prepareBD();
if(!this.isExistDB){
onCreate(db);
}
db.close();
}
private void prepareBD() {
try{
File sdCard = Environment.getExternalStorageDirectory();
File directory = new File(sdCard.getAbsolutePath () + "/" + dbName);
directory.mkdirs();
dbFile = new File(directory, dbName);
if(dbFile.exists())this.isExistDB=true;
this.db = SQLiteDatabase.openOrCreateDatabase(dbFile, null);
}catch (Exception e) {}
}
abstract public void onCreate(SQLiteDatabase db);
abstract public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion);
public void close(){
db.close();
}
public SQLiteDatabase getReadableDatabase(){
SQLiteDatabase resdDb = SQLiteDatabase.openDatabase(dbFile.getAbsolutePath(), null, SQLiteDatabase.OPEN_READWRITE);
return resdDb;
}
public SQLiteDatabase getWritableDatabase(){
SQLiteDatabase resdDb = SQLiteDatabase.openDatabase(dbFile.getAbsolutePath(), null, SQLiteDatabase.OPEN_READWRITE);
return resdDb;
}
}
Re: SQLite на SD карте
Добавлено: 24 апр 2012, 08:43
SKR
tyapavel, спасибо, очень пригодилось!
Re: SQLite на SD карте
Добавлено: 10 мар 2013, 13:49
beeline09
У меня есть одна проблема. Есть класс ExternalDbOpenHelper, который открывает мою базу с флешки, а если ее нет, то копирует ее из assets в папку на флешке. В нем база открывается в режиме readwrite. Так вот, при сохранении записей в БД, они есть и я спокойно отображаю нужное значение в spinner-е, но после пересоздания активити они все пропадают.
Вот класс:
Код: Выделить всё
;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import android.content.Context;
import android.database.SQLException;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;
import android.os.Environment;
import android.util.Log;
public class ExternalDbOpenHelper extends SQLiteOpenHelper {
//Путь к папке с базами на устройстве
public static String DB_PATH;
//Имя файла с базой
public static String DB_NAME;
public SQLiteDatabase database;
public final Context context;
final String DIR_SD = "Hudeem";
final String dbname = "data.db";
public SQLiteDatabase getDb() {
return database;
}
public ExternalDbOpenHelper(Context context, String databaseName) {
super(context, databaseName, null, 1);
this.context = context;
//Составим полный путь к базам для вашего приложения
File sdPath = Environment.getExternalStorageDirectory();
DB_NAME = databaseName;
DB_PATH = String.format(sdPath.getAbsolutePath() + "/" + DIR_SD + "/", DB_NAME);
try {
copyDataBase();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
openDataBase();
}
//Создаст базу, если она не создана
public void createDataBase() {
boolean dbExist = checkDataBase();
if (!dbExist) {
this.getReadableDatabase();
try {
copyDataBase();
} catch (IOException e) {
Log.e(this.getClass().toString(), "Copying error");
throw new Error("Error copying database!");
}
} else {
Log.i(this.getClass().toString(), "Database already exists");
}
}
//Проверка существования базы данных
private boolean checkDataBase() {
SQLiteDatabase checkDb = null;
try {
String path = DB_PATH + DB_NAME;
checkDb = SQLiteDatabase.openDatabase(path, null,
SQLiteDatabase.OPEN_READONLY);
} catch (SQLException e) {
Log.e(this.getClass().toString(), "Error while checking db");
}
//Андроид не любит утечки ресурсов, все должно закрываться
if (checkDb != null) {
checkDb.close();
}
return checkDb != null;
}
//Метод копирования базы
private void copyDataBase() throws IOException {
// Открываем поток для чтения из уже созданной нами БД
//источник в assets
InputStream externalDbStream = context.getAssets().open(DB_NAME);
// Путь к уже созданной пустой базе в андроиде
String outFileName = DB_PATH + DB_NAME;
// Теперь создадим поток для записи в эту БД побайтно
OutputStream localDbStream = new FileOutputStream(outFileName);
// Собственно, копирование
byte[] buffer = new byte[1024];
int bytesRead;
while ((bytesRead = externalDbStream.read(buffer)) > 0) {
localDbStream.write(buffer, 0, bytesRead);
}
// Мы будем хорошими мальчиками (девочками) и закроем потоки
localDbStream.close();
externalDbStream.close();
}
public SQLiteDatabase openDataBase() throws SQLException {
String path = DB_PATH + DB_NAME;
createDataBase();
if (database == null) {
createDataBase();
database = SQLiteDatabase.openDatabase(path, null,
SQLiteDatabase.OPEN_READWRITE);
}
return database;
}
@Override
public synchronized void close() {
if (database != null) {
database.close();
}
super.close();
}
@Override
public void onCreate(SQLiteDatabase db) {}
@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {}
}
Вот так записываю данные:
Код: Выделить всё
ExternalDbOpenHelper extdb = new ExternalDbOpenHelper(this, DB_NAME);
db = extdb.openDataBase();
String sql = "INSERT INTO Data(data, title, ves, kkal100, itogo) VALUES ("+"'"+date_time+"'"+","+"'"+title+"'"+","+"'"+ves+"'"+","+"'"+kkal100+"'"+","+"'"+itogo_str+"'"+")";
db.execSQL(sql);
Obnovut_spiner_iz_bazy();
Spinner обновляется и новая строчка в нем появляется. Но как только переоткрою активити, то никаких изменений в БД не замечается. Т.е. она не записалась
Re: SQLite на SD карте
Добавлено: 16 мар 2013, 00:36
MoonNah
beeline09 писал(а):Spinner обновляется и новая строчка в нем появляется. Но как только переоткрою активити, то никаких изменений в БД не замечается. Т.е. она не записалась
Брат, ты все-таки дело с БД имеешь
Код: Выделить всё
ExternalDbOpenHelper extdb = new ExternalDbOpenHelper(this, DB_NAME);
db = extdb.openDataBase();
try {
db.beginTransaction();
String sql = "INSERT INTO Data(data, title, ves, kkal100, itogo) VALUES ("+"'"+date_time+"'"+","+"'"+title+"'"+","+"'"+ves+"'"+","+"'"+kkal100+"'"+","+"'"+itogo_str+"'"+")";
db.execSQL(sql);
db.setTransactionSuccessful();
} catch (SQLException e) {
} finally {
db.endTransaction();
}
Obnovut_spiner_iz_bazy();
без транзакции фиг что в базу запишется
Re: SQLite на SD карте
Добавлено: 18 мар 2013, 08:07
beeline09
MoonNah писал(а):
без транзакции фиг что в базу запишется
А что мне даст это? Если у меня база одна. Доступ к ней только из одной активти, пишу в нее только в том куске кода, который указал выше, курсор закрывается каждый раз. И что значит фиг знает что? Хочешь сказать, что разные значения не в свои столбцы могут попасть?
Re: SQLite на SD карте
Добавлено: 21 мар 2013, 01:40
MoonNah
beeline09 писал(а):А что мне даст это?
так, давай определимся, ты хочешь подискутировать на эту тему, или хочешь чтобы данные сохранились в базе , если второе, то используй транзакцию, потому что
1. при таком подходе база будет залочена для любого другого обращения (сам ты не сможешь это гарантировать на 100%)
2. в базу запишется корректно инфа, если там будут ошибки, то все откатится и не положит базу
3. Самое главное, что информация физически запишется в файл базы данных
Короче проверяй и убедишься (просто я так работаю в своей программе переводчике и программе контроля за автомобилем со спутниковой сигналкой и неоднократно сталкивался с подобным вопросом, а на начальном этапе и сам ловил такую плюху, да и мануал SQLite, как и у других БД, рекомендует пользовать транзакции).
И еще нафига ты такое в запросе нагородил (куча ненужных плюсов и кавычек)
Код: Выделить всё
String sql = "INSERT INTO Data(data, title, ves, kkal100, itogo) VALUES ("+"'"+date_time+"'"+","+"'"+title+"'"+","+"'"+ves+"'"+","+"'"+kkal100+"'"+","+"'"+itogo_str+"'"+")";
когда гораздо удобоваримее написать было так
Код: Выделить всё
String sql = "INSERT INTO Data(data, title, ves, kkal100, itogo) VALUES ('"+date_time+"', '"+title+"', '"+ves+"', '"+kkal100+"', '"+itogo_str+"')";
Re: SQLite на SD карте
Добавлено: 21 мар 2013, 12:22
beeline09
MoonNah писал(а):beeline09 писал(а):А что мне даст это?
так, давай определимся, ты хочешь подискутировать на эту тему, или хочешь чтобы данные сохранились в базе , если второе, то используй транзакцию, потому что
1. при таком подходе база будет залочена для любого другого обращения (сам ты не сможешь это гарантировать на 100%)
2. в базу запишется корректно инфа, если там будут ошибки, то все откатится и не положит базу
3. Самое главное, что информация физически запишется в файл базы данных
Короче проверяй и убедишься (просто я так работаю в своей программе переводчике и программе контроля за автомобилем со спутниковой сигналкой и неоднократно сталкивался с подобным вопросом, а на начальном этапе и сам ловил такую плюху, да и мануал SQLite, как и у других БД, рекомендует пользовать транзакции).
И еще нафига ты такое в запросе нагородил (куча ненужных плюсов и кавычек)
Код: Выделить всё
String sql = "INSERT INTO Data(data, title, ves, kkal100, itogo) VALUES ("+"'"+date_time+"'"+","+"'"+title+"'"+","+"'"+ves+"'"+","+"'"+kkal100+"'"+","+"'"+itogo_str+"'"+")";
когда гораздо удобоваримее написать было так
Код: Выделить всё
String sql = "INSERT INTO Data(data, title, ves, kkal100, itogo) VALUES ('"+date_time+"', '"+title+"', '"+ves+"', '"+kkal100+"', '"+itogo_str+"')";
Хорошо, не могу не согласиться - сделаю в следующей версии. Сейчас уже обновленная в маркете лежит.
А на счет запроса да, торопился вот и нагородил. Я это уже исправил ))
Спасибо за помощь!
Re: SQLite на SD карте
Добавлено: 13 фев 2014, 17:55
AnDron
tyapavel писал(а):Понятно. Изменил суперкласс. Теперь Actyvity.onDestroy() можно не переопределять.
Код: Выделить всё
import java.io.File;
import android.content.Context;
import android.database.sqlite.SQLiteDatabase;
import android.os.Environment;
public abstract class MyDataBaseOpenHelper {
private SQLiteDatabase db;
private File dbFile;
private String dbName;
private boolean isExistDB;
public MyDataBaseOpenHelper(Context context, String dbName) {
super();
this.isExistDB = false;
this.dbName = dbName;
this.prepareBD();
if(!this.isExistDB){
onCreate(db);
}
db.close();
}
private void prepareBD() {
try{
File sdCard = Environment.getExternalStorageDirectory();
File directory = new File(sdCard.getAbsolutePath () + "/" + dbName);
directory.mkdirs();
dbFile = new File(directory, dbName);
if(dbFile.exists())this.isExistDB=true;
this.db = SQLiteDatabase.openOrCreateDatabase(dbFile, null);
}catch (Exception e) {}
}
abstract public void onCreate(SQLiteDatabase db);
abstract public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion);
public void close(){
db.close();
}
public SQLiteDatabase getReadableDatabase(){
SQLiteDatabase resdDb = SQLiteDatabase.openDatabase(dbFile.getAbsolutePath(), null, SQLiteDatabase.OPEN_READWRITE);
return resdDb;
}
public SQLiteDatabase getWritableDatabase(){
SQLiteDatabase resdDb = SQLiteDatabase.openDatabase(dbFile.getAbsolutePath(), null, SQLiteDatabase.OPEN_READWRITE);
return resdDb;
}
}
Не могу понять как поменять в данном случае версию базы? Создаваемая база всегда имеет версию "0".
Re: SQLite на SD карте
Добавлено: 13 фев 2014, 19:07
altwin
AnDron писал(а):tyapavel писал(а):Понятно. Изменил суперкласс. Теперь Actyvity.onDestroy() можно не переопределять.
Код: Выделить всё
import java.io.File;
import android.content.Context;
import android.database.sqlite.SQLiteDatabase;
import android.os.Environment;
public abstract class MyDataBaseOpenHelper {
private SQLiteDatabase db;
private File dbFile;
private String dbName;
private boolean isExistDB;
public MyDataBaseOpenHelper(Context context, String dbName) {
super();
this.isExistDB = false;
this.dbName = dbName;
this.prepareBD();
if(!this.isExistDB){
onCreate(db);
}
db.close();
}
private void prepareBD() {
try{
File sdCard = Environment.getExternalStorageDirectory();
File directory = new File(sdCard.getAbsolutePath () + "/" + dbName);
directory.mkdirs();
dbFile = new File(directory, dbName);
if(dbFile.exists())this.isExistDB=true;
this.db = SQLiteDatabase.openOrCreateDatabase(dbFile, null);
}catch (Exception e) {}
}
abstract public void onCreate(SQLiteDatabase db);
abstract public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion);
public void close(){
db.close();
}
public SQLiteDatabase getReadableDatabase(){
SQLiteDatabase resdDb = SQLiteDatabase.openDatabase(dbFile.getAbsolutePath(), null, SQLiteDatabase.OPEN_READWRITE);
return resdDb;
}
public SQLiteDatabase getWritableDatabase(){
SQLiteDatabase resdDb = SQLiteDatabase.openDatabase(dbFile.getAbsolutePath(), null, SQLiteDatabase.OPEN_READWRITE);
return resdDb;
}
}
Не могу понять как поменять в данном случае версию базы? Создаваемая база всегда имеет версию "0".
ответ в этой строчке: abstract public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion);
т.е. базу нужно не создавать а обновлять. Но это именно в этом случае.