[ < ] [ > ]   [ << ] [ Up ] [ >> ]         [Top] [Contents] [Index] [ ? ]

4. Using With Android


[ < ] [ > ]   [ << ] [ Up ] [ >> ]         [Top] [Contents] [Index] [ ? ]

4.1 Android Basics

Because of the lack of official support for JDBC in Android OS, ORMLite makes direct calls to the Android database APIs to access SQLite databases. You should make sure that you have downloaded and are depending on the ormlite-core.jar and ormlite-android.jar files, but not the ormlite-jdbc.jar version. Although a number of developers are using the package in their projects, we continue to improve how ORMLite integrates with the Android classes. Feedback on this would be welcome.

After you have read the getting started section (see section Getting Started), the following instructions should be followed to help you get ORMLite working under Android OS.

  1. You will need to create your own database helper class which should extend the OrmLiteSqliteOpenHelper class. This class creates and upgrades the database when your application is installed and can also provide the DAO classes used by your other classes. Your helper class must implement the methods onCreate(SQLiteDatabase sqliteDatabase, ConnectionSource connectionSource) and onUpgrade(SQLiteDatabase database, ConnectionSource connectionSource, int oldVersion, int newVersion). onCreate creates the database when your app is first installed while onUpgrade handles the upgrading of the database tables when you upgrade your app to a new version. There is a sample DatabaseHelper class as well as example projects online.
  2. The helper can be kept open across all activities in your app with the same SQLite database connection reused by all threads. If you open multiple connections to the same database, stale data and unexpected results may occur. We recommend using the OpenHelperManager to monitor the usage of the helper – it will create it on the first access, track each time a part of your code is using it, and then it will close the last time the helper is released.
  3. Once you have defined your database helper and are managing it correctly, you will need to use it in your Activity classes. An easy way to use the OpenHelperManager is to extend OrmLiteBaseActivity for each of your activity classes – there is also OrmLiteBaseListActivity, OrmLiteBaseService, and OrmLiteBaseTabActivity. These classes provide a helper protected field and a getHelper() method to access the database helper whenever it is needed and will automatically create the helper in the onCreate() method and release it in the onDestroy() method. See the sample HelloAndroid activity class in the examples. See section Android Examples.
  4. If you do not want to extend the OrmLiteBaseActivity and other base classes then you will need to duplicate their functionality. You will need to call OpenHelperManager.getHelper(Context context, Class openHelperClass) at the start of your code, save the helper and use it as much as you want, and then call OpenHelperManager.release() when you are done with it. You will probably want to have something like the following in your classes:
     
    private DatabaseHelper databaseHelper = null;
    
    @Override
    protected void onDestroy() {
        super.onDestroy();
        if (databaseHelper != null) {
            OpenHelperManager.releaseHelper();
            databaseHelper = null;
        }
    }
    
    private DBHelper getHelper() {
        if (databaseHelper == null) {
            databaseHelper =
                OpenHelperManager.getHelper(this, DatabaseHelper.class);
        }
        return databaseHelper;
    }
    

  5. By default, if you are using OrmLiteBaseActivity or other base classes, the OpenHelperManager will detect the database helper class through reflection. Another way to wire in the appropriate database helper class is to set the full class name of it in the the open_helper_classname value defined in the res/values/strings.xml resource file . You can also set the class using the OpenHelperManager.setOpenHelperClass(Class) method in a static {} block in your code.
  6. The Android native SQLite database type is SqliteAndroidDatabaseType and is used by the base classes internally.
  7. WARNING: You must make sure that any background threads call the OpenHelperManager.getHelper() and release() methods appropriately. Otherwise, if they access the database before it is opened or after it is closed, you will get exceptions.

Please see the example code documentation for more information. See section Android Examples. Again, feedback on this is welcome.


[ < ] [ > ]   [ << ] [ Up ] [ >> ]         [Top] [Contents] [Index] [ ? ]

4.2 Using Table Config File

For some time we have been struggling with DAO startup issues under Android that we thought were due to ORMLite object bandwidth. Although improvements and DAO caching has been made, creating a couple of DAOs when your application starts can still take too long and generate far too much garbage collection activity. Turns out that one of the major culprits is some ugly code down in the Android OS – especially in Method.equals(). Because annotations use this method, looking up annotation values is extremely expensive, often garbage collecting thousands of objects and megabytes of space. Android knows about the issues and a fix has been made but we have no idea when these performance improvements will make it into an Android release.

