Mobile

Android Library Project Reference Not Showing

I previously posted about how you could make a free and lite version of your application using the Android Library project feature inside of Eclipse.

I set out to do this a few days ago with a client project that I had been working on. My development work flow went like this:

  1. Build the entire app in one project first. All features.
  2. Turn the said app into a library
  3. Build out the Full and Lite shells
  4. Crank out the Full and Lite versions as APK’s so I can get them on the Android Market.

The Problem

However, wen I got to part 3 I ran into an issue. The Android Project would not reference the Library. However, in the gen/ folder I could see that the R.java file was being generated by Eclipse with the resources from the referenced project. However, there was no referenced project icon and anytime I added anything to the ApplicationManifest.xml file Eclipse would complain that it could not find the class.

It seemed that Eclipse ‘knew’ about the project because it could build the R.java file, but could not fully find it. It was somewhere in Eclipse limbo.

Solution

After about 6 hours of trying various things I figured out what the issue was.

The problem: My projects in Eclipse had spaces in them (note, I’ve found that periods also cause a problem).

I changed the name of my projects to not have any spaces or punctuation in them. Such as “FooLibrary” instead of “Foo Library” or “Foo.Library”. Then I de-referenced the project, then referenced it again. It still would not show up. Sometimes restarting Eclipse can make everything “bind” again. Not sure why, but it seems to be a bug with the ADT plugin. Long story short, after restarting Eclipse, the referenced project was now available and working as it should.

Note, this is only the project name in Eclipse, not the Java package name.

Conclusion

Moral of the story – don’t use spaces, periods or any punctuation in your projects or libraries if you want to use Android Library Projects.

I wish the Android Documentation had a comment system so that I could post this there as it would have saved me 6 hours and a lot of frustration because spaces in the name of a project being the root cause is not too intuitive.

HowTo: Android – Full and Lite Versions

A full working code example of this post is located on my GitHub account here: http://github.com/donnfelker/FullAndLiteVersionSharedLibrary

I’m in the process of completing an Android app for a client and they needed the ability to have full and lite versions of the same application. The lite version would be free and the full version would be for a small fee (say, $2.99).

The Android Market stores applications based upon their unique Java package name. Therefore you cannot have multiple apps with the same package name. Therefore you’re left to do one of two things-

  1. Create two Android projects and copy the code, altering/removing/etc whatever is needed for the Lite/Full Version.
  2. Manually recompile and change the package name each time you want to release each version.

Either way its a real PITA. Which is why I’m writing this post. If you’re application requires Lite and Full versions (or more) then you’ll love this below.

Android Project Libraries

Android now has project libraries. This solves the problem of above. I now have the following package structure -

  • com.example.myappAndroid Project Library - This is where my ENTIRE app lives. All the functionality for the FULL and LITE versions.
  • com.example.myapp.full - Android Application Project - This is a shell that contains graphics and resources needed for the full version only. Basically it’s a super lightweight shell.
  • com.example.myapp.lite - Android Application Project – This is another shell that contains nothing but graphics and resources needed for the lite version. Again, its a super lightweight shell.

How To Determine Lite vs. Full

In the com.example.myapp, I have an Application object that derives from the Android Application object. In this part of the app I check to see the package name contains the word “lite”. If so, then the app is running under a lite version. Here’s the code:


return getPackageName().toLowerCase().contains("lite");</pre>

Whats Happening Here? The library project takes on the package name of the project that is referencing it. Therefore at runtime, the package name equates to-

com.example.myapp.lite

Therefore I know that I’m running under the context of my LITE app. I can now disable/enable a feature based upon that knowledge.

Conclusion

While this may be a quick and simple approach, it works. Now, inside of my main library (com.example.myapp), I can sprinkle if statements all over the place to determine if the app is the full or lite version. That way I can share a common code base and not have to worry about maintaining different, yet similar code bases.

Downloads

Download a full working example here

Android: ‘Must Override a Superclass Method’ Errors

I’ve been working by myself on all of my Android projects and just recently I need ed to expand my workforce to beyond that just myself. Therefore I needed to make my first hire for my company Agilevent. In doing so I needed to get my code over to another developers machine and I ran into all kinds of problems.

Background

The code is stored on GitHub in a private repository and the new developer had access. They had recently set up Eclipse and the ADT with Java 1.6. Everything was working golden. We could create a new project and everything worked fine. Except … when we tried to import the recently cloned GitHub project.

After importing Eclipse would report “Must Override a Superslass Method” on all kinds of methods all over the place (literally 86 different places in this one app).

Resolution

After a ton of futzing around we found the problem. Eclipse was defaulting to Java 1.5 for the imported project. I’m learning more and more and more about Java and its idiosyncrasies every day. In Java 1.5 classes can only be decorated with @Override when overriding a superclass method. In Java 1.6 if you have classes implementing interface methods you can decorate them with @Override and everything’s golden. You cant do that in 1.5. AKA: The source of the errors.

