Friday, July 22, 2011

Frustrations With Data Access on Android + Intro to OrmLite

Working in the .Net world during the day and doing some Android development in my spare time has a tendency to bias my opinion against Java. Don't get me wrong, I'll take Java over C/C++/Objective-C any day, but the lack of lambdas and delegates can get frustrating when you are spoiled by .Net's recent trend towards functional programming. This really becomes noticeable at the data access layer of Android applications where the API's for interacting with the onboard SQLite database can best be described as adequate.

Thankfully API problems, unlike core system architecture problems, are easily overcome by open source software and your fellow developers experiencing the same problems as you. 

After having just about enough inline SQL to drive me crazy I decided to investigate what Object Relational Mappers (ORM) are available for the Android OS. Since Android is based on its own implementation of the Java Virtual Machine there are a lot of Java applicable tools out there. 

The first and most popular you may run across when looking around is Hibernate. Quite possibly one of the older and more popular open source ORM's available, it was originally built for larger Java applications than a simple Android app. 

Another common one you will find is OrmLite. OrmLite is designed to be much more lightweight and smaller scale than Hibernate. This means that it does sacrifice some of the power that Hibernate has, but instead has simplicity and very small Jar files to add to your project.

Because of this I have arbitrarily started exploring OrmLite as my choice for Android development. Later on I may decide to go with Hibernate, but for now its OrmLite. 

So lets say you have run into this same problem and have decided that you too are sick of endless Cursor manipulation and are ready to spend more time writing Android apps and less time praying that the rawQuery statement you just wrote is valid SQL syntax.
  1. The first thing you need to do is add the Jar's to your project. Go to the OrmLite website and download the ormlite-core and ormlite-android jar files. 
  2. Add these to your project. Assuming you are using Eclipse, right click on the project name and choose Properties. Then under Java Build Path->Libraries choose Add External JARs... and select the two Jar's that you just downloaded.
  3. First you want to create the model class that you are going to be working with. OrmLite relies on Annotations to make sense of your class. Use the @DatabaseTable annotation to mark your class as a table, and the @DatabaseField annotations to mark your properties as fields. Here is an example of a super simple data class. In it you can see that I set the id field to be the primary key for the table:

    import com.j256.ormlite.table.DatabaseTable;
    import com.j256.ormlite.field.DatabaseField;
    
    @DatabaseTable(tableName="data")
    public class Data {
     
     @DatabaseField(id = true)
     private long id;
     @DatabaseField
     private String value;
     
     public Data() {
      
     }
     
     public Data(long id, String value) {
      this.id = id;
      this.value = value;
     }
     
     public long getId() {
      return this.id;
     }
     
     public String getValue() {
      return this.value;
     }
    }
    
  4. Now you need to create your database helper class with the verbosely named OrmLiteSqliteOpenHelper. If you have created SqliteOpenHelper classes before, this should feel very similar. Setup your constructor and override the onCreate and onUpgrade methods as you would a normal SqliteOpenHelper. One important note is that OrmLite relies on Data Access Objects to act as an intermediary layer on top of your database. This is what abstracts away the details you didn't want to deal with when you decided to go the ORM route. Because of this, it is recommended to give yourself a public method that returns yourself a Dao object specific to the type you are working with. The Dao is a generic class, where the first type is the type of the model you want to work with and the second type is the type of the property you marked as the id in your model. In my example I used a long for the id so I create a Dao<Data, Long>, but if you hadn't set an id property you should be able to use the Object type. Example class:

    import com.j256.ormlite.android.apptools.OrmLiteSqliteOpenHelper;
    import com.j256.ormlite.dao.Dao;
    import com.j256.ormlite.support.ConnectionSource;
    import com.j256.ormlite.table.TableUtils;
    
    public class DatabaseHelper extends OrmLiteSqliteOpenHelper {
    
     private static final String DATABASE_NAME = "test.db";
     private static final int DATABASE_VERSION = 1;
     
     private Dao mDao;
     
     public DatabaseHelper(Context context) {
         super(context, DATABASE_NAME, null, DATABASE_VERSION);  
         mDao = null;
     }
     
     @Override
     public void onCreate(SQLiteDatabase db, ConnectionSource connectionSource) {
         try {
             TableUtils.createTable(connectionSource, Data.class);
         } catch(SQLException e) {
             Log.e(DatabaseHelper.class.getName(), "Can't create database.");
             throw new RuntimeException(e);
         }
     }
     
     @Override
     public void onUpgrade(SQLiteDatabase db, ConnectionSource connectionSource, int oldVersion, int newVersion) {
         try {
             TableUtils.dropTable(connectionSource, Data.class, true);
      
             onCreate(db, connectionSource);
         } catch(SQLException e) {
              Log.e(DatabaseHelper.class.getName(), "Can't drop databases.");
             throw new RuntimeException(e);
         }
     }
     
     public Dao getDataDao() throws SQLException {
      if (mDao == null) {
       mDao = getDao(Data.class);
       
      }
      
      return mDao;
     }
     
    }
    
  5. The easiest way to start using your DatabaseHelper from your Activity is to create an OrmLiteBaseActivity. This gives you a few added methods to simplify things for you. For example it gives you the getHelper() method that returns an instance of the DatabaseHelper you wanted to work with. Since you went ahead and created a public method to return yourself and instance of the Dao you also now have an easy way to get at a Dao for doing your data manipulation. Example class:

    import java.sql.SQLException;
    import java.util.List;
    
    import com.j256.ormlite.android.apptools.OrmLiteBaseActivity;
    import com.j256.ormlite.dao.Dao;
    import android.os.Bundle;
    import android.widget.TextView;
    
    public class Main extends OrmLiteBaseActivity {
     
        /** Called when the activity is first created. */
        @Override
        public void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            try {
         Dao dao = getHelper().getDataDao();
                Data d = new Data(1, "Hello World");  
         dao.create(d);
     } catch (SQLException e) {
         e.printStackTrace();
     }
      
     try {
         Dao laterDao = getHelper().getDataDao();
         List result = laterDao.queryForMatching(new Data(1, "Hello World"));
                TextView tv = new TextView(this);
         tv.setText(result.get(0).getValue());
         setContentView(tv);
     } catch (SQLException e) {
         e.printStackTrace();
     }  
            
        }
        
    }
    

