Static References == Memory leaks && onDestroy not called
2010-05-26 06:47:18
Java as we all know has garbage collection (GC) which means the programmer need not bother with explicitly deleting objects he/she creates. This is almost true, except that in order for GC to work the java engine has to see that there are no "live" references to the object.
For Android applications and their activities and services this basically boils down to one rule:
Don't keep static references to anything that is or refers to a Context.
To be absolutely sure we follow the above rule, we may adopt an even simpler one: only have static references to simple objects such as strings and integers.
Let's say you keep a static reference to an Activity, then you're not only preventing the memory it uses to be freed, you're also disrupting the "normal" life cycle of the activity. The activity can't be destroyed (i.e. onDestroy() won't be called) until all references to it are released.
In fact, if you start seeing your Activity behaving strangely (for instance as described in this previous post about saving persistent state) then you may have a static reference to your Activity somewhere. Note that it may be an indirect reference, e.g. some database helper or other object which was created in the context of the Activity.
Now if you want to get technical, you can also avoid the memory leaks by replacing your static references with corresponding weak references. If you're unfamiliar with them, here's a short refresher on weak references in Java. In the context of writing Android apps however it shouldn't be too difficult to avoid problematic static references altogether, weak or otherwise.
Posted by android 1
0 CommentsBackwards compatibility
2010-05-08 13:51:54
Ok, so finally time to take the bull by the horns (is that even an expression?). As I want my lovely apps full of all the latest stuff, but at the same time I don't want to deny all poor bastards with older android versions the pleasure of using said apps. Solution: eh, right. That's what I need to find out.
In fact the reason I decided to do this now is that I ran across a bug that is unique to 2.1. So I need to do one thing on 2.1 and another on all other platforms. To do this, I would check: Build.VERSION.SDK_INT which would have the value 7 on android 2.1. But this constant wasn't introduced until release 1.6. There is a similar constant in earlier versions, but since it is deprecated (and a String) I'd prefer to use newer one.
Method 1: Reflection
The "official" documentation on the how to ensure backwards compatibility says that the simplest way is by using reflection. But the example they use is really quite big and complex. In my case, the absolute minimal way of solving the problem using reflection would be the following piece of code:
int my_SDK_INT;
try {
// works for level 4 and up
Field SDK_INT_field = Build.VERSION.class.getField("SDK_INT");
my_SDK_INT = (Integer) SDK_INT_field.get(null);
} catch (NoSuchFieldException e) {
// Must be level 3 (since my app doesn't support lower levels)
my_SDK_INT=3;
}
That is, instead of a direct reference to Build.VERSION.SDK_INT, which would cause a dreaded VerifyError exception, I use getField to check if it exists. This returns a Field object, or throws an exception if the field wasn't found. In my case, an exception would mean that SDK_INT is 3 since my Manifest contains:
<uses-sdk android:minSdkVersion="3"/>
the app won't install on prior versions. On level 4 and up, once we have our Field object, we can retrieve the value using the get) method. Since the field is static, we pass a null pointer as argument to this method. Had it been an object field, we'd have to pass in the object in question.
The same technique can be used for methods that may be non existing. E.g. the following would handle the case where a static method "someMethod" may not exist:
try {
Method m=SomeClass.getMethod("someMethod");
m.invoke(obj, args);
} catch (NoSuchMethodException e) {
// Some default action
}
So that is reflection, the quick and dirty way. Needless to say, it makes sense to put all the code using reflection into a separate file and class just to make it tidy.
Method 2: Wrapper class
If we're dealing with classes or packages that may be non-existant, or if we want to avoid the overhead introduced by 'invoke' and 'get' of reflection, we can turn to the second method (which actually is a bit simpler in my opinion): using wrapper classes.
The idea is to place all references to methods, fields, and classes that may be non-existing, in separate wrapper classes. The upside to this is that in the wrapper class, we can write nice and efficient code without having to check anything. The downside is that loading the class may throw an exception. The class should implement some 'isAvailable' method that the user can call to know if it may be used. Example:
class WrapperUser {
boolean mWrapperOK=false;
static {
try {
WrapperClass.isAvailable()
mWrapperOK=True;
} catch (Throwable e) {
}
}
...
}
class WrapperClass {
public void isAvailable() throws Exception {
if (Reflect.SDK_INT<3)
throw new Exception();
}
}
The documentation suggests to use Class.forName() ) in the isAvailable-method. But this doesn't seem to work reliably when I've tried it.
Instead I prefer to use my_SDK_INT, that I wrapped so nicely above, in order to decide if a wrapper class is available or not. Obviously, this has its drawbacks, but at least it is reliable.
Caveats
Note that the wrapper class should not implement an interface which may not exist. Doing so seems to cause a dreaded VerifyError, even before the class is loaded, hence it is impossible to catch for a mere mortal application. The workaround is to introduce additional classes accessed only by the wrapper class, which in turn can implement the interfaces. I suspect that there are other similar issues with extending a potentially missing class.
Posted by android 1
0 CommentsThe Portable Android
2010-05-07 13:48:47
Yes, soon you will see Android on an iPhone 3G near you. No more excuses :) (sorry this can't be viewed to android).
Posted by android 2
0 CommentsPage 1