Transcript Lecture 8a

Persistent
Storage using
Perst Lite
in Android
Introduction
 Android
by default uses SQLLite to handle data
storage
 SQLLite



is a relational database style storage
Requires using SQL based concepts to query for data
Require mapping from raw data to object fields
Not very object-oriented
Perst Lite
 Perst
Lite is an Object-Oriented Database
Management System (OODBMS) with a small
memory footprint meant for mobile devices
 A from



scratch implementation of persistence
Working directly with objects not columns and tables
Implements Persistence by reachability
High-speed indexed lookups and Query-by-Example
 Open-source
and usable for free on any noncommercial project
Dependencies
 In
order for the Perst classes to be available to
you, you must include the PerstAndroid.jar in your
Build Path and Export it


This was pre-built for use in this course
Generally, you need to build it yourself
 This
will apply to any third party library you need
included in your application
Dependencies
 To







set the build path
Copy the PerstAndroid.jar to the root of your project
Right click the project folder
Select Build Path -> Configure Build Path
Select Libraries Tab
Add JAR -> select your PerstAndroid.jar
Select Order/Export Tab
Check the PerstAndroid.jar
 PerstAndroid.jar
should now appear in the
Referenced Libraries node of the project
Permissions
Permissions
 Android
requires sensitive operations to be
marked with permissions




File access
Network access
SMS access
Etc
 Permissions
 If
are attached to the Android Manifest
permissions are not proper placed, APIs
needing them will fail
Example
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package=“admu.edu.cs119"
android:versionCode="1"
android:versionName="1.0">
…
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"></uses-permission>
<uses-permission android:name="android.permission.SEND_SMS"></uses-permission>
<uses-permission android:name="android.permission.RECEIVE_SMS"></uses-permission>
<uses-permission android:name="android.permission.INTERNET"></uses-permission>
</manifest>

Specific APIs will list which permissions are required by them in order
to run

These can be added in the XML or via the GUI
Perst permissions
The
following permissions are required
in order for Perst to run

android.permission.WRITE_EXTERNAL_STORAGE
Application
Management
Application Management
 Perst
will create a file in the SD card to
represent its database
 This
file will remain in place even after the
application has been shut off and use this
when it starts again

i.e. the data will persist between app starts
 In
order to “reset” the application state to its
initial stat, you will need to clear application
data
Example

When viewing the application
you have the option to empty
out any previously stored data

Hit Clear data
PerstLite
PerstLite
 PerstLite
has several concepts that need to be
understood to be able to use it
 Storage
 Root Object
 Persistent classes
 Reachability
 Index
Storage
Storage

The Storage interface provides the methods for accessing
the object database

This abstracts how the DB is implemented
 SD card, etc.
A Storage instance is created using a factory method
// get instance of the storage
Storage db = StorageFactory.getInstance().createStorage();

// open the database once you have storage instance
db.open(databasePath, 40*1024); // min 40k
Defining the database path
String databasePath = "test.dbs";
try
{
// MODE_APPEND is needed or else the file will auto-delete
openFileOutput(databasePath, Context.MODE_APPEND).close();
}
catch(Exception e)
{
throw new RuntimeException(e);
}
databasePath = getFileStreamPath(databasePath).getAbsolutePath();
System.out.println("Initializing: "+databasePath);
// open the database
db.open(databasePath, 40*1024);

The default
Android file
system is readonly

You will need to
find the actual
path to the db file
by first opening it
and then getting
the absolute path
to it
Opening the database
 The
database file is opened using the open(String
name, int pagesize)
 The name corresponds to the filename of the db
file (you can actually see this file in the /appdb
folder)
 The pagesize is how much memory to allocate
to Perst
 The minimum size is 40*1024 bytes
 This is used as its internal cache
Closing the Database

Perst keeps data in memory as much as possible to
prevent unnecessary saves to the file system which is
significantly slower

However, premature termination of the app can prevent
changes from being saved

In order to close the DB correctly, you must invoke close()
on the Storage

If the incorrectly closed, Perst will try to recover what it can on the
next startup.
Example
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
Storage db = StorageFactory.getInstance().createStorage();
String databasePath = getAbsolutePath("test.dbs");
db.open(databasePath, 40*1024);
// open the database
MyRootClass root = (MyRootClass)db.getRoot();
// check if a root object is present in this file
if (root == null) {
root = new MyRootClass();
// create root object
root.setText("I am new text");
db.setRoot(root);
TextView tf = (TextView) findViewById(R.id.text);
tf.setText("Saving root");
} else {
TextView tf = (TextView) findViewById(R.id.text);
tf.setText(root.getText());
}
db.close();
}
Example getAbsolutePath()
private String getAbsolutePath(String databasePath)
{
try
{
// MODE_APPEND is needed or else the file will auto-delete
openFileOutput(databasePath, Context.MODE_APPEND).close();
}
catch(Exception e)
{
throw new RuntimeException(e);
}
databasePath = getFileStreamPath(databasePath).getAbsolutePath();
return databasePath;
}
NOTE: openFileOutput() and
getFileStreamPath() are methods of Activity
Saving objects
Root object

