T_Dun
T_Dun

Reputation: 101

Reusing snapshots of Realm databases

I have a Realm database being populated with data a number of times each second. I am viewing the database as plotted MPChartLib data. My goal is to snap a picture of the database at a particular point in time and reuse that information a bit later.

I am trying a couple of approaches and have reached a stalemate in both. My first approach is to have a second Realm database that I would populate with the information in the first database on a button click. My second approach is to export the database to a file and then reopen it later.

In my first approach I build two databases in the onCreateView as shown below. I'm using mRealm as the dynamically changing Realm and mSRealm as the static one that only changes on button click.

  // Building a Realm instance un-persisted in memory (ie not on disk)
    RealmConfiguration config1 = new RealmConfiguration.Builder(getActivity())
            .name("myDynamic.realm")
            .inMemory()
            .build();
    // Building a Realm instance on disk)
    RealmConfiguration config2 = new RealmConfiguration.Builder(getActivity())
            .name("myStatic.realm")
            .build();
    Log.i(TAG, "delete previous");
    Realm.deleteRealm(config2);
    Realm.deleteRealm(config1); // Clean slate
    Log.i(TAG, "set default configuration");
    Realm.setDefaultConfiguration(config1); // Make this Realm the default
    Log.i(TAG, "define an instance for this");
    mRealm = buildDatabase(config1);
    mSRealm = buildDatabase(config2);

    Log.i(TAG, "build a Listener for database changes");
    realmListener = new RealmChangeListener() {
        @Override
        public void onChange() {
            //        Log.i(TAG, "database changed");
            if (startTime != 0) {
                //        Log.i(TAG, "off to plot");
                setData();
                mChart.invalidate();
            }
        }
    };

    Log.i(TAG, "add Listener  ");
    mRealm.addChangeListener(realmListener);

And the buildDatabase code for each configuration

  public Realm buildDatabase(RealmConfiguration config){

    try {
        Log.i(TAG, "return database since there was not already one ");
        return Realm.getInstance(config);
    } catch (RealmMigrationNeededException e){
        try {
            Log.i(TAG, "deleted database since there was one");
            Realm.deleteRealm(config);
            Log.i(TAG, "return new database since there was one deleted");
            return Realm.getInstance(config);
        } catch (Exception ex){
            Log.i(TAG, "should not ever get here");
            throw ex;
        }
    }
}

When the mRealm database has completed it's transaction I test to see if the recordData flag has been set to true via button push. if so, then I export the database as a file and /or try to update the static database snapshot to the current state of the dynamic one.

   //    Log.i(TAG, "copy element to the database ");
        mRealm.copyToRealm(entry);
        //        Log.i(TAG, " database  has size =  " + results.size());
        mRealm.commitTransaction();

        if (recordData) {
            Log.i(TAG, "record copy to file ");
            exportDatabase(mRealm);
            Log.i(TAG, "record copy to static Database ");
            copyToStaticDatabase(mRealm);
        //    Log.i(TAG, "record to singleton ");
        //    updateDataLab();
            recordData = !recordData;
        }

