Crash after attempting to restore realm - android

I'm attempting to implement a backup/restore functionality for my android application using Realm. After I restore the data from the backup, if I try to access any information (that would request data from realm), the app crashes A/libc: Fatal signal 7 (SIGBUS), code 2, fault addr 0x9694d000 in tid 5047 (...), pid 5047 (...)
Keep in mind that the after reopening the app, data is restored (so backup/restore works as expected apart from the crash)
This is my RealmBackup class
public class RealmBackup {
private final static String TAG = RealmBackup.class.getName();
private Context context;
private Realm realm;
public RealmBackup(Context context) {
this.realm = Realm.getInstance(Realm.getDefaultConfiguration());
this.context = context;
}
public void backup() {
File exportRealmFile = null;
File exportRealmPATH = context.getExternalFilesDir(null);
String exportRealmFileName = "default.realm";
// create a backup file
exportRealmFile = new File(exportRealmPATH, exportRealmFileName);
// if backup file already exists, delete it
exportRealmFile.delete();
// copy current realm to backup file
realm.writeCopyTo(exportRealmFile);
String msg = "File exported to Path: " + context.getExternalFilesDir(null);
realm.close();
}
public void restore() {
//Restore
File exportRealmPATH = context.getExternalFilesDir(null);
String FileName = "default.realm";
String restoreFilePath = context.getExternalFilesDir(null) + "/" + FileName;
copyBundledRealmFile(restoreFilePath, FileName);
}
private void copyBundledRealmFile(String oldFilePath, String outFileName) {
try {
File file = new File(context.getFilesDir(), outFileName);
FileOutputStream outputStream = new FileOutputStream(file);
FileInputStream inputStream = new FileInputStream(new File(oldFilePath));
byte[] buf = new byte[1024];
int bytesRead;
while ((bytesRead = inputStream.read(buf)) > 0) {
outputStream.write(buf, 0, bytesRead);
}
outputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
private String dbPath() {
return realm.getPath();
}
}
And whenever I'm trying to backup/restore the data I'm simply calling realmBackup.backup() or restore. The problem, I believe is that the realm must be reopened after the restore occured with the new data, however, I'm unsure how to do that, I've tried using whether
Realm.init(this);
RealmConfiguration config = new RealmConfiguration
.Builder()
.deleteRealmIfMigrationNeeded()
.build();
Realm.setDefaultConfiguration(config);
or simply realm = Realm.getDefaultInstance(); but that didn't quite work.

You can't just delete and overwrite an open Realm as "restore", you must ensure that Realm.getGlobalInstanceCount(Realm.getDefaultConfiguration()) == 0 first.
To do that, you must call realm.close() on the instances that are opened.
Also, if the app was updated with a new schema, your backup would be deleted by deleteIfMigrationNeeded, so look out for that

Clean the project, uninstall and reinstall the app.

Related

new BlobStoreManager read write on Android 11

I previously used external storage to store specific data that I would like to share between my applications (without having any contentprovider "host")
File folder = new File(Environment.getExternalStorageDirectory(), "FOLDER_NAME");
File file = new File(folder, "FILE_NAME.dat");
FileOutputStream outputStream = new FileOutputStream(file);
That is why I am trying to use BlobStoreManager, as suggested in google's recommendation for targeting 30 (https://developer.android.com/training/data-storage/shared/datasets)
The read & write are based on a BlobHandle with 4 parameters, one being MessageDigest based on a "content". BlobHandle must use the same 4 parameters, or read will fail (SecurityException).
I managed to write data, and to read it, but it makes no sense:
It seems that in order to write, I need to use the data I want to write to generate the BlobHandle.
Then, to read, as BlobHandle must use the same 4 parameters, I also need the data I wrote to be able to read.
Totally illogic, as I wanted to read this data, I don't have it!
I must miss something or just do not understand how it work. If someone can help :)
Here are my sample:
If I set the following:
createBlobHandle: content = "mydata"
write: data = "mydata"
Then write will success, and read will success too. But it I can not know the value before reading it in a normal usecase :(
If I set the following (which would be logic, at least to me):
createBlobHandle: content = "somekey"
write: data = "mydata"
Then write will fail :(
#RequiresApi(api = Build.VERSION_CODES.R)
private BlobHandle createBlobHandle() {
//Transfer object
String content = "SomeContentToWrite";
String label = "label123";
String tag = "test";
//Sha256 summary of the transmission object
try {
byte[] contentByte = content.getBytes("utf-8");
MessageDigest md = MessageDigest.getInstance("sha256");
byte[] contentHash = md.digest(contentByte);
return BlobHandle.createWithSha256(contentHash, label,0, tag);
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
return null;
}
private void write() {
String data = "SomeContentToWrite";
#SuppressLint("WrongConstant") final BlobStoreManager blobStoreManager = ((BlobStoreManager) applicationContext.getSystemService(Context.BLOB_STORE_SERVICE));
//Generate the session of this operation
try {
BlobHandle blobHandle = createBlobHandle();
if (blobHandle == null)
return;
long sessionId = blobStoreManager.createSession(blobHandle);
try (BlobStoreManager.Session session = blobStoreManager.openSession(sessionId)) {
try (OutputStream pfd = new ParcelFileDescriptor.AutoCloseOutputStream(session.openWrite(0, data.getBytes().length))) {
//The abstract of the written object must be consistent with the above, otherwise it will report SecurityException
Log.d(TAG, "writeFile: >>>>>>>>>>text = " + data);
pfd.write(data.getBytes());
pfd.flush();
//Allow public access
session.allowPublicAccess();
session.commit(applicationContext.getMainExecutor(), new Consumer<Integer>() {
#Override
public void accept(Integer integer) {
//0 success 1 failure
Log.d(TAG, "accept: >>>>>>>>" + integer);
}
});
}
}
} catch (IOException e) {
e.printStackTrace();
}
}
private String read() {
String data = "";
#SuppressLint("WrongConstant") final BlobStoreManager blobStoreManager = ((BlobStoreManager) applicationContext.getSystemService(Context.BLOB_STORE_SERVICE));
BlobHandle blobHandle = createBlobHandle();
if (blobHandle != null) {
try (InputStream pfd = new ParcelFileDescriptor.AutoCloseInputStream(blobStoreManager.openBlob(createBlobHandle()))) {
//Read data
byte[] buffer = new byte[pfd.available()];
pfd.read(buffer);
String text = new String(buffer, Charset.forName("UTF-8"));
Log.d(TAG, "readFile: >>>>>>>>>>>>>>>>>>>>" + text);
} catch (IOException e) {
e.printStackTrace();
} catch (SecurityException e) {
e.printStackTrace();
}
}
return data;
}
According to the official training documentation linked in the question, the missing piece of information, at the time of the question having been asked, is that the four pieces of data contained in the BlobHandler need to be uploaded to a server owned by the client application then subsequently downloaded by which ever other application wants to access the blob via the BlobStorageManager.
So it would seem that on-device blob discovery is not supported. There could also be a solution possible using a Content Provider which could offer up the four required pieces of data, thus circumventing the need for the server infrastructure.

Saving objects in cache

I am trying to save in cache response from server for certain time.
There are tne next data for saving in cache: I have a List<ProgrammeItem> which I am getting from server. While user is working, he can download up to ~230 List<ProgrammeItem> (but it is unreal to reach this, estimated is 10-50).
ProgrammeItem oblect including strings, int, int[].
That is how I am saving and getting the last downloaded List<ProgrammeItem>:
//saving / getting Programme items
public boolean saveObject(List<ProgrammeItem> obj) {
final File suspend_f=new File(android.os.Environment.getExternalStorageDirectory(), "test");
FileOutputStream fos = null;
ObjectOutputStream oos = null;
boolean keep = true;
try {
fos = new FileOutputStream(suspend_f);
oos = new ObjectOutputStream(fos);
oos.writeObject(obj);
} catch (Exception e) {
keep = false;
Log.e("catching exception ", "" + e.getMessage() + ";;;" + e);
} finally {
try {
if (oos != null) oos.close();
if (fos != null) fos.close();
if (keep == false) suspend_f.delete();
} catch (Exception e) { /* do nothing */ }
}
return keep;
}
public List<ProgrammeItem> getObject(Context c) {
final File suspend_f=new File(android.os.Environment.getExternalStorageDirectory(), "test");
List<ProgrammeItem> simpleClass= null;
FileInputStream fis = null;
ObjectInputStream is = null;
try {
fis = new FileInputStream(suspend_f);
is = new ObjectInputStream(fis);
simpleClass = (List<ProgrammeItem>) is.readObject();
} catch(Exception e) {
String val= e.getMessage();
} finally {
try {
if (fis != null) fis.close();
if (is != null) is.close();
} catch (Exception e) { }
}
return simpleClass;
}
That is how I am saving and getting object in activity:
PI = new ProgrammeItem();
List<ProgrammeItem> programmeItems = new ArrayList<>();
...
//filling programmeItems with data from server
...
boolean result = PI.saveObject(programmeItems); //Save object
ProgrammeItem m = new ProgrammeItem();
List<ProgrammeItem> c = m.getObject(getApplicationContext()); //Get object
The question is: how can I save a lot of my objects instead of only one?
I think I should done something like public boolean addObjectsInCache(List<ProgrammeItem> obj) for adding objects, not overriding them.
And change get method into public List<ProgrammeItem> getObject(Context c, String id), where id will be unique identifier, which will includes into every ProgrammeItem in the every List<ProgrammeItem>.
Am I right? And how I can achieve this? Maybe you will show me the other way to work with objects and cache?
You can use SharedPreference instead, while having a local database Android Room can also be an option. SharedPreference basically is stored in your device's cache while the local database is stored in your device's data hence in our apps we have clear cache and clear data function.
Additional Resources:
StackOverFlow: How Android SharedPreferences save/store object
Object based preference library: https://github.com/ShawnLin013/PreferencesManager I would suggest you go with this one, since it can easily save you time saving list based object and retrieving them. You can also add more to the persisted list object when needed.
Secured Preferences: https://github.com/scottyab/secure-preferences
An option could be to use Room database with inMemoryDatabaseBuilder:
db = Room.inMemoryDatabaseBuilder(context, ProgrammeDatabase::class.java)
.build()
if it all can fit in memory.

How to avoid from always loading cached app data from Google Drive

Currently, I'm using Google Drive Android API, to store my Android app data, to Google Drive App Folder.
This is what I'm doing when saving my application data
Generate a checksum for the current local zip file.
Search in Google Drive App Folder, to see whether there is an existing App Folder zip file.
If there is, overwrite the content of existing App Folder zip file, with current local zip files. Also, we will rename existing App Folder zip filename, with the latest checksum.
If there isn't existing App Folder zip file, generate a new App Folder zip file, with local zip file's content. We will use the latest checksum as App Folder zip filename.
Here's the code which performs the above-mentioned operations.
Generate new App Folder zip file, or update existing App Folder zip file
public static boolean saveToGoogleDrive(GoogleApiClient googleApiClient, File file, HandleStatusable h, PublishProgressable p) {
// Should we new or replace?
GoogleCloudFile googleCloudFile = searchFromGoogleDrive(googleApiClient, h, p);
try {
p.publishProgress(JStockApplication.instance().getString(R.string.uploading));
final long checksum = org.yccheok.jstock.gui.Utils.getChecksum(file);
final long date = new Date().getTime();
final int version = org.yccheok.jstock.gui.Utils.getCloudFileVersionID();
final String title = getGoogleDriveTitle(checksum, date, version);
DriveContents driveContents;
DriveFile driveFile = null;
if (googleCloudFile == null) {
DriveApi.DriveContentsResult driveContentsResult = Drive.DriveApi.newDriveContents(googleApiClient).await();
if (driveContentsResult == null) {
return false;
}
Status status = driveContentsResult.getStatus();
if (!status.isSuccess()) {
h.handleStatus(status);
return false;
}
driveContents = driveContentsResult.getDriveContents();
} else {
driveFile = googleCloudFile.metadata.getDriveId().asDriveFile();
DriveApi.DriveContentsResult driveContentsResult = driveFile.open(googleApiClient, DriveFile.MODE_WRITE_ONLY, null).await();
if (driveContentsResult == null) {
return false;
}
Status status = driveContentsResult.getStatus();
if (!status.isSuccess()) {
h.handleStatus(status);
return false;
}
driveContents = driveContentsResult.getDriveContents();
}
OutputStream outputStream = driveContents.getOutputStream();
InputStream inputStream = null;
byte[] buf = new byte[8192];
try {
inputStream = new FileInputStream(file);
int c;
while ((c = inputStream.read(buf, 0, buf.length)) > 0) {
outputStream.write(buf, 0, c);
}
} catch (IOException e) {
Log.e(TAG, "", e);
return false;
} finally {
org.yccheok.jstock.file.Utils.close(outputStream);
org.yccheok.jstock.file.Utils.close(inputStream);
}
if (googleCloudFile == null) {
// Create the metadata for the new file including title and MIME
// type.
MetadataChangeSet metadataChangeSet = new MetadataChangeSet.Builder()
.setTitle(title)
.setMimeType("application/zip").build();
DriveFolder driveFolder = Drive.DriveApi.getAppFolder(googleApiClient);
DriveFolder.DriveFileResult driveFileResult = driveFolder.createFile(googleApiClient, metadataChangeSet, driveContents).await();
if (driveFileResult == null) {
return false;
}
Status status = driveFileResult.getStatus();
if (!status.isSuccess()) {
h.handleStatus(status);
return false;
}
} else {
MetadataChangeSet metadataChangeSet = new MetadataChangeSet.Builder()
.setTitle(title).build();
DriveResource.MetadataResult metadataResult = driveFile.updateMetadata(googleApiClient, metadataChangeSet).await();
Status status = metadataResult.getStatus();
if (!status.isSuccess()) {
h.handleStatus(status);
return false;
}
}
Status status;
try {
status = driveContents.commit(googleApiClient, null).await();
} catch (java.lang.IllegalStateException e) {
// java.lang.IllegalStateException: DriveContents already closed.
Log.e(TAG, "", e);
return false;
}
if (!status.isSuccess()) {
h.handleStatus(status);
return false;
}
status = Drive.DriveApi.requestSync(googleApiClient).await();
if (!status.isSuccess()) {
// Sync request rate limit exceeded.
//
//h.handleStatus(status);
//return false;
}
return true;
} finally {
if (googleCloudFile != null) {
googleCloudFile.metadataBuffer.release();
}
}
}
Search for existing App Folder zip file
private static String getGoogleDriveTitle(long checksum, long date, int version) {
return "jstock-" + org.yccheok.jstock.gui.Utils.getJStockUUID() + "-checksum=" + checksum + "-date=" + date + "-version=" + version + ".zip";
}
// https://stackoverflow.com/questions/1360113/is-java-regex-thread-safe
private static final Pattern googleDocTitlePattern = Pattern.compile("jstock-" + org.yccheok.jstock.gui.Utils.getJStockUUID() + "-checksum=([0-9]+)-date=([0-9]+)-version=([0-9]+)\\.zip", Pattern.CASE_INSENSITIVE);
private static GoogleCloudFile searchFromGoogleDrive(GoogleApiClient googleApiClient, HandleStatusable h, PublishProgressable p) {
DriveFolder driveFolder = Drive.DriveApi.getAppFolder(googleApiClient);
// https://stackoverflow.com/questions/34705929/filters-ownedbyme-doesnt-work-in-drive-api-for-android-but-works-correctly-i
final String titleName = ("jstock-" + org.yccheok.jstock.gui.Utils.getJStockUUID() + "-checksum=");
Query query = new Query.Builder()
.addFilter(Filters.and(
Filters.contains(SearchableField.TITLE, titleName),
Filters.eq(SearchableField.TRASHED, false)
))
.build();
DriveApi.MetadataBufferResult metadataBufferResult = driveFolder.queryChildren(googleApiClient, query).await();
if (metadataBufferResult == null) {
return null;
}
Status status = metadataBufferResult.getStatus();
if (!status.isSuccess()) {
h.handleStatus(status);
return null;
}
MetadataBuffer metadataBuffer = null;
boolean needToReleaseMetadataBuffer = true;
try {
metadataBuffer = metadataBufferResult.getMetadataBuffer();
if (metadataBuffer != null ) {
long checksum = 0;
long date = 0;
int version = 0;
Metadata metadata = null;
for (Metadata md : metadataBuffer) {
if (p.isCancelled()) {
return null;
}
if (md == null || !md.isDataValid()) {
continue;
}
final String title = md.getTitle();
// Retrieve checksum, date and version information from filename.
final Matcher matcher = googleDocTitlePattern.matcher(title);
String _checksum = null;
String _date = null;
String _version = null;
if (matcher.find()){
if (matcher.groupCount() == 3) {
_checksum = matcher.group(1);
_date = matcher.group(2);
_version = matcher.group(3);
}
}
if (_checksum == null || _date == null || _version == null) {
continue;
}
try {
checksum = Long.parseLong(_checksum);
date = Long.parseLong(_date);
version = Integer.parseInt(_version);
} catch (NumberFormatException ex) {
Log.e(TAG, "", ex);
continue;
}
metadata = md;
break;
} // for
if (metadata != null) {
// Caller will be responsible to release the resource. If release too early,
// metadata will not readable.
needToReleaseMetadataBuffer = false;
return GoogleCloudFile.newInstance(metadataBuffer, metadata, checksum, date, version);
}
} // if
} finally {
if (needToReleaseMetadataBuffer) {
if (metadataBuffer != null) {
metadataBuffer.release();
}
}
}
return null;
}
The problem occurs, during loading application data. Imagine the following operations
Upload zip data to Google Drive App Folder for the first time. The checksum is 12345. The filename being used is ...checksum=12345...zip
Search for zip data from Google Drive App Folder. Able to find the file with filename ...checksum=12345...zip. Download the content. Verify the checksum of content is 12345 too.
Overwrite new zip data to existing Google Drive App Folder file. New zip data checksum is 67890. The existing app folder zip file is renamed to ...checksum=67890...zip
Search for zip data from Google Drive App Folder. Able to find the file with filename ...checksum=67890...zip. However, after downloading the content, the checksum of the content is still old 12345!
Download App Folder zip file
public static CloudFile loadFromGoogleDrive(GoogleApiClient googleApiClient, HandleStatusable h, PublishProgressable p) {
final java.io.File directory = JStockApplication.instance().getExternalCacheDir();
if (directory == null) {
org.yccheok.jstock.gui.Utils.showLongToast(R.string.unable_to_access_external_storage);
return null;
}
Status status = Drive.DriveApi.requestSync(googleApiClient).await();
if (!status.isSuccess()) {
// Sync request rate limit exceeded.
//
//h.handleStatus(status);
//return null;
}
GoogleCloudFile googleCloudFile = searchFromGoogleDrive(googleApiClient, h, p);
if (googleCloudFile == null) {
return null;
}
try {
DriveFile driveFile = googleCloudFile.metadata.getDriveId().asDriveFile();
DriveApi.DriveContentsResult driveContentsResult = driveFile.open(googleApiClient, DriveFile.MODE_READ_ONLY, null).await();
if (driveContentsResult == null) {
return null;
}
status = driveContentsResult.getStatus();
if (!status.isSuccess()) {
h.handleStatus(status);
return null;
}
final long checksum = googleCloudFile.checksum;
final long date = googleCloudFile.date;
final int version = googleCloudFile.version;
p.publishProgress(JStockApplication.instance().getString(R.string.downloading));
final DriveContents driveContents = driveContentsResult.getDriveContents();
InputStream inputStream = null;
java.io.File outputFile = null;
OutputStream outputStream = null;
try {
inputStream = driveContents.getInputStream();
outputFile = java.io.File.createTempFile(org.yccheok.jstock.gui.Utils.getJStockUUID(), ".zip", directory);
outputFile.deleteOnExit();
outputStream = new FileOutputStream(outputFile);
int read = 0;
byte[] bytes = new byte[1024];
while ((read = inputStream.read(bytes)) != -1) {
outputStream.write(bytes, 0, read);
}
} catch (IOException ex) {
Log.e(TAG, "", ex);
} finally {
org.yccheok.jstock.file.Utils.close(outputStream);
org.yccheok.jstock.file.Utils.close(inputStream);
driveContents.discard(googleApiClient);
}
if (outputFile == null) {
return null;
}
return CloudFile.newInstance(outputFile, checksum, date, version);
} finally {
googleCloudFile.metadataBuffer.release();
}
}
First, I thought
Status status = Drive.DriveApi.requestSync(googleApiClient).await()
doesn't do the job well. It fails in most of the situation, with error message Sync request rate limit exceeded. In fact, the hard limit imposed in requestSync, make that API not particularly useful - Android Google Play / Drive Api
However, even when requestSync success, loadFromGoogleDrive still can only get the latest filename, but outdated checksum content.
I'm 100% sure loadFromGoogleDrive is returning me a cached data content, with the following observations.
I install a DownloadProgressListener in driveFile.open, bytesDownloaded is 0 and bytesExpected is -1.
If I use Google Drive Rest API, with the following desktop code, I can find the latest filename with correct checksum content.
If I uninstall my Android app and re-install again, loadFromGoogleDrive will able to get the latest filename with correct checksum content.
Is there any robust way, to avoid from always loading cached app data from Google Drive?
I manage to produce a demo. Here are the steps to reproduce this problem.
Step 1: Download source code
https://github.com/yccheok/google-drive-bug
Step 2 : Setup in API console
Step 3: Press button SAVE "123.TXT" WITH CONTENT "123"
A file with filename "123.TXT", content "123" will create in the app folder.
Step 4: Press button SAVE "456.TXT" WITH CONTENT "456"
The previous file will be renamed to "456.TXT", with content updated to "456"
Step 5: Press button LOAD LAST SAVED FILE
File with filename "456.TXT" was found, but the previous cached content "123" is read. I was expecting content "456".
Take note that, if we
Uninstall demo app.
Re-install demo app.
Press button LOAD LAST SAVED FILE, file with filename "456.TXT" and content "456" is found.
I had submitted issues report officially - https://code.google.com/a/google.com/p/apps-api-issues/issues/detail?id=4727
Other info
This is how it looks like under my device - http://youtu.be/kuIHoi4A1c0
I realise, not all users will hit with this problem. For instance, I had tested with another Nexus 6, Google Play Services 9.4.52 (440-127739847). The problem doesn't appear.
I had compiled an APK for testing purpose - https://github.com/yccheok/google-drive-bug/releases/download/1.0/demo.apk
Search on Google Drive is slow.
Why not use properties of the base folder to store id of the zip file?
https://developers.google.com/drive/v2/web/properties
File names on Google Drive are not unique, you can upload multiple files with same names. The File ID returned by Google, however, is unique.

How to correctly use pre populated Realm database in Android?

I'm on Realm 0.90.0 and I'm having problems with loading a pre populated Realm db: once I load the file from the source, isEmpty on the returned collection gives true, when it obviously isn't (I created it by importing a csv in Realm Browser and everything seems fine when opening the db in there).
This is what I did so far:
I put a realm db file in my res/raw folder
I loaded it in MyApplication class by doing
copyBundledRealmFile(this.getResources().openRawResource(R.raw.station), Realm.DEFAULT_REALM_NAME); // more info about this is said later
RealmConfiguration config = new RealmConfiguration.Builder(this)
.deleteRealmIfMigrationNeeded()
.build();
Realm.deleteRealm(config);
Realm.setDefaultConfiguration(config);
Then in my Fragment's onViewCreated I queried for every object in it
Realm realm = Realm.getDefaultInstance();
RealmResults<Station4Database> stations = realm.where(Station4Database.class).findAll();
Log.d("TAG", "stations loading ended",
stations.isLoaded(),// true
stations.isValid(), // true
stations.isEmpty(), // true instead of FALSE
stations.size()); // 0 instead of 2929 (actual size)
Why doesn't it get the objects right? Any help / input would be immensely appreciated!
Thanks!
PS this is the function I took from the official example in Realm's repo (should make the job right).
private String copyBundledRealmFile(InputStream inputStream, String outFileName) {
try {
File file = new File(this.getFilesDir(), outFileName);
FileOutputStream outputStream = new FileOutputStream(file);
byte[] buf = new byte[1024];
int bytesRead;
while ((bytesRead = inputStream.read(buf)) > 0) {
outputStream.write(buf, 0, bytesRead);
}
outputStream.close();
return file.getAbsolutePath();
} catch (IOException e) {
e.printStackTrace();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (java.io.IOException e) {
e.printStackTrace();
}
return null;
}
Finally solved it!
The problem was that I was using a non-perfectly-mapped version of the model class for the objects in the database (Station4Database in this specific case, the field names were not exactly the same!).
Therefore, remember kids, always use the model definitions generated by Realm Browser if you don't wanna incur in Realm retrieving empty collections without giving any kind of error / exception back to you.

save sqlite database android after application overwrite

My question is that I want to restore my application's sqlite database after I overwrite the same app on my device. I don't like to add settings again and again on start up of my app.
So is it possible in android to save the database somewhere from which I can again restore it ?
I have searched for more than hours on Google and SO but couldnt ind any solution.
EDIT: Its not a fixed database. So I can't store it in Assets Folder. It is editable by user but by default it should carry the last edited values(values before the app overwrite).
This method I find very helpful:
public static void movedb(File srcdb, File destdb)
{
try
{
if (Environment.getExternalStorageDirectory().canWrite())
{
if (srcdb.exists())
{
FileChannel src = new FileInputStream(srcdb).getChannel();
FileChannel dst = new FileOutputStream(destdb).getChannel();
dst.transferFrom(src, 0, src.size());
src.close();
dst.close();
}
else
{
//ERROR: "Database file references are incorrect"
}
}
else
{
//ERROR: "Cannot write to file"
}
}
catch (Exception e)
{
//ERROR: e.getMessage()
}
}
Then I just call
movedb(this, new File(<context>.getDatabasePath("...your DB name...")), new File("... your location ..."));
To back up, and then to restore:
movedb(this, new File("... your location ..."), new File(<context>.getDatabasePath("...your DB name...")));
I'm using ORMLite and, apart from storing the database in the external public directory, after I restore the file to the database directory, I have to re-instantiate the DatabaseHelper singleton and create a new one.
Here is my version, omitting every try/catch block for the sake of simplicitiy:
public boolean restoreBackup(Context context){
String databasePath = "data/data/my.package.name/databases/myDatabase.sqlite";
String backUpPath = context.getDatabaseDir("myDatabase.sqlite");
// Copies back-up to database directory
new File(databasePath).delete();
FileInputStream streemToBackUp = new FileInputStream(new File(backUpPath));
OutputStream streamToDatabaseFile = new FileOutputStream(databasePath);
byte[] buffer = new byte[1024];
int length;
while ((length = streamToBackUp.read(buffer)) > 0) {
streamToDatabaseFile.write(buffer, 0, length);
}
streamToDatabaseFile.flush();
streamToDatabaseFile.close();
streamToBackUp.close();
// Re-instantiate DatabasHelper singleton
DatabaseHelper.closeHelper();
}
The body of closeHelper() is as follows:
public static void closeHelper() {
helper.close();
}
#Override
public void close() {
super.close();
myDao = null; // Set to null every day you have
helper = null; // Set to null the singleton instance of the helper
}
This will work as long as you don't use OpenHelperManager class to instantiate the helper, and always use getHelper() whenever you need the database instead of storing the instance returned.

Categories

Resources