We have made a couple of changes in ORMLite to help work around this issue. Firstly, we added some reflection hacks to work around these problems in the short run. Annotations using this mechanism run 20 times faster than with the native Android calls.

With a little bit of work (and some caveats), you can remove all annotation work from your application and make DAO creation an extremely fast operation. ORMLite supports the loading of the data configurations from a text configuration file. When a DAO is created, these configurations will be used, removing the need for any annotation method calls entirely.

  1. The OrmLiteConfigUtil utility class writes a ormlite_config.txt configuration file in the raw resource folder res/raw/ormlite_config.txt. You will need to extend this class into your own project along side your DatabaseHelper class. It should look something like:
     
    public class DatabaseConfigUtil extends OrmLiteConfigUtil {
      public static void main(String[] args) throws Exception {
        writeConfigFile("ormlite_config.txt");
      }
    }
    
  2. You will need to run this utility locally on your development box (not in an Android device), whenever you make a change to one of your data classes. This means that right now, this must be done by hand to keep the configuration file in sync with your database classes. To run the utility you will need to use the local Java runtime environment (JRE). Under eclipse, edit the "Run Configuration" for the utility, select the JRE tab, and select an alternative JRE (1.5 or 1.6). Your project’s JRE should be undefined since it is an Android application. You’ll also need to remove the Android bootstrap entry from the Classpath tab.
  3. By default this utility will look in the current directory and below in files ending in .java for the existence of one of the @DatabaseTable or DatabaseField annotations. These classes will be investigated and written into the database configuration file. You can also list the classes to be processed:
     
    public class DatabaseConfigUtil extends OrmLiteConfigUtil {
      private static final Class<?>[] classes = new Class[] {
        SimpleData.class,
      };
      public static void main(String[] args) throws Exception {
        writeConfigFile("ormlite_config.txt", classes);
      }
    }
    
  4. When the utility is run it should create the ormlite_config.txt configuration file in the raw resource folder. This folder must exist before the utility is run. Afterwards, if you refresh your project your should see the file appear. In the Eclipse console, you should see something like the following outputted by the utility:
     
    Writing configurations to /HelloAndroid/./res/raw/ormlite_config.txt
    Wrote config for class com.example.helloandroid.SimpleData
    Done.
    

    The config file generated should look something like:

     
    #
    # generated on 2011/09/15 01:42:02
    #
    # --table-start--
    dataClass=com.example.helloandroid.SimpleData
    tableName=simpledata
    # --table-fields-start--
    # --field-start--
    fieldName=id
    canBeNull=true
    generatedId=true
    …
    
  5. The first time you create the config file in the resource folder, the Android build plugin should add it to the R.java file inside of the gen folder. This defines a unique integer value so that the application can open this resource by file-id number. The file should contain something like:
     
    public final class R {
      …
      public static final class raw {
        public static final int ormlite_config=0x7f040000;
      }
      …
    }
    
  6. After the R.java file entry has been generated, you will need to enable the reading of the file at runtime. Inside of your DatabaseHelper class, you will need to change the constructor to add the integer file-id. The constructor will look something like the following:
     
    public DatabaseHelper(Context context) {
      super(context, DATABASE_NAME, null, DATABASE_VERSION,
        R.raw.ormlite_config);
    }
    

    Notice the R.raw.ormlite_config entry at the end that passes the file-id to the super class so it can be read in. You can also pass in a file-name or a Java File if you want to load in the config file from another location.

  7. When you build and run your application, you will know that the database configuration file is being loaded if you see log entries like the following:
     
    I/DaoManager(  999): Loaded configuration for class ...SimpleData
    

    Oh, and if it is working you should notice a significant lack of GC messages during DAO startup.

As of 9/2011, this is one of the newest parts of ORMLite so we most likely will be improving it. Feedback is welcome.


[ < ] [ > ]   [ << ] [ Up ] [ >> ]         [Top] [Contents] [Index] [ ? ]

4.3 Android Logging

The ormlite-android.jar classes define the AndroidLog class which is the Android specific version of ORMLite logging. This class makes calls to the Log.d, Log.i, … methods in the Android API. To see the log output, you will need to use the adb utility to view the log output:

 
adb logcat

Since INFO is the default under Android, only message such as the following will be spit out by default:

 
I/TableUtils(  254): creating table 'simpledata'
I/TableUtils(  254): creating index 'simpledata_string_idx' for table
   'simpledata
I/TableUtils(  254): executed create table statement changed 1 rows:
   CREATE TABLE `simpledata` (`date` VARCHAR, `id` INTEGER PRIMARY
   KEY AUTOINCREMENT , `even` SMALLINT )