To export the data to a file I'm using:

    public void exportDatabase(Realm mRealm ) {
    Log.i(TAG, "into export the database to a file ");
    File exportRealmFile = null;
    try {
        // get or create an "export.realm" file
        Log.i(TAG, "get or create file ");
        exportRealmFile = new File(getActivity().getExternalCacheDir(), "export.realm");

        // if "export.realm" already exists, delete
        exportRealmFile.delete();

        // copy current realm to "export.realm"
        try {
            Log.i(TAG, "write copy to file ");
            mRealm.writeCopyTo(exportRealmFile);
        } catch (java.io.IOException e) {
            e.printStackTrace();
        }
    }catch (IOException e) {
            e.printStackTrace();
        }

To copy to the static base I'm using:

  public void copyToStaticDatabase(Realm mRealm ){

    Log.i(TAG, "get query of current state ");
    RealmResults<DataEntry> result2 = mRealm.where(DataEntry.class).findAllSorted("timestamp");
    Log.i(TAG, "delete old and create new static database ");
    Realm mSRealm = buildDatabase(config2);
    Log.i(TAG, "begin repopulating static database ");
    mSRealm.beginTransaction();

    for (int i = 0; i < result2.size(); i++) {
        DataEntry entry = mSRealm.createObject(DataEntry.class);
        entry.setX(result2.get(i).getX());
        entry.setY(result2.get(i).getY());
        entry.setZ(result2.get(i).getZ());
        entry.setTimestamp(result2.get(i).getTimestamp());
        entry.setAccuracy(result2.get(i).getAccuracy());
        entry.setsTimestamp(result2.get(i).getsTimestamp());

        mRealm.copyToRealm(entry);
    }
    Log.i(TAG, "finish static database ");
    mSRealm.commitTransaction();
}

This code dies at the point of creating a new instance of mSRealm since the "begin populating etc" never gets displayed in the log. Not sure what I'm doing wrong there.

 Log.i(TAG, "delete old and create new static database ");
    Realm mSRealm = buildDatabase(config2);
    Log.i(TAG, "begin repopulating static database ");

I would like to go to a new fragment and use the static Database. In the onCreateView in the new fragment I try to create a configuration that points to the static Realm:

    // Building a Realm instance on disk
    RealmConfiguration config = new RealmConfiguration.Builder(getActivity())
            .name("myStatic.realm")
            .build();
    Log.i(TAG, "keep previous");
//    Realm.deleteRealm(config); // Clean slate
    Log.i(TAG, "set default configuration");
    Realm.setDefaultConfiguration(config); // Make this Realm the default
    Log.i(TAG, "define an instance for this");
    mRealm = Realm.getDefaultInstance();

I can't test this however since my code stops at my attempted copy. For the file approach I've attempted to work through the MigrationExampleActivity that I've rewritten as fragment and included it in my project to test how migrating is performed through version updates. This will become more important as it will be important to archive the snapshots for long term future reference.

I think this is the way to "reload" a database from a file from what I've read on other questions. My code again crashes at the point below of getting a new Instance.

@Override
public View onCreateView(LayoutInflater inflater, final ViewGroup container,
                         Bundle savedInstanceState) {

    Log.i(TAG, " in onCreate  View ");
    View view = inflater.inflate(R.layout.activity_realm_basic_example, container, false);


    rootLayout = ((LinearLayout) view.findViewById(R.id.container));
    rootLayout.removeAllViews();
    Log.i(TAG, " get resources of previous database  ");

    copyBundledRealmFile(this.getResources().openRawResource(R.raw.default1), "default1");

    Log.i(TAG, " set up new configuration ");
    RealmConfiguration config1 = new RealmConfiguration.Builder(getActivity())
            .name("default1")
            .schemaVersion(3)
            .migration(new Migration())
            .build();
    Log.i(TAG, " get new instance ");
    realm = Realm.getInstance(config1); // Automatically run migration if needed
    Log.i(TAG, " got new instance ");
    showStatus("Default1");
    showStatus(realm);
    realm.close();

    return view;
}

I've tried to mimic the structure of realmjavamaster in my project but perhaps I have an issue with setting up the raw directory. I'm not very clear on how to utilize "resident" realms and how to reference re-opening them , getting the correct path and such.

Could be my problem is in the referencing or perhaps I don't know enough details in getting new Instances. Maybe I'm missing something fundamental.

Any assistance is greatly appreciated.

Upvotes: 0

Views: 642

Answers (1)

T_Dun
T_Dun

Reputation: 101

Ok, so I spent some time working through the basics of Realm Databases and now am all set with using multiple realms within one fragment, saving and retrieving snapshot static realms of my actively varying realm.

My question was based on a somewhat fundamental lack of understanding of the definition of schema for the model classes and ways to migrate to new versions as changes are made to those model classes. Along with some basic misunderstandings of how and where realms are stored.

I have also discovered a huge benefit of using a realm database as a method to pass data between fragments. I now have a database called "GlobalVariables" which I can read and write to/from within any fragment in my project. No more fragment args for me! or passing data between fragments by using "hosting activities".

I just open an instance of GlobalVariables anywhere I want data and done! In addition, I can store variations of the GlobalVariables as records to allow for custom setups for a particular operation type on my app.

Thanks beender, cmelchior and Realm, bravo...

Upvotes: 1

Related Questions