In order to be able to save anything to the Storage you
must first have a root object attached to it
 Which must be an IPersistent subtype

Through this reference, all other IPersistent data (if any)
can be retrieved. For example:
 Some applications need only a single set of data to be
saved
 Others need several lists of data and a way to pull these
lists out by name
Registering the Root object

When initializing a Storage file you must first invoke
getRoot() to check if a root has already been attached
 If the method returns null, no root is attached

You must then instantiate an IPersistent type and assign
this using the setRoot(IPersistent)
Example
MyRootClass root = (MyRootClass)db.getRoot();
// get storage root
if (root == null) {
// Root is not yet defined: storage is not initialized
root = new MyRootClass(); // create root object
db.setRoot(root); // register root object
}
Styles of Root objects
Simple
Storage
Not
Single
Persistent
Object
Could be a simple Persistent
subclass with nothing but
primitives and Strings
so simple
Storage
Persistent
Object
Contains fields
or collections of
Persistent
objects
fields
Persistent
Object
Persistent
Object
Collection
Persistent
Object
Styles of Root objects
Complex
Storage
Index
Index (see
later) acts as a
dynamic lookup table
for other objects
Persistent
Object
Persistent
Object
Persistent
Object
IPersistent/Persistent

The IPersistent interface contains many methods. Most of
these are not directly used by the developer but are used
by Perst itself

The usual way to make a class work with Perst is to make
the class you are saving extend the Persistent class



org.garret.perst.IPersistent and org.garret.perst.Persistent
This supplies all the IPersistent methods needed by the Perst
In cases where extending Persistent is not possible
 You will need to open the source code of Persistent and copy
them to your class manually and make your class implement the
IPersistent interface
Example
public class MyRootClass extends Persistent
{
private String text;
public String getText() {
return text;
}
public void setText(String text) {
this.text = text;
}
}
Persistence-style
There
are two ways to persist data in
Perst
 Explictly
 By
Reachability
Explicit Persistent and Storage

When using explicit persistence, a persistent object must
be associated to a Storage object so that it can be saved
e.g. Via constructor
Appointment(Strorage db)
{
super(db)
}


super constructor should pass this db to the parent
A Persistent object can then be saved by calling the
object’s store() method
Example
import org.garret.perst.*;
public class Appointment extends
Persistent
{
private Date time;
private int length;
private String location;
private String subject;
Appointment()
{
}
Appointment(Strorage db)
{
super(db)
}
…

For explicit style, you need
to have 2 constructors
 One blank
 One which takes a
Storage object
Problem with explicit persistence
Storage
Persistent
Object
Contains fields
or collections of
Persistent
objects
 The
fields
Persistent
Object
Persistent
Object
Collection
Persistent
Object
problem with explicit persistence is the fact is
becomes cumbersome to save a tree of objects
 You need to manually store each object in the
graph front the top down
Persistence by Reachability




Instead of manually calling
store() on an object, you can
call the Storage commit()
In order for your new object to
be saved, however, a path to
the root object must be present
(i.e. It is reachable)
NewPersistent1 will be saved
on commit() but not
NewPersistent2
commit() is automatically called
on a Storage close()
Storage
Root
Persistent
Object
contains
addElement()
Vector
New
persistent1
New
persistent2
Fields and Collections
 Your
root object can contain IPersistent type
fields or collections like Lists and HashMaps

Collections can be automatically saved as long
as their contents are all IPersistent types
 Fields
that are not meant to be persisted
should be marked with the Java keyword
transient
Editing existing Persistent objects

Once a root object is saved and retrieved in subsequent
runs of your program, you can edit the root’s values
 Including any internal objects
 You can manually save individual objects using their
store() method if a Storage is already associated to it
Editing existing Persistent objects

However, when using Storage.commit() Perst will only
update objects that have their modify() method called
 This will flag the object for saving (dirty)
 Perst will not resave everything, it only saves objects
that have been marked as dirty
 For performance purposes (re-saving all objects is
slow)
Design considerations
When
using Persistence by reachability
you are relieved of the burden of
manually saving an object graph
However,
you are now responsible for
marking changes using modify() in the
changed objects in the graph
Design considerations
 Where
you place the modify() is something
you need to consider
you can modify() at the UI level when you press
a button
 You can modify() at the object level
 The object itself calls its own modify() when
certain fields are changed, requires placing
modify() in all methods of the object causing
change