I/TableUtils(  254): executed create table statement changed 1 rows:
   CREATE INDEX `simpledata_string_idx` ON `simpledata` ( `string` )

To enable more debug information you will want to do something like the following to turn on logging for a particular class:

 
adb shell setprop log.tag.StatementExecutor VERBOSE
adb shell setprop log.tag.BaseMappedStatement VERBOSE
adb shell setprop log.tag.MappedCreate VERBOSE

This enables messages such as:

 
D/BaseMappedStatement(465): create object using 'INSERT INTO `simpledata`
   (`date` ,`string` ,`millis` ,`even` ) VALUES (?,?,?,?)' and 4 args,
   changed 1 rows
D/BaseMappedStatement(465): assigned id '9' from keyholder to 'id' in
   SimpleData object

To enable all debug messages for all ORMLite classes then use the following:

 
adb shell setprop log.tag.ORMLite DEBUG

NOTE: Unfortunately, Android property names are limited in size so the ORMLite logger only takes that last 23 [sic] characters of the class name if it is larger than 23 characters. For example, if the class is AndroidDatabaseConnection you would do:

 
adb shell setprop log.tag.droidDatabaseConnection VERBOSE

If you are trying to track operations performed to the database by ORMLite use:

 
adb shell setprop log.tag.droidDatabaseConnection VERBOSE
adb shell setprop log.tag.ndroidCompiledStatement VERBOSE

[ < ] [ > ]   [ << ] [ Up ] [ >> ]         [Top] [Contents] [Index] [ ? ]

4.4 Runtime Versus SQL Exceptions

By default, most of the DAO methods throw SQLException which is the default internal exception for most JDBC and other SQL calls. But in Android-land, especially, most of the exceptions extend RuntimeException so having to put a lot of ignored try ... catch stanzas is inconvenient. For this reason we’ve added a RuntimeExceptionDao which wraps all calls to the underlying DAO to rethrow the SQL exceptions as runtime exceptions. To get one, you can wrap your own down in it:

 
Dao<Account, String> dao =
  DaoManager.createDao(connectionSource, Account.class);
RuntimeExceptionDao<Account, String> accountDao =
  new RuntimeExceptionDao<Account, String>(dao);

Or you can call the createDao helper methods on RuntimeExceptionDao:

 
RuntimeExceptionDao<Account, String> accountDao =
  RuntimeExceptionDao.createDao(connectionSource, Account.class);

Other classes such as TableUtils and QueryBuilder still throw SQLException but the hope is that RuntimeExceptionDao helps a little bit at least.


[ < ] [ > ]   [ << ] [ Up ] [ >> ]         [Top] [Contents] [Index] [ ? ]

4.5 Upgrading Your Schema

When you upgrade your application, you may have to add columns or make other changes to the data that was stored by previous versions of your application. If you are on Android then in your DatabaseHelper, there should be an onUpgrade() method that extends the following method from the OrmLiteSqliteOpenHelper.

 
abstract void onUpgrade(SQLiteDatabase database,
  ConnectionSource connectionSource, int oldVersion, int newVersion)

In that method you can use your DAO to perform any tweaks to the schema:

 
Dao<Account, Integer> dao = getHelper().getAccountDao();
// change the table to add a new column named "age" 
dao.executeRaw("ALTER TABLE `account` ADD COLUMN age INTEGER;");

Here’s more information about SQLite’s ALTER TABLE. In SQLite, all you can do is rename a table name and add a new column. You can’t rename or remove a column or change the constraints. Remember that SQLite is typeless so changing the type of a column doesn’t matter.

Most likely, you should make your schema changes conditional to the version you are upgrading from:

 
if (oldVersion < 2) {
  // we added the age column in version 2
  dao.executeRaw("ALTER TABLE `account` ADD COLUMN age INTEGER;");
}
if (oldVersion < 3) {
  // we added the weight column in version 3
  dao.executeRaw("ALTER TABLE `account` ADD COLUMN weight INTEGER;");
}

You can also modify data in the tables using something like the following:

 
dao.executeRaw(
    "ALTER TABLE `account` ADD COLUMN hasDog BOOLEAN DEFAULT 0;");
dao.updateRaw("UPDATE `account` SET hasDog = 1 WHERE dogCount > 0;");

If you are using some other database over JDBC then the above commands will work but you will have to handle the versioning of your application manually.


[ << ] [ >> ]           [Top] [Contents] [Index] [ ? ]

This document was generated by Gray Watson on December 16, 2013 using texi2html 1.82.