Android: remove data from database - android

I am using this code (android-sqlite-asset-helper) to load a database from a file place in the asset folder. This works great.
However, the processing of refreshing/upgrading the database is not simple, and I am wondering if there is a simple way to manually remove all data from the databases in the app; in order to load a new database from the asset file.

You can use simple copy file to override data in default database. Simply by overwriting default database with your new database file.
The following code work only with a file, so you need a little change here and there to make it work with asset file.
Here the method to overwrite the database file:
/**
* Copies the database file at the specified location over the current
* internal application database.
**/
public boolean importDatabase(Context context, String dbPath) throws IOException {
File OldDbFile = context.getApplicationContext().getDatabasePath(DBSchema.DATABASE_NAME);
// Close the SQLiteOpenHelper so it will commit the created empty
// database to internal storage.
close();
File newDb = new File(dbPath);
if (newDb.exists()) {
FileUtils.copyFile(new FileInputStream(newDb), new FileOutputStream(OldDbFile));
// Access the copied database so SQLiteHelper will cache it and mark
// it as created.
getWritableDatabase().close();
return true;
}
return false;
}
FileUtils class:
public class FileUtils {
/**
* Creates the specified <code>toFile</code> as a byte for byte copy of the
* <code>fromFile</code>. If <code>toFile</code> already exists, then it
* will be replaced with a copy of <code>fromFile</code>. The name and path
* of <code>toFile</code> will be that of <code>toFile</code>.<br/>
* <br/>
* <i> Note: <code>fromFile</code> and <code>toFile</code> will be closed by
* this function.</i>
*
* #param fromFile - FileInputStream for the file to copy from.
* #param toFile - FileInputStream for the file to copy to.
*/
public static void copyFile(FileInputStream fromFile, FileOutputStream toFile)
throws IOException {
FileChannel fromChannel = null;
FileChannel toChannel = null;
try {
fromChannel = fromFile.getChannel();
toChannel = toFile.getChannel();
fromChannel.transferTo(0, fromChannel.size(), toChannel);
} finally {
try {
if (fromChannel != null) {
fromChannel.close();
}
} finally {
if (toChannel != null) {
toChannel.close();
}
}
}
}
}
I really forget where I took the copyFile method :(.
There is one caveat: when user cleaning app data, the database will be back to default.

It seems to work with this in the main activity creation:
getApplicationContext().deleteDatabase("mydatabase.db");

Related

How to change the default database file location of Room DataBase?

I am using RoomDB in my app. able to perform crud operations.
Actually i want to see the db file.
getDatabasePath("user.db").getAbsolutePath();
above code is giving me the directory where the db file is saves
directory is like this
/data/data/com.example.manvish.roomdb/databases/user.db
but still i am unable to access the data directory even using sudo from command prompt.
now i want to change the DB file location to some other folders in internal memory or SD card. how can i do this?
Java solution:
Grant permissions in Manifest
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
Create db (Here SofDatabase is a singleton class)
private static final String DB_NAME = "stack_overflow_db";
private static final String DB_PATH = String.format("%s/%s",
Environment.getExternalStorageDirectory().getAbsolutePath(), DB_NAME);
public static synchronized SofDatabase getInstance(Context aContext) {
if (sInstance == null) {
sInstance = Room.databaseBuilder(aContext, SofDatabase.class, DB_PATH)
.fallbackToDestructiveMigration()
.addCallback(roomCallback).build(); //adding callback from Room
}
return sInstance;
}
Callback
/**
* Get Notified once db is created
*/
private static final RoomDatabase.Callback roomCallback = new RoomDatabase.Callback() {
#Override
public void onCreate(#NonNull SupportSQLiteDatabase db) {
super.onCreate(db);
Log.i("SOF", db.getPath()); //which prints out --> I/SOF: /storage/emulated/0/stack_overflow_db
// add some jobs once db is created
}
};
To change the db location just put the path when you build the Room database, an example for a db in your folder, in the internal storage:
fun getDatabase(context: Context, scope: CoroutineScope): TestDatabase {
return INSTANCE ?: synchronized(this) {
val instance =
Room.databaseBuilder(
context.applicationContext,
TestDatabase::class.java,
Environment.getExternalStorageDirectory().absolutePath + "/yourFolder/yourdb"
).build()
INSTANCE = instance
return instance
}
}
This is a Kotlin example, if you need it in Java, just let me know.
Regards.
So guys I'm posting my solution because I got stuck for several hours on this problem and I was almost convinced that we couldn't use a database that is located elsewhere than the "/data/data" internal directory of the application with Room
The solution is however very simple. with the following code we have the IllegalArgumentException exception: "File ... contains a path separator"
private fun buildDatabase(context: Context) : RMSRoomDatabase {
val packageName: String = context.getApplicationInfo().packageName
var path = "sdcard/Android/data/$packageName/files/rms_database.sqlite"
var builder = Room.databaseBuilder(
context,
RMSRoomDatabase::class.java,
path
)
return builder.allowMainThreadQueries()
.fallbackToDestructiveMigration()
.build()
}
But by simply changing the path with an slash as the first character, everything works correctly!
var path = "/sdcard/Android/data/$packageName/files/rms_database.sqlite"
Not sure if this is what you are looking for but in my case just like #M.Yogeshwaran I needed to be able to setup an initial state of the database and work from there so I ended up doing this:
/**
* #param context
*/
public DatabaseService(Context context) {
File dst = context.getDatabasePath(DB_NAME);
dst.delete();
File src = new File(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS).getPath()+"/database.db");
try {
copy(src,dst);
} catch (IOException e) {
e.printStackTrace();
}
// Init the database singleton
db = Room.databaseBuilder(context, CrediforceDatabase.class, Constants.DB_NAME).build();
}
public static void copy(File src, File dst) throws IOException {
try (InputStream in = new FileInputStream(src)) {
try (OutputStream out = new FileOutputStream(dst)) {
// Transfer bytes from in to out
byte[] buf = new byte[1024];
int len;
while ((len = in.read(buf)) > 0) {
out.write(buf, 0, len);
}
}
}
}
To give a bit more clarity:
DB_NAME = "database.db"
For testing purposes, I have the "database.db" that I want to "import" in my downloads folder, later on I will convert this code so instead the device pulls the initial database from the server.
the dst.delete(); is just to delete the existing database file as I was testing things out (not sure if required)
This basically translates to: Start the app > Replace the database file > build the database.
Gotta be careful with the identity_hash in the room_master_table, if this is different from what's supposed to be it's not going to work.
Also, I pulled this idea from this answer:
https://stackoverflow.com/a/50383879/2150472
Hope it helps anyone!
P.S, if what you need is just to be able to see the data and perform CRUD operations you can always use this plugging: https://github.com/amitshekhariitbhu/Android-Debug-Database