So where to change it?

Choose Window –> Preferences –> Java –> Compiler and ensure 1.6 is selected in the Compiler Compliance Level. If that is set and you still have problems, then in the same window, choose “Configure Project Specific Settings …”, choose the project in the chooser, and enable the project specific settings checkbox and select 1.6 in the Compiler Compliance level.

Fun times. Now if I could only get those two hours back …

Gotchas with Rooting a MyTouch Slide

I got a hold of an extra MyTouch slide for testing and wanted to root it in order to put some a different ROM on there (namely Cyanogen). The MyTouch slide root instructions are fairly well documented and have worked for a lot of people. Unfortunately they did not work for me and I got stuck where 90% of most people get stuck, in the “loop”. I was stuck in this mode for a week and nearly called it quits until I found this Gem last night. I could not get the loop to recognize my device as “offline”. Well, it would, but only for a split second.

I then found out why:

The instructions don’t point this out, and maybe this case is only due to my device but here goes:

  • With the USB unplugged from the Phone and computer, Boot into HBOOT (with the phone off, press Power + the Down Volume). Then wait a second for the boot loader to try to find some images and then once its done navigate down to “recovery” (DO NOT GO INTO RECOVERY YET), so at this point “recovery” is just highlighted. Plug the USB into the phone, the other end of the USB should NOT be plugged into anything. So now you should have a phone with a USB cord connected/dangling not connected to the computer. Read this through, then do it: Now you will need to start the loop. Then as soon as you can, plug the USB into the computer. As soon (I mean lightning quick here people) as you feel the USB pop into place ENTER RECOVERY MODE (hit the power button). This has to be done before your phone recognizes that its plugged into the USB. As soon as the phone recognizes its in USB mode it will change from HBOOT to HBOOT with USB (or something like that, you’ll see it at the top of the screen in blue when in HBOOT). Notice: the switch from HBOOT to HBOOT WITH USB is almost instantaneous. Its about 1/2 of 1 second, its that quick. So AS SOON AS YOU feel the USB click (gotta be super fast people), enter recovery mode.
  • I had to use the stock SD card that came with the phone, not a new/replacement one. Previously I had a 4 gig card in there from an old phone which was actually bought at Micro Center (had photos/files/etc on it). I read somewhere that you needed to use a stock SD card (I guess the types are different). Long story short: The new card I had in the phone did not allow it to show up as “offline”. I used the stock SD card and magically the phone started showing up in the loop immediately.

That pretty much did it for me. I was able to root the phone, install other roms and then I went full circle and brought it back to factory settings with this ClockworkMod nandroid backup.

So key points: Be fast with plugging in the USB and entering recovery mode. Use the stock SD card not some junk you bought from Micro Center (like me). :)

Android: Db4o StackOverflowError

I‘m using db4o for a persistence store in an Android app since db4o allows FREE usage in Android apps I decide it would be a good fit. Until I ran into the an error I could not get around for a couple of weeks.

Over the last couple of weeks I’ve been dealing with an error from hell, the StackOverflowError. I reworked the object model, I cut down the calls, trimmed up all the fat in the Android app I could find and I couldn’t find what was causing the db40 classes to throw a StackOverflow error.

The error that I’d get back would look like this:


