In my project I am have LoginFragment(which accepts user name and password) PinFrgment which Accepts a PIN and Confirms that PIN and saves it in Realm.
When the user confirms the PIN I am creating a realmconfiguration and getting the realm instance as shown below.
protected void initRealm(Context context, byte[] key) {
try {
Realm.init(context);
RealmConfiguration config = new RealmConfiguration.Builder().deleteRealmIfMigrationNeeded().encryptionKey(key).build();
Realm.setDefaultConfiguration(config);
realmInstance = Realm.getDefaultInstance();
DoPayApplication.getInstance().setRealmException(false);
} catch (RealmFileException realmFileException) {
DoPayApplication.getInstance().setRealmException(true);
Log.d(TAG, realmFileException.toString());
}catch (Exception ex){
Log.i(TAG, ex.toString());
DoPayApplication.getInstance().setRealmException(true);
}
}
On logout and forgot PIN I am calling the following method to close/reset realm.
public static void closeRealm(Context context) {
try{
if (realmInstance != null && !realmInstance.isClosed()) {
realmInstance.close();
RealmConfiguration config = realmInstance.getConfiguration();
realmInstance.deleteRealm(config);
}
}catch (Exception e){
Log.d(TAG, e.getMessage());
}
}
As long as I am in the app, on logout I am able to closeRealm as realmInstance is not null.
If the app is closed and user comes back to it. On forgot PIn when I call closeRealm 'realmInstance' is null and I cannot get the realmconfiguration to delete.
As the PIN is forgotten and the encryptionkey can not be generated. Is there a way to delete/recreate the real file without the Key.
Any advice would be very helpful.
Thanks
R
You can just delete .realm file with native tools:
// realm file default name is default.ream
File file = new File(context.getFilesDir(), "realmFileName.realm");
file.delete();
In that way you can copy db dump from assets or raw resources, also without realm configurations:
//Load dump from assets folder
InputStream inputStream = application.getAssets().open("my_factory_data_db.realm");
// realm file default name is default.ream
File file = new File(context.getFilesDir(), "realmFileName.realm");
FileOutputStream outputStream = new FileOutputStream(file);
byte[] buf = new byte[1024];
int bytesRead;
while ((bytesRead = inputStream.read(buf)) > 0) {
outputStream.write(buf, 0, bytesRead);
}
ApiPreferenceHelper.setVersionApi(application, BuildConfig.BUILD_TIMESTAMP);
inputStream.close();
outputStream.close();
You can lear more about realm db file here How to find my realm file?
Related
I am developing an app where time-cost of an algorithm matters a lot.
In the algorithm, I need to get path string to a file in assets folder. And I got answer from this question.
The file is a configuration file, which is ~400 bytes in size. The library I used requires path, but not some Java string.
My code is like:
public static File getCacheFile(String path, Context context) throws IOException {
File cacheFile = new File(context.getCacheDir(), path);
try {
InputStream inputStream = context.getAssets().open(path);
try {
FileOutputStream outputStream = new FileOutputStream(cacheFile);
try {
byte[] buf = new byte[1024];
int len;
while ((len = inputStream.read(buf)) > 0) {
outputStream.write(buf, 0, len);
}
} finally {
outputStream.close();
}
} finally {
inputStream.close();
}
} catch (IOException e) {
throw new IOException("Could not open file", e);
}
return cacheFile;
}
If I run the algorithm for the first time since I start my app, it will cost ~900ms.
If I run the algorithm again without restarting the app, it will cost ~400ms.
So I guess the time difference is that this function attempts to load the file into cache and read path from the cache? Maybe the file is already in the cache and that is why it is faster.
Is there any way to make it faster? E.g. preload this file to cache in onCreate(), maybe?
Edit: I tried to preload this file in onCreate() and it does not work.
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
try {
getCacheFile("a.properties", getApplicationContext());
} catch (IOException e) {
e.printStackTrace();
}
}
Edit2: Not sure whether it matters, but my algorithm is posted here.
I have a situation where i prompt user whether to backup their messages or no with the google backup service. I am successfully backing up the current realm database onto the SD card from where the backup to google service will happen.
But yet i though about the scenario where i want to actually restore the database from SD card to the current Realm Instance,meaning replace the current realm file with the one which is available on the SD Card.
In the older versions i saw that Realm gave a way to specify a custom path from which realm would read the database file,but in this new one i see none of it.
Any help please?
CREATE BACKUP FILE
public void backup() {
try {
// create a backup file
File exportRealmFile;
exportRealmFile = new File(EXPORT_REALM_PATH, EXPORT_REALM_FILE_NAME);
// if backup file already exists, delete it
exportRealmFile.delete();
// copy current realm to backup file
realm.writeCopyTo(exportRealmFile);
} catch (IOException e) {
e.printStackTrace();
}
realm.close();
}
RESTORE
private String copyBundledRealmFile(String oldFilePath, String outFileName) {
try {
File file = new File(activity.getApplicationContext().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();
return file.getAbsolutePath();
} catch (IOException e) {
e.printStackTrace();
}
return null;
}
Hope this help.
Credit to link
I'm relatively new to Android. I'm transferring a file from an Android Wear device to a phone, which I did through PutDataRequest. On the phone side I get a DataItemAsset which can provide me a file descriptor using Wearable.DataApi.getFdForAsset(). My question is how do I save this file to external storage?
Thank you!
Here's how I managed to upload a text file from an Android Wear watch to it's paired mobile phone. There may be a simpler way, but this is what worked for me.
(1) On the watch side, create a text file, and read it into an Asset which you can put through the DataApi:
public void SendTextFile()
{
// Get folder for output
File sdcard = Environment.getExternalStorageDirectory();
File dir = new File(sdcard.getAbsolutePath()+ "/MyAppFolder/");
if (!dir.exists()) {dir.mkdirs();} // Create folder if needed
final File file = new File(dir, "test.txt");
if (file.exists()) file.delete();
// Write a text file to external storage on the watch
try {
Date now = new Date();
long nTime = now.getTime();
FileOutputStream fOut = new FileOutputStream(file);
PrintStream ps = new PrintStream(fOut);
ps.println("Time = "+Long.toString(nTime)); // A value that changes each time
ps.close();
} catch (Exception e) {
}
// Read the text file into a byte array
FileInputStream fileInputStream = null;
byte[] bFile = new byte[(int) file.length()];
try {
fileInputStream = new FileInputStream(file);
fileInputStream.read(bFile);
fileInputStream.close();
} catch (Exception e) {
}
// Create an Asset from the byte array, and send it via the DataApi
Asset asset = Asset.createFromBytes(bFile);
PutDataMapRequest dataMap = PutDataMapRequest.create("/txt");
dataMap.getDataMap().putAsset("com.example.company.key.TXT", asset);
PutDataRequest request = dataMap.asPutDataRequest();
PendingResult<DataApi.DataItemResult> pendingResult = Wearable.DataApi
.putDataItem(mGoogleApiClient, request);
}
(2) On the mobile side, receive the asset and write it back out to a file:
public void onDataChanged(DataEventBuffer dataEvents) {
for (DataEvent event : dataEvents) {
if (event.getType() == DataEvent.TYPE_CHANGED &&
event.getDataItem().getUri().getPath().equals("/txt"))
{
// Get the Asset object
DataMapItem dataMapItem = DataMapItem.fromDataItem(event.getDataItem());
Asset asset = dataMapItem.getDataMap().getAsset("com.example.company.key.TXT");
ConnectionResult result =
mGoogleApiClient.blockingConnect(10000, TimeUnit.MILLISECONDS);
if (!result.isSuccess()) {return;}
// Convert asset into a file descriptor and block until it's ready
InputStream assetInputStream = Wearable.DataApi.getFdForAsset(
mGoogleApiClient, asset).await().getInputStream();
mGoogleApiClient.disconnect();
if (assetInputStream == null) { return; }
// Get folder for output
File sdcard = Environment.getExternalStorageDirectory();
File dir = new File(sdcard.getAbsolutePath() + "/MyAppFolder/");
if (!dir.exists()) { dir.mkdirs(); } // Create folder if needed
// Read data from the Asset and write it to a file on external storage
final File file = new File(dir, "test.txt");
try {
FileOutputStream fOut = new FileOutputStream(file);
int nRead;
byte[] data = new byte[16384];
while ((nRead = assetInputStream.read(data, 0, data.length)) != -1) {
fOut.write(data, 0, nRead);
}
fOut.flush();
fOut.close();
}
catch (Exception e)
{
}
// Rescan folder to make it appear
try {
String[] paths = new String[1];
paths[0] = file.getAbsolutePath();
MediaScannerConnection.scanFile(this, paths, null, null);
} catch (Exception e) {
}
}
}
}
You will also need to add the following permission to your manifests at both ends to write to external storage: android.permission.WRITE_EXTERNAL_STORAGE
Note: the most frustrating thing to watch out for is this: if the data does not change, no transfer will occur. So, when you're testing if you write the same data file contents twice, it will only come across the first time - even if you deleted the file from the first run. I lost quite a few hours to this insidious feature of the DataApi ! That's why my code above is writing the current time into the text file.
Also, of course make sure that you set up the GoogleApiClient object to connect, add listeners, etc as described here:
http://developer.android.com/training/wearables/data-layer/index.html
I am copying a database file from my assets folder to the databases folder on install. I have a shared preference named firstRun with a default value of true. If this is true then I copy the database file and set the firstRun value to false. Immediately following this process I then query a database table for some data. On an older Android version (2.1) an SQLite Exception occurs (no such table) and the application crashes, on Android 4.2.1 the dbHelper logs the same message but continues to run and returns the values from the table it just failed to find. With the earlier Android version, if I launch the application again, the database table is found and all operations proceed normally. After the application crashes I can inspect the copied database and the table and rows are present. This does seem to be different from other issues where tables genuinely don't exist as I can see that they do. I wonder if it's some kind of synchronisation issue where the table doesn't exist immediately after the copy process, but does at some point when a process has finished. To my knowledge this is not done asynchronously so I'm not sure why.
Here is some code to show the problem:
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(getBaseContext());
boolean firstRun = prefs.getBoolean(getString(R.string.first_time_run), true);
if (firstRun) {
SharedPreferences.Editor edit = prefs.edit();
edit.putBoolean(getString(R.string.first_time_run), Boolean.FALSE);
edit.commit();
try {
dbHelper.createDatabase();
} catch (IOException ioe) {
ioe.printStackTrace();
}
}
// This method will fire an exception the first time the app is run - no such table
Bundle metaData = dbHelper.fetchAppMetaData();
this.appTitle = metaData.getString("app_title");
this.normalId = Integer.parseInt(metaData.getString("normal_id"));
The fetchAppMetaData method is a basic sqlite.query:
Bundle metaData = new Bundle();
Cursor dbCursor = null;
SQLiteDatabase database = getReadableDatabase();
String[] cols = new String[] {"app_title", "normal_id"};
dbCursor = database.query(true, TBL_METADATA, cols, null, null, null, null, null, null);
if (dbCursor != null) {
dbCursor.moveToFirst();
which would eventually return a bundle.
The database creation method is:
//Open the local db as the input stream
InputStream dbFromAssets = myContext.getAssets().open(DB_NAME);
// Path to the just created empty db
String outFileName = DB_PATH + DB_NAME;
// Check that the directory now exists
File f = new File(DB_PATH);
if (!f.exists()) {
f.mkdir();
}
// Open the empty db as the output stream
OutputStream appDatabase = new FileOutputStream(outFileName);
// Transfer bytes from the inputfile to the outputfile
byte[] buffer = new byte[1024];
int length;
while ((length = dbFromAssets.read(buffer)) > 0){
appDatabase.write(buffer, 0, length);
}
// Close the streams - don't cross them!
appDatabase.flush();
appDatabase.close();
dbFromAssets.close();
Would be grateful for any ideas please.
Below is a cut and paste from working code. I use this on the launch of the MainActivity each time the application loads. Tested and working with versions 2.3 - 4.2:
Here is the code I'm using that does the check:
try
{
String destPath = "/data/data/" + getPackageName() + "/databases/(...your db...)";
File f = new File(destPath);
File c = new File("/data/data/" + getPackageName() + "/databases/");
// ---if directory doesn't exist then create it---
if (!c.exists())
{
c.mkdir();
}
// ---if db file doesn't exist then create it---
if (!f.exists())
{
CopyDB(getBaseContext().getAssets().open("...name of db from assets foleder..."), new FileOutputStream(destPath));
}
}
catch (FileNotFoundException e)
{
e.printStackTrace();
}
catch (IOException e)
{
e.printStackTrace();
}
Here is the code I'm using that does the copying if not found:
public void CopyDB(InputStream inputStream, OutputStream outputStream) throws IOException
{
//---copy 1K bytes at a time---
byte[] buffer = new byte[1024];
int length;
while ((length = inputStream.read(buffer)) > 0)
{
outputStream.write(buffer, 0, length);
}
inputStream.close();
outputStream.close();
}
Hope this is hopeful...
The solution for me was to close the dbHelper after the database is created and before I try to use it again.
For example:
try {
dbHelper.createDatabase();
} catch (Exception e) {
}
dbHelper.close();
dbHelper.fetchAppMetaData();
Hope this helps someone else.
I had created database in my android app, then inserted a statement. Everything worked, so i wanted to get my database fro MY_PACKAGE/databses/ and copy it to sd card to be reachable.
This worked, but when i trying to open with my sqlite Firefox plugin i get this error:
SQLiteManager: Error in opening file Datas.sqlite - either the file is encrypted or corrupt
Exception Name: NS_ERROR_FILE_CORRUPTED
Exception Message: Component returned failure code: 0x8052000b (NS_ERROR_FILE_CORRUPTED) [mozIStorageService.openUnsharedDatabase]
Maybe i have to open with something else or i can't open this so easily ?
I will give all the code i used:
Handling my db i used all this code:
Using your own SQLite database in Android applications
Copying it to sd card this method:
public static boolean backUpDataBase(Context context) {
final String DATABASE_NAME = "Data.sqlite";
final String DATABASE_NAME_FULL = "/data/data/package/databases/"
+ DATABASE_NAME;
boolean result = true;
// Source path in the application database folder
String appDbPath = DATABASE_NAME_FULL;
// Destination Path to the sdcard app folder
String sdFolder = Environment.getExternalStorageDirectory()
.getAbsolutePath() + "/" + "Datas.sqlite";
File f = new File(sdFolder);
// if (!f.exists()) {
// f.mkdir();
// }
InputStream myInput = null;
OutputStream myOutput = null;
try {
// Open your local db as the input stream
myInput = new FileInputStream(appDbPath);
// Open the empty db as the output stream
myOutput = new FileOutputStream(f);
// 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);
}
} catch (IOException e) {
result = false;
e.printStackTrace();
} finally {
try {
// Close the streams
if (myOutput != null) {
myOutput.flush();
myOutput.close();
}
if (myInput != null) {
myInput.close();
}
} catch (IOException e) {
}
}
return result;
}
My database looks like this:
2 tables:
CREATE TABLE "Test" ("_id" INTEGER PRIMARY KEY NOT NULL UNIQUE , "Info" TEXT)
CREATE TABLE "android_metadata" ("locale" TEXT DEFAULT 'en_US')
And code to do all i need:
//return databse which is read and write
DataBaseHelper dataBase= Main.createOrOpenDB(mContext);
Main.backUpDataBase(mContext);
db = dataBase.myDataBase;
// Step 1: Inflate layout
setContentView(R.layout.tabs_fragment_activity);
try{
db.execSQL("INSERT INTO " +"Test" +" Values ('1','Inserted');");
}catch (Exception e) {
// TODO: handle exception
e.printStackTrace();
}
So where is wrong, as insert works fine?
It sounds like there is a problem in your code to write it to the SD card (which I'm not seeing immediately).
I wonder, why are you copying it to the SDCard? It sounds like you merely want to check the file...
If that's actually your goal, then I would recommend running the emulator and simply using the DDMS view from eclipse, navigate to the file and click the button in the upper right corner whose tool-tip says "Pull a file from the device". What you get in the emulator should be exactly what you get on your phone.
try to use SQLiteOpenHelper | Android Developers