Today, I will share my wonderful adventures with Library Projects !
Library Projects are a new feature in Froyo, that I was really excited about (see here) !
Library Projects allow you to share some whole parts of your applications, resources included. The main and immediate use is to create several versions of the same application.
For instance, if you have a payed version and an ad supported free version, you need to have 2 differents applications, but with very few differences.
In my case, I have two different games "Word Prospector" and 'Chasseur de mots", that are, basically, the same game, but with different data ( the dictionary : it is a english and a french version of the same game ). As the dictionary is 50% of the apk weight, I didn't want to have only one application with two dictionaries.
But, let's face it : until now, I dealed with those two versions as badly as I could : when I made a development in one of the game, I had then to report it in the other game.
I could not even copy / paste all the files, as some divergence appeared. For instance, some key values for middleware ( Flurry for instance ).
So I was really impatient to try this new Android feature !
The start :
Reading the Google doc, it seems really easy to use : make a library project, click "Is Library" in the project properties, then in the application projects, just select this library as "used library".
First step, first success...
The first step was creating the library. I saw in the documentation that you can convert an existing project into a library project, with just one click ! But this arises a question : is this library still considered as a legit application ?
So I tried to launch this library version... And it worked perfectly !
It wouldn't last :)
Second step, first fail !
Once I had my library, in just a matter of seconds, I tried to use it in my french version of the game.
I started to copy my whole project (just in case thing would be funkier than expected ! ).
Then I removed one activity from this game, expecting Eclipse to find it in the library.
So I deleted this activity file, and in the manifest, I change its declaration, by adding the path to the library activity.
So I was changing :
Looks quite simple, for at this moment, I really enjoyed it !
Then I try to compile it, and Eclipse started complaining : it didn't know about this activity...
Gasp... After different tries, I search on the Internet to find the solution :
You have to close and re-start Eclipse !
Ok, it just worked perfectly.
But, for some reasons, my confidence started to vanish...
I continued with all my activities until I had a very simple project : an application, and some data files.
All the activities were coming from the library...
The manifest was OK with this config, but I still had some compilation issue.
Next Step : dealing with Attr
As I'm using AdMob in my game, I have a custom AdMob view, and some custom attributes for this view, defined in an attr.xml file.
Trouble is : the game using the library couldn't find the csutom attributes.
Adding another attr.xlm file in the game using the library didn't help : the compiler complaining that some attributes being defined twice (aarggghhh.. )
Searching on the Internet, I found this discussion here where Xavier Ducrochet, Android SDK Tech Lead, said "At this time, the only solution is to remove the layout from the library and move it into the apps and edit the namespace to match the application package".
Ok, so I tried to do that, but couldn't manage to have a compiling solution.
So I took Admob off, while waiting for a good idea...
( my goal at this point was to have something working as soon as possible, if necessary with less features. )
Getting the application from Activities
As I said somewhere in the intro, the code for the two games is the same, but for some keys values for middlewares ( Flurry and Scoreloop ).
So I decided to put the keys in the application ( as each game already has its own application ), and let the activities get them from the application.
So I had some code like that in the (common) activities :
Application CurrentApp = getApplication();
LetterGameApp CurApp = (GameApp) CurrentApp;
MyMiddlewareKey = CurApp.GetMyMiddlewareKey();
But it just did not work : the activities are in the library, so the GameApp cast is interpreted as a
GameLibrary.GameApp - the library application-.
And in my game using the library getApplication returns a GameUsingLibrary.GameApp, that is not related to a GameLibrary.GameApp and the cast fails !
My Solution at this point was to make the GameUsingLibrary application inheriting from the GameLibrary application.
This way, the cast is still valid.
Note that if it were only for the key values, I could have stored them in some xml files. But I'm also using the application to gather some informations on the user behaviour, to help me for debugging crashes, so I call some application methods from all over my code !
Final step, final fail...
At this step, I had everything working correctly.
Both the game used as a library and the game using the library were working correctly.
It was quite late, and I was looking forward to go to bed.
I made a last test on the game using the library ( you should never make a last test !! ), and found something really strange : a breakpoint being triggered on a empty line !! And then I could step throw some comment lines. WTF ???
So I decided to clean the two projects, to rebuild them.
I then tried to launch both of them... And when launching the game used as a library, Eclipse said 'Could Not Find EnglishGame.apk'.... ???
I cleared the project once again. Same message. I modified the code a little... Same message...
I restarted Eclipse. Same message.
I reboot my computer. Same message.
I'm sure all of you have already feel the same when a very last little modification ruins your entire development day. I felt a little distressed, tired, with a hard desire to kick my computer ( and I perhaps had some bad thoughts for Google engineers... Sorry guys... ).
Finally, I unchecked the "Is A library" checkbox, and the .apk could at least be generated !
Dealing with attr, slight return
As light as they can be, my revenues with AdMob are the only income I get from my android games, and at least as a symbol, I really want to keep it, so I wanted to fix the attr issue and have AdMob back before publishing the version.
I couldn't find the solution for the attr issue, but I finally could fix my problem :
I got rid of my attr file, and of the custom attributes of the AdMob view.
And set this custom attribute values by code, and no more in the activity layouts.
Finally, I now need to uncheck the 'Is A Library' checkbox when I want to make the first game, and recheck it when I want to make the second game.
This is a complete non sense, and I feel now like having a library that is also a legit application was just plain wrong. But the documentation is not completely clear on this point, and Eclipse partly let you do it, so it's easy to feel like it's possible when it's not really.
I really should have a library project, and two application projects using this library.
After some time using this configuration, I can tell you that Library Projects are really nice to use.
There are still some place to make this feature better, there are still some confusing things. For instance, debugging is quite strange : you debug a kind of fake file, with a path like 'MyProject/PathInTheLibrary/FileInTheLibrary'.
and this file really does not exist !
Modifying this fake file really modifies the file in the library, so every thing seems to fit perfectly in place, but it still is quite strange, and several times, I launched the wrong game.
There are still some issues - hopefully corrected in the next SDK version - but once your setup is done, and most of your issues solved, having this solution to have your code in only one place is a huge confort gain !