先の記事で、継承使えば良いんじゃーとか偉そうに言いましたが、
http://developer.android.com/tools/projects/index.html
に下記の記述がありました(汗)
読んでねえ。もちろん読んでねえ。
If you are creating an application that exists in both free and paid versions. You move the part of the application that is common to both versions into a library project. The two dependent projects, with their different package names, will reference the library project and provide only the difference between the two application versions.
で、肝心のlibrary化はどうやるのか?
Eclipseだと色々と助けてくれると思うので、ここでは敢えてantベースの手順を記載します。
ざっくりとした流れは下記の通り。
- ベースとなるプロジェクトを作成。まずはlibrary projectでなくてOK
- ベースプロジェクト内で実装、Free/Paid共通の基本機能をもつアプリとして作成し動確します
- ベースプロジェクトをlibrary project化
- 新たにFreeもしくはPaidのプロジェクトを作成(どちらか、もしくは必要ならば両方)
- library projectを参照、継承しFree/Paidのアプリを作成
です。上記でポイントとなる所についてちょっと解説します
■ベースプロジェクト(以下lib project)のlibrary project化
これは非常に簡単です。lib projectのproject.propertiesに下記を追加します
android.library=true
■library projectの参照
作成したFree or Paid版プロジェクト(以後app project)のproject.propertiesを編集し下記を追加します
android.library.reference.1=../path/to/lib-project
パスは相対パス(project.propertiesからのパス)である点に注意
■lib projectを使用する際の注意点
本記事を書いている時点でまだあまり一般的でないと思われる注意点として
「libで生成されるR.idはfinalが付いてない」
という問題があります。
これはどうやら仕様のようです。(
http://d.hatena.ne.jp/Kazzz/20111022/p1)
これがどういう事かと言うと、一般的な問題としてR.idをswith/caseの条件として使用できなくなる、という事です。
http://developer.android.com/guide/topics/ui/menus.html
はおもいきりswitch caseで実装してますが・・・
上記の例に逆らって、R.idはswitch/caseの条件に使用しないようにしましょう。つまり上記のURL先にある下記のコードを例に取ると
@Override
public boolean onOptionsItemSelected(MenuItem item) {
// Handle item selection
switch (item.getItemId()) {
case R.id.new_game:
newGame();
return true;
case R.id.help:
showHelp();
return true;
default:
return super.onOptionsItemSelected(item);
ではなく
@Override
public boolean onOptionsItemSelected(MenuItem item) {
if (item.getItemId() == R.id.new_game) {
newGame();
return true;
} else if (item.getItemId() == R.id.help) {
showHelp();
return true;
} else {
return super.onOptionsItemSelected(item);
}
にしておく必要があります。
■lib projectにあるactivityを使い回す
複数のactivityが存在するlib projectをベースにしている場合、lib projectのactivityが明示的intentで他のlib project内のactivityを
startActivityしているケースが多いと思います。この場合、このactivityは当然lib project内のactivityを指定してしまいます。この指定される先のactivityをapp projectで上書きしたactivityに置き換えたい場合に登場するのがAndroid.Manifest内のactivity-aliasです。
使い方は簡単で、app projectのAndroid.Manifest内の
内に必要なactivityの数だけ下記を追記します
appActivityにlibActivityの名前を付ける、という意味です。
例えばあるlibActivity1がlibActivity2を明示的intentでstartActivityするように実装されているケースにて、起動されるActivityをlibActivity2を継承したappActivity2に差し替えたい場合は、appActivity2にlibActivity2という名前を付けてあげる、というやりかたで解決しているわけです。
■lib project内でGCMとか使ってる場合のtips
GCMでは、ユーザーがGCMBaseIntentServiceを継承し、メッセージを受けるようにGCMIntentServiceを実装するのがパターンです。しかしこのGCMIntentServiceをlib package内で定義した場合でも、GCMは固定的にapp-package.GCMIntentServiceに対して通知してしまいます。
lib-packageのGCMIntentServiceをそのまま使うためには、GCMがlib.package.GCMIntentServiceに対して通知するようにしなくてはなりません。このためには
以上で、app projectでもlib.package.GCMIntentServiceにちゃんと通知が行くようになります。