06-12 18:31:06.630: DEBUG/dalvikvm(17018): newInstance failed: no ()
 06-12 18:31:06.640: INFO/dalvikvm(17018): Stack overflow, expanding  (0x41869200 to 0x41869000)
 06-12 18:31:06.640: INFO/dalvikvm(17018): Shrank stack (to 0x41869200,  curFrame is 0x41869698)
 06-12 18:31:06.640: DEBUG/AndroidRuntime(17018): Shutting down VM
 06-12 18:31:06.640: WARN/dalvikvm(17018): threadid=3: thread exiting  with uncaught exception (group=0x4001b180)
 06-12 18:31:06.690: DEBUG/dalvikvm(17018): GC freed 5995 objects /  562312 bytes in 36ms
 06-12 18:31:06.800: DEBUG/dalvikvm(17018): GC freed 3836 objects /  436456 bytes in 79ms
 06-12 18:31:06.840: DEBUG/dalvikvm(17018): GC freed 687 objects / 167288  bytes in 33ms
 06-12 18:31:06.860: ERROR/AndroidRuntime(17018): Uncaught handler:  thread main exiting due to uncaught exception
 06-12 18:31:07.020: DEBUG/Gmail(140): MailEngine.sendOrSaveMessage  messageId=1338387167469109248 refMessageId=0,  conversationId=1338387167469109248
 06-12 18:31:07.030: WARN/ActivityManager(76): Activity pause timeout for  HistoryRecord{449684b8 com.google.android.gm/.ComposeActivity}
 06-12 18:31:07.090: DEBUG/dalvikvm(17018): GC freed 3055 objects /  566504 bytes in 215ms
 06-12 18:31:07.100: ERROR/AndroidRuntime(17018):  java.lang.StackOverflowError
 06-12 18:31:07.100: ERROR/AndroidRuntime(17018):     at  com.db4o.foundation.CircularLongBuffer4.remove(CircularLongBuffer4.java:73)
 06-12 18:31:07.100: ERROR/AndroidRuntime(17018):     at  com.db4o.internal.caching.LRU2QLongCache.produce(LRU2QLongCache.java:40)
 06-12 18:31:07.100: ERROR/AndroidRuntime(17018):     at  com.db4o.internal.caching.LRU2QLongCache.produce(LRU2QLongCache.java:14)
 06-12 18:31:07.100: ERROR/AndroidRuntime(17018):     at  com.db4o.io.CachingBin.getPage(CachingBin.java:153)
 06-12 18:31:07.100: ERROR/AndroidRuntime(17018):     at  com.db4o.io.CachingBin.readInternal(CachingBin.java:65)
 06-12 18:31:07.100: ERROR/AndroidRuntime(17018):     at  com.db4o.io.CachingBin.read(CachingBin.java:57)
 06-12 18:31:07.100: ERROR/AndroidRuntime(17018):     at  com.db4o.io.BinDecorator.read(BinDecorator.java:40)
 06-12 18:31:07.100: ERROR/AndroidRuntime(17018):     at  com.db4o.io.BlockAwareBin.blockRead(BlockAwareBin.java:90)
 06-12 18:31:07.100: ERROR/AndroidRuntime(17018):     at  com.db4o.internal.IoAdaptedObjectContainer.readBytes(IoAdaptedObjectContainer.java:210)
 06-12 18:31:07.100: ERROR/AndroidRuntime(17018):     at  com.db4o.internal.IoAdaptedObjectContainer.readBytes(IoAdaptedObjectContainer.java:201)
 06-12 18:31:07.100: ERROR/AndroidRuntime(17018):     at  com.db4o.internal.LocalTransaction.readPointer(LocalTransaction.java:387)
 06-12 18:31:07.100: ERROR/AndroidRuntime(17018):     at  com.db4o.internal.LocalTransaction.getCurrentSlotOfID(LocalTransaction.java:355)
 06-12 18:31:07.100: ERROR/AndroidRuntime(17018):     at  com.db4o.internal.LocalObjectContainer.readReaderOrWriterByID(LocalObjectContainer.java:437)
 06-12 18:31:07.100: ERROR/AndroidRuntime(17018):     at  com.db4o.internal.LocalObjectContainer.readReaderByID(LocalObjectContainer.java:415)
 06-12 18:31:07.100: ERROR/AndroidRuntime(17018):     at  com.db4o.internal.LocalObjectContainer.readReaderByID(LocalObjectContainer.java:419)
 06-12 18:31:07.100: ERROR/AndroidRuntime(17018):     at  com.db4o.internal.marshall.UnmarshallingContext.readBuffer(UnmarshallingContext.java:127)
 06-12 18:31:07.100: ERROR/AndroidRuntime(17018):     at  com.db4o.internal.marshall.UnmarshallingContext.read(UnmarshallingContext.java:40)
 06-12 18:31:07.100: ERROR/AndroidRuntime(17018):     at  com.db4o.internal.ObjectReference.read(ObjectReference.java:304)
 06-12 18:31:07.100: ERROR/AndroidRuntime(17018):     at  com.db4o.internal.ObjectReference.read(ObjectReference.java:290)
 06-12 18:31:07.100: ERROR/AndroidRuntime(17018):     at  com.db4o.internal.ObjectContainerBase.getHardObjectReferenceById(ObjectContainerBase.java:956)
 06-12 18:31:07.100: ERROR/AndroidRuntime(17018):     at  com.db4o.internal.marshall.AbstractReadContext.classMetadataForObjectId(AbstractReadContext.java:85)
 06-12 18:31:07.100: ERROR/AndroidRuntime(17018):     at  com.db4o.internal.marshall.AbstractReadContext.readObject(AbstractReadContext.java:57)
 06-12 18:31:07.100: ERROR/AndroidRuntime(17018):     at  com.db4o.internal.marshall.AbstractReadContext.readAtCurrentSeekPosition(AbstractReadContext.java:46)
 06-12 18:31:07.100: ERROR/AndroidRuntime(17018):     at  com.db4o.internal.OpenTypeHandler.read(OpenTypeHandler.java:172)
 06-12 18:31:07.100: ERROR/AndroidRuntime(17018):     at  com.db4o.internal.Handlers4.readValueType(Handlers4.java:313)
 06-12 18:31:07.100: ERROR/AndroidRuntime(17018):     at  com.db4o.internal.marshall.AbstractReadContext.readAtCurrentSeekPosition(AbstractReadContext.java:48)
 06-12 18:31:07.100: ERROR/AndroidRuntime(17018):     at  com.db4o.internal.marshall.AbstractReadContext$1.run(AbstractReadContext.java:38)
 06-12 18:31:07.100: ERROR/AndroidRuntime(17018):     at  com.db4o.internal.marshall.SlotFormat.doWithSlotIndirection(SlotFormat.java:64)
 06-12 18:31:07.100: ERROR/AndroidRuntime(17018):     at  com.db4o.internal.marshall.AbstractReadContext.readObject(AbstractReadContext.java:36)
 06-12 18:31:07.100: ERROR/AndroidRuntime(17018):     at  com.db4o.internal.handlers.array.ArrayHandler.readInto(ArrayHandler.java:381)
 06-12 18:31:07.100: ERROR/AndroidRuntime(17018):     at  com.db4o.internal.handlers.array.ArrayHandler.readElements(ArrayHandler.java:363)
 06-12 18:31:07.100: ERROR/AndroidRuntime(17018):     at  com.db4o.internal.handlers.array.ArrayHandler.read(ArrayHandler.java:355)
 06-12 18:31:07.100: ERROR/AndroidRuntime(17018):     at  com.db4o.internal.Handlers4.readValueType(Handlers4.java:313)
 06-12 18:31:07.100: ERROR/AndroidRuntime(17018):     at  com.db4o.internal.marshall.AbstractReadContext.readAtCurrentSeekPosition(AbstractReadContext.java:48)
 06-12 18:31:07.100: ERROR/AndroidRuntime(17018):     at  com.db4o.internal.OpenTypeHandler.read(OpenTypeHandler.java:172)
 06-12 18:31:07.100: ERROR/AndroidRuntime(17018):     at  com.db4o.internal.Handlers4.readValueType(Handlers4.java:313)
 06-12 18:31:07.100: ERROR/AndroidRuntime(17018):     at  com.db4o.internal.marshall.AbstractReadContext.readAtCurrentSeekPosition(AbstractReadContext.java:48)
 06-12 18:31:07.100: ERROR/AndroidRuntime(17018):     at  com.db4o.internal.marshall.AbstractReadContext$1.run(AbstractReadContext.java:38)
 06-12 18:31:07.100: ERROR/AndroidRuntime(17018):     at  com.db4o.internal.marshall.SlotFormat.doWithSlotIndirection(SlotFormat.java:64)
 06-12 18:31:07.100: ERROR/AndroidRuntime(17018):     at  com.db4o.internal.marshall.AbstractReadContext.readObject(AbstractReadContext.java:36)
 06-12 18:31:07.100: ERROR/AndroidRuntime(17018):     at  com.db4o.internal.marshall.AbstractReadContext.read(AbstractReadContext.java:28)
 06-12 18:31:07.100: ERROR/AndroidRuntime(17018):     at  com.db4o.internal.FieldMetadata.read(FieldMetadata.java:811)
 06-12 18:31:07.100: ERROR/AndroidRuntime(17018):     at  com.db4o.internal.FieldMetadata.activate(FieldMetadata.java:646)
 06-12 18:31:07.100: ERROR/AndroidRuntime(17018):     at  com.db4o.internal.handlers.StandardReferenceTypeHandler$2.processAspect(StandardReferenceTypeHandler.java:93)
 06-12 18:31:07.100: ERROR/AndroidRuntime(17018):     at  com.db4o.internal.metadata.MarshallingInfoTraverseAspectCommand.processAspect(MarshallingInfoTraverseAspectCommand.java:52)
 06-12 18:31:07.100: ERROR/AndroidRuntime(17018):     at  com.db4o.internal.metadata.StandardAspectTraversalStrategy.traverseAllAspects(StandardAspectTraversalStrategy.java:24)
 06-12 18:31:07.100: ERROR/AndroidRuntime(17018):     at  com.db4o.internal.ClassMetadata.traverseAllAspects(ClassMetadata.java:2086)
 06-12 18:31:07.100: ERROR/AndroidRuntime(17018):     at  com.db4o.internal.handlers.StandardReferenceTypeHandler.traverseAllAspects(St

After posting on the forums I was given some advice from Fabio (who looks to be an employee),. He advised to try the TransparentActivation mechanism. I implemented it on my 6 classes and in the configuration of db4o as shown in an example in the previous link.

Magically, the error went away. So, if you get this error, implement the Activatable interface (or IActivatable in .NET).

NOTE: This could be vastly useful when Windows Phone 7 is released if you’re using it for .NET.