Friday, March 25, 2011

Marketplace Statistics

Google took another positive step forward with their Marketplace recently. As a developer they have finally released statistics that let you track the number of devices that your app is installed on, what type of devices, and what countries those devices are located in. You can also see a graph showing the number of installs over time. 

So here are a few interesting, and unexpected stats that I have learned from mine:
  • While the vast majority of FastShopper users are in the US, the second largest country is Taiwan. 
  • Android 2.1 and 2.2 are the dominant OS versions. Over 99% of my users are on those two versions.
  • The Motorola X is the single device with the most installs representing 10.5% of all installs.
  • There are more tablet users than I expected. 5% of installs are on the Galaxy Tablet. 
  • English is the primary language for only about 60% of my users. Knowing this now I'm going to be doing more work to ensure that my apps work across multiple languages. 
  • The total number of installs goes up then slowly trails down after each release. My guess is because a large number of users frequently download new apps from the "Just In" category to try out but very few end up liking and sticking with these applications.

Saturday, February 26, 2011

Code Assist Slow in Eclipse for Android Development

This one has been bugging me for awhile but I finally had enough. Because I'm spoiled on content assist and Visual Studio Intellisense I go about most of my normal Android development relying heavily on Eclipse to find the methods I am looking for and auto-complete them. Unfortunately if you are running Eclipse 3.6 or below, even on the newest ADT version the content assist is unbearably slow.

Thankfully it seems they are coming out with a fix for this in Eclipse 3.7. Since most of us are impatient and don't like waiting for fixes, one kind developer out there backported the fix into Eclipse 3.6.:
You can replace your <ECLIPSE_HOME>/plugins/ 
org.eclipse.jdt.core_3.6.1.v_A68_R36x.jar plugin with one from 
http://adt-addons.googlecode.com/svn/patches/org.eclipse.jdt.core_3.6... 
and restart Eclipse. Content Assists will be much better. Just try it. 
Don't forget backup your original plugins. 

I have verified that this is working, and unlike other fixes which require you to download the entire Android source for each version you are working in, you can have this up and running in under a minute.

Wednesday, February 23, 2011

Android 3.0 SDK - I'm Tired of The Emulator

Anyone who has done any Android development knows that the emulator is the weak point of all Android development tools. On the upside it runs true ARM code, so what is ran on the device really is what is ran in the emulator unlike iPhone development. However, this interpreter is horrendously slow and with the improvements to Android 3.0, it feels like it just got a heck of a lot slower.

I downloaded the 3.0 SDK last night, anxiously ready to try out the new features in this tablet focused OS. Unfortunately, what I got was about 10 minutes just to boot the emulator, and once I got in it I lost out completely on the new experience because opening anything took absurdly long. All the animations where unreasonably choppy. I'm honestly unsure how Google was able to develop any of the "built in" apps on this given how slow it is.

As the company who revolutionized Javascript performance, I expect a little more when it comes to software speed.

Saturday, February 5, 2011

'unicode' object has no attribute '_meta'

It appears I have run across another annoying problem for which there is a lot of contradicting information. I'm working on a Django project and I ran into this error: 'unicode' object has no attribute '_meta'. What is really important is the context in which I received the error. There are a lot of different possible culprits and an even larger number of possible fixes.

The context within I was receiving this error was in a view function that was "supposed" to be returning a list of strings in json. If you have any experience with returning Django queryset results in json you probably immediately think of doing something like this:

#Not what you want!
from django.core import serializers 
from django.http import HttpResponse
from myproject.myapp.models import MyModel

def get_my_strings(request):
    some_models = MyModel.objects.filter(user=request.user)
    my_strings = []
    for s in my_strings:
        my_strings.append(some_models.some_string)
    #error will be thrown
    my_strings = serializers.serialize('json', my_strings) 
    return HttpResponse(my_strings, mimetype='application/json')

This is wrong.

What you actually want is to use the simplejson.dumps function to serialize your data.  Here is the correct form:

#Import simplejson instead
from django.utils import simplejson 
from django.http import HttpResponse
from myproject.myapp.models import MyModel

def get_my_strings(request):
    some_models = MyModel.objects.filter(user=request.user)
    my_strings = []
    for s in my_strings:
        my_strings.append(some_models.some_string)
    #Corrected!
    my_strings = simplejson.dumps(my_strings, indent=4) 
    return HttpResponse(my_strings, mimetype='application/json'