0

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.

T_Dun
  • 101
  • 1
  • 10
  • What is the exception message when crash? – beeender Mar 28 '16 at 02:20
  • Sorry, I missed your prompt question. Here is what I'm getting: java.lang.NullPointerException: Attempt to invoke virtual method 'io.realm.RealmObjectSchema io.realm.RealmObjectSchema.addRealmListField(java.lang.String, io.realm.RealmObjectSchema)' on a null object reference at com.tomphyx.android.accelwear1.database.Migration.migrate(Migration.java:98) – T_Dun Mar 30 '16 at 13:13
  • The above error is from my attempting to implement the MigrationExample. and is due to my lack of understanding about schema. How to define and the like. – T_Dun Mar 30 '16 at 13:39
  • When trying to run my own I get the following java.lang.RuntimeException: Could not dispatch event: class com.tomphyx.android.accelwear1.events.SensorUpdatedEvent to handler [EventHandler public void com.tomphyx.android.accelwear1.WearDataRecordFragment.onSensorUpdatedEvent(com.tomphyx.android.accelwear1.events.SensorUpdatedEvent)]: A non-null RealmConfiguration must be provided at com.squareup.otto.Bus.throwRuntimeException(Bus.java:458) This also looks like I have a null somewhere that I should have defined. – T_Dun Mar 30 '16 at 13:39

1 Answers1

1

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...

T_Dun
  • 101
  • 1
  • 10
  • Just curious, are you still using two Realms for snapshot? – beeender Apr 01 '16 at 03:15
  • In my Fragment DataEntryPlot, I can view the realm data base , DataEntry which is changing many times per second. Once I like the appearance of the data I hit the Snapshot button which does an Realm.writeCopyTo(exported.realm) Then, I reopen this file in Fragment MeasurePlot to do analysis on this static Realm. If I like that data I hit the MeasurePlot snapshot button which does a Realm.writeCopyTo(uniqueFilename) for archival storage of the measurement. Works well, like a "storage" type oscilloscope. Next I'm working on cloud / encrypted storage for file management – T_Dun Apr 02 '16 at 00:59
  • More directly, I have a actively varying realm in DataEntryPLot which I export as a tempoary file and reopen in MeasurePlot. Every export from DataEntryPlot overwrites the temp file. – T_Dun Apr 02 '16 at 01:03
  • I was thinking about keeping a `Version` object and link other objects to it in one single Realm file. But now i think your solution is much much easier for the scenario! – beeender Apr 03 '16 at 14:29