Azure blob storage - Copy Block blobs in the SAME container

I'm using Azure Storage with the Android API. I'm trying to copy one block blob from one location to another (in the SAME blob container).
However, when I make the copy I get a "CannotVerifyCopySource" error after invoking the startCopy method.
Example Code:
private void sample(String path1, String path2, File file) {
CloudBlockBlob blob1 = container.getBlockBlobReference(path1);
CloudBlockBlob blob2 = container.getBlockBlobReference(path2);
blob1.upload(new FileInputStream(file), file.length());
blob1.startCopy(blob2);
}
Any ideas on what might be the problem?
Best regards,
see source about startCopy below. I think you should use blob2.startCopy(blob1) here.
/**
* Requests the service to start copying a block blob's contents, properties, and metadata to a new block blob.
*
* #param sourceBlob
* A <code>CloudBlockBlob</code> object that represents the source blob to copy.
*
* #return A <code>String</code> which represents the copy ID associated with the copy operation.
*
* #throws StorageException
* If a storage service error occurred.
* #throws URISyntaxException
*/
#DoesServiceRequest
public final String startCopy(final CloudBlockBlob sourceBlob) throws StorageException, URISyntaxException {
return this.startCopy(sourceBlob, null /* sourceAccessCondition */,
null /* destinationAccessCondition */, null /* options */, null /* opContext */);
}

I try to download a google file, there isn't errors but it doesn't work

I'm developing an app with google drive and I can see my files with these app. I want to download the files, but I can't.
This is my function, is equal to google documentation:
Thanks un advance!!
/* Download a file's content.
*
* #param service Drive API service instance.
* #param file Drive File instance.
*
* #return InputStream containing the file's content if successful, {#code null} otherwise.
*/
public static InputStream downloadFile(Drive service, File file) throws IOException
{
if (file.getDownloadUrl() != null && file.getDownloadUrl().length() > 0)
{
try
{
HttpResponse resp = service.getRequestFactory().buildGetRequest(new GenericUrl(file.getDownloadUrl())).execute();
return resp.getContent();
}
catch (IOException e)
{
// An error occurred.
e.printStackTrace();
return null;
}
}
else
{
// The file doesn't have any content stored on Drive.
return null;
}
}
No errors in Logcat, but I don't see any file in downloads, sd-card, etc
If there isn't error, I have to see very carefull in sd-card, system etc?
it's necessarely anything else??
You are getting the file content and returning an InputStream however your code doesn't show what you are doing with this InputStream. You need to use it to save the content of the file to Disk or database etc... Please have a look at the link that FaddishWorm sent you in comments which writes the file to the SD Card: Download a file with Android, and showing the progress in a ProgressDialog

Android: copy database from asset folder, but only get an empty file

guys, I have the problem when copying database from local assets folder to /data/data/package_name/databases directory. As I use the http://www.reigndesign.com/blog/using-your-own-sqlite-database-in-android-applications/ tutorial to do it, I can only get an empty file.
I quoted the part of copyDataBase() method and there is no difference. Every time the app start, it will create the directory and empty database. So is there any way to make the copyDataBase() work?
Thank you very much!!
Why wouldn't you copy from assets? It's perfectly normal to do so. But you can't do it in the onCreate, at that point an empty database is already created. You need to do it prior. I usually do it in an override of getWriteableDatabase, something like
public synchronized SQLiteDatabase getWritableDatabase() {
SQLiteDatabase db = null;
if (!doesDatabaseExist()) {
try {
copyDatabase();
db = super.getWritableDatabase();
} catch(Exception ex) {
Log.e("Database Log", getDatabasePath() + " failed to copy correctly. " + ex.getLocalizedMessage());
}
}
else {
db = super.getWritableDatabase();
}
return db;
}
I wouldn't copy any database form the assets-folder. If you need some standard entry's in your Database, you can add them using INSERTs in your onCreate()-method.
Update: Since this is getting down-voted for being wrong (which is kinda right) and I can't delete it, here is a little update.
I'd say it depends upon how many standard entries you want to add to your database. If it's just one or two, shipping a packed DB might not be worth it.
Anyways, some apps come with rather large databases (for example, a recipe collection). You can obviously not add all these in code.
For small test-entries, I'd still prefer simply adding them in onCreate().
For bigger databases, you should pre-populate them and ship em along with your app.
For the later to work, you'll need to copy the database file from assets/ to your app-folder. There is a nice library to handle that for you: android-sqlite-asset-helper
I don't know if it is still usefull but here is the solution for others that get here to see the awnser. The code you used, works for most phones, some older phones have different behaviour with the getReadableDatabase() function. Your problem therefore is not in the copyDataBase function but in the createDataBase function.
in createDataBase() there is the following check;
this.getReadableDatabase();
This checks if there is already a database with the provided name and if not creates an empty database such that it can be overwritten with the one in the assets folder. On newer devices this works flawlessly but there are some devices on which this doesn't work. Mainly older devices. I do not know exactly why, but it seems like the getReadableDatabase() function not only gets the database but also opens it. If you then copy the database from the assets folder over it, it still has the pointer to an empty database and you will get table does not exist errors.
So in order to make it work on all devices you should modify it to the following lines:
SQLiteDatabase db = this.getReadableDatabase();
if (db.isOpen()){
db.close();
}
Even if the database is opened in the check, it is closed thereafter and it will not give you any more trouble.
at the right above example worked for me this way:
db = super.getWritableDatabase();
db.close;
copyDatabase();
otherwise i got an IO error;
Here is the simple and convenient code which I use:
public class DataBaseImportHelper {
private DataBaseImportHelper() {}; // Avoid instantiation
/**
* Creates a empty database on the system and rewrites it with your own database.
*/
public static boolean importDataBase(Context context) {
InputStream myInput = null;
OutputStream myOutput = null;
try {
// Open local db from assets as the input stream
myInput = context.getAssets().open(DATABASE_NAME);
createEmptyDatabase(context); // See this method below
// Open the empty db as the output stream
myOutput = new FileOutputStream(getDatabaseFile(context));
// 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();
myInput.close();
myOutput.close();
return true;
} catch (IOException e) {
e.printStackTrace();
}
return false;
}
/**
* Check if the database already exists.
* #return true if it exists, false if it doesn't
*/
public static boolean isDatabaseExists(Context context) {
return getDatabaseFile(context).exists();
}
private static File getDatabaseFile(Context context) {
return context.getDatabasePath(DatabaseHelper.DATABASE_NAME);
}
/**
* Create an empty database into the default application database
* folder.So we are gonna be able to overwrite that database with our database
*/
private static void createEmptyDatabase(Context context) {
// use anonimous helper to create empty database
new SQLiteOpenHelper(context, DatabaseHelper.DATABASE_NAME, null, 1) {
// Methods are empty. We don`t need to override them
#Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
}
#Override
public void onCreate(SQLiteDatabase db) {
}
}.getReadableDatabase().close();
}
}
And using in code:
if(!DataBaseImportHelper.isDatabaseExists(this)){
if (!DataBaseImportHelper.importDataBase(this)){
throw new IllegalStateException("Database doesn`t exist and hasn`t been copied!");
}
}

How to put existing database in the .apk file?

I have prebuild database of mostly string objects. I want to know how to put in my apk file so the database will be already created when the user installs the database.
I found a good example of this: Using your own SQLite database in Android applications
Basically, you export the created database as an sql-file and store it in the assets-folder.
On the first program start, you import the data of the file into your final database.
I think it's the best approach, however your data will be there twice in the apk and the db, using some more storage space.
I've just started developing for Android, and was surprised to discover that bundling a static database is not easy to do. So I did the only reasonable thing: created a library which does just that. Example usage:
import android.database.sqlite.SQLiteDatabase;
import kiwidrew.staticdb.StaticDatabase;
public class BlahBlah extends Activity {
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
SQLiteDatabase db = StaticDatabase.openDatabase(this, "assets/foobar.db");
// Do normal database stuff with 'db'...
}
}
You get back a standard SQLiteDatabase object, with the only restriction being that it doesn't support writing. (Obviously!)
Note that this will fail unless the database is stored in your .apk without compression. Add the sqlite database using the aapt -0 command or modify your build.xml to pass the <nocompress extension="db" /> flag to the <aapt> tag...
Get the code at http://bitbucket.org/kiwidrew/android-staticdb.
Note: I've literally just finished writing this, and have only done very basic testing so far. Bug reports would be appreciated!
There are no any simple way to read database from assets directly. You should copy your database from assets to data folder in the first run, then when every time your app starts up, you should check database in the data folder and copy it again if the database does not exist.
These steps help you:
1) Execute these commands on your database, if android_metadata table does not exist in your database, android could not open your database.:
CREATE TABLE android_metadata(locale TEXT DEFAULT 'en_US')
INSERT INTO android_metadata VALUES('en_US')
2) Chunk your database because android does not support reading a file that more than 1 MB size from assets.
This python code chunks your database:
def chunk_file(file_name):
input_file = open(file_name, "rb")
chunk_counter = 0;
while True:
chunk = input_file.read(512 * 1024) # 512 KB
if chunk:
output_file_name = file_name + "." + str(chunk_counter).zfill(4)
output_file = open(output_file_name, "wb")
output_file.write(chunk)
output_file.close()
chunk_counter += 1
else:
break
input_file.close()
return
# Input: database.db
# Output: database.db.0000, database.db.0001, database.db.0002, ...
chunk_file("database.db")
Then put database.db.0000, database.db.0001, database.db.0002, ... in the assets folder.
3) Check database exists in the data folder when app starts up.
public static boolean databaseExists() {
boolean result = false;
SQLiteDatabase checkDB = null;
try {
checkDB = SQLiteDatabase.openDatabase(
getApplicationContext().getFilesDir().getPath() + "/database.db",
null, SQLiteDatabase.OPEN_READONLY);
result = true;
} catch (SQLiteException exception) {
result = false;
}
if (checkDB != null) {
checkDB.close();
}
return result;
}
4) If database does not exist in data folder, copy database from assets to data folder.
public static void copyDatabase() throws IOException {
AssetManager assets = getApplicationContext().getAssets();
// database.db.0000, database.db.0001, database.db.0002, ... --> databaseChunks.
String[] databaseChunks = assets.list("");
Arrays.sort(databaseChunks);
OutputStream databaseStream = new FileOutputStream(
getApplicationContext().getFilesDir().getPath() + "/database.db");
for (int i = 0; i < databaseChunks.length; i++) {
String databaseChunkName = databaseChunks[i];
InputStream chunkStream = assets.open(databaseChunkName);
int length;
byte[] buffer = new byte[1024];
while ((length = chunkStream.read(buffer)) > 0) {
databaseStream.write(buffer, 0, length);
}
chunkStream.close();
}
databaseStream.close();
return;
}
5) You can connect to the database now:
SQLiteDatabase database = SQLiteDatabase.openDatabase(
getApplicationContext().getFilesDir().getPath() + "/database.db",
null, SQLiteDatabase.OPEN_READONLY);
// ...
database.close();

Categories

Resources