日記:Android勉強会とアプリのアップデート

昨日ですが、うちでAndroid勉強会を開きました。
参加者は自分併せて5名で、集まってそれぞれが好きに開発をやるという形式だったので、まぁ勉強会というか、わからないところを教えあいながら開発をやるというスタンスでした。Androidと銘打ちながらも、まあ開発者で集まって勉強しようぜーという感じだったので、CMSの話とかもしたり、組み込みの話をしたり、色々でしたねー。

私は音声エディタのバージョンアップ(ステータスバーに常駐)を行おうと思っていたので、それをやって、他の方が躓いているところでアドバイスしたり、教えてもらったりなどなど。Eclipseの使い方で知らない事とか、やっぱり結構あるようで、そういうのを教えたりしました。

  1. タブのダブルクリックで最大化される
  2. タブをドラッグして移動させれば表示領域を変えられる
  3. DDMSのログの見方

などなど。ノートPCでやってると解像度が低いのでなかなか作業がしづらいので、こういうのを知っているだけでずいぶん効率が変わりますからねー。string.xml書きながらActivityファイル書いたりとかもできるので、1画面に2ファイル表示とかは便利です。

私としてもADKのtools以下についてほとんど触った事がなかったので、そういうのを使うといいですよーと教えてもらって勉強になりました!デバッグうまくなりたいんで!Androidでテストケースとかまだやったことないので、そこらへんもぼちぼちやっていきたいところです。

勉強会が終わった後に、ご飯を食べに行って色々と話をしてとても楽しかったです。今回、懇親会に来れなかった方も次やるときはぜひ参加していただけたらなぁと思います!開発者の輪、Androidの輪を拡げていきましょう!!

あと、頭を悩ませ続けていたBatteryCrystalのアップデートと、音声エディタのアップデートを行いました。

BatteryCrystalはHT-03Aを持っていなかったので、twitterでHT-03A所有者の方々にテストを手伝っていただきながら、2度のダメだしを喰らっての、3度目の正直でのリリースだったので、感慨深かったです…(T_T)テスト協力者の方々、本当にありがとうございました。また、助言を頂けた方々にも感謝しきりです!Android開発者のネットワークは温かくてあり難いです!

音声エディタは、「よく使うので、簡単に呼び出せるようにしてほしい」というリクエストがあったので、マッシュドアーのようにステータスバーに常駐させるようにしました。簡単だったので、それでリリースしたのですが、今度は「常駐を解除できるようにしてほしい」というリクエストが入ったので、それに対応しました(まぁ確かに、あのアイコンがステータスバーに常にあったら、オタク系の人と勘違いされかねないかも…)。

1日の間に3度リリースしたので、結構疲れました(^_^;)でも充実した日だったと思います。また定期的にAndroid勉強会を開催したいなぁと思いました。


Android: AlarmManagerのPendingIntentでハマった

先週からずっとBatteryCrystalのバグ修正と戦っているのですが、
未だに収束していません!(>_<) バッテリー状態を見張るBroadcastReceiverを登録しているServiceがシステムに勝手に落とされるのがバグの原因と思っているのでそれをなんとかしようと色々と修正し、twitterでアプリの検証をしてくださる方を募ってテストしていただいているのですが、まだ直らず。今さっき出来たバージョンでたぶんいけると思うのですが、深夜なので、メールは朝させてもらおうと思ってます。 とりあえず、今までやったことを一覧にしておきます。

  1. AlarmManagerを使って制御。
    ただ、AlarmManager.setしたイベントがウィジェットを削除してもServiceを起動させてしまったため、断念(私のミス)
  2. ServiceのonDestroyのタイミングで、独自のBroadcastを投げて、受け取ったレシーバがServiceを起動させる。
    これで勝ったと思ったのだけど、ServiceがonDestroyを通らずに落ちる場合があるらしく、動かなかったと報告ありorz
  3. @yokmamaさんと@hyoromoさんから助言いただき、AlarmManagerに再度チャレンジ。cancelすればServiceを止められるらしい。で、成功!あとはHT-03Aユーザさんに検証していただく←今ここ

で、紆余曲折あったので、メモを残しておこうと思います。

  1. PendingIntentをメンバ変数に持ってはいけない!

あれ?結果的にこれだけだなー…。
これはどういうことかというと、AlarmManager.setするときに、PendingIntentのインスタンスを渡すんですが、AlarmManager.cancelするときにもPendingIntentの同じ情報を渡すので、同じだったらメンバ変数に持っておいたら消す時に間違わなくていいだろうと思っていたのです。ですが、このメンバ変数に入れてあるPendingIntentを渡してcancelしたつもりでも、全然キャンセルされないのです。

この時点でPendingIntentをメンバ変数にしているということが原因だと気付いていなかったので、cancelするタイミングをonDeletedからonDisabledに変えてみたりだとか、onUpdateイベントをsetしていたので、異なるレシーバ経由でService呼び出してみたりだとか、余計なことばかりしていました。

数時間後に、ここをメンバ変数でなく、イベント毎にPendingIntentを生成してAlarmをcancelしたら止まりました。

おそらく、これでサービスの起動も定期的に確認できるので、HT-03Aなどでもまともに動くようになるかと思います。ただイベント発生数が前のに比べると結構多いので、バッテリーの消費量が気になるのですが。まぁ通信とかなくて自分のバッテリー残量を確認するだけのサービスの生存確認なので、大した事はないかと思いますけどね…。

ソースの全体ではありませんが、ハマった部分について自戒も込めて公開しておきます。
LogUtils.dは、自分のライブラリのメソッドです。
マニフェストでandroid:debuggable=trueならLog.dでログ出すというやつです。

public class BatteryCrystal extends AppWidgetProvider {
	private static final String TAG = "BatteryCrystal";
	private static final long INTERVAL = 300000;	// 5分
//	private PendingIntent sender; // 使えなかったのでコメントアウト

	@Override
	public void onDisabled(Context context) {
		LogUtils.d(TAG, "onDisabled called!!");
		AlarmManager am = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
		// ウィジェットが削除されたらアラームを停止する
		// メンバ変数ではだめだったのでgetPendingAlarmIntentで取得
		PendingIntent sender = getPendingAlarmIntent(context);
		am.cancel(sender);
		super.onDisabled(context);
	}
	@Override
	public void onReceive(Context context, Intent intent) {
		LogUtils.d(TAG, "onReceive called!!");
		String action = intent.getAction();
		if (AppWidgetManager.ACTION_APPWIDGET_UPDATE.equals(action)) {
			setAlarm(context);
			Intent serviceIntent = new Intent(context, BatteryCheckService.class);
			// context.stopService(serviceIntent);
			context.startService(serviceIntent);
		}
		super.onReceive(context, intent);
	}
	private void setAlarm(Context context) {
		AlarmManager am = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
		long now = System.currentTimeMillis() + 1;
		long nextTime = now + INTERVAL - now % 1000;
		// メンバ変数ではダメだった
		// sender = getPendingAlarmIntent(context);
		PendingIntent sender = getPendingAlarmIntent(context);
		am.set(AlarmManager.RTC_WAKEUP, nextTime, sender);
	}

	private PendingIntent getPendingAlarmIntent(Context context) {
		Intent intent = new Intent(context, BatteryCrystal.class);
		intent.setAction(AppWidgetManager.ACTION_APPWIDGET_UPDATE);
		PendingIntent pendingIntent = PendingIntent.getBroadcast(context, 0, intent, 0);
		return pendingIntent;
	}
}

Android: 外部ライブラリ化の方法

自分がよく使うような処理をクラスにまとめてライブラリ化しておきたいんだけど、どうすりゃいいんだー!って思っていたのですが、ググってみたらありました。@tomorrowkeyさんがまとめてくださっていました。素晴らしい!!

外部ライブラリのすすめ2 – 明日の鍵

自分はログの出力をマニフェストのandroid:debuggableの値で切り替えるようにしました。trueだとログ出力。falseだとログ出さないと。いちいちログの部分をコメントアウトしていくのは面倒ですからねー…。


日記:Android勉強会@愛媛

Android勉強会@愛媛に参加してきました。
Androidのタブレット評価会ということだったのですが、まったく評価機触らずに他の開発者の方にアドバイス聞いてデバッグしてました。うーむ、せっかくの機会だったのだから、触っておいたほうがよかったかもしれないとちと後悔。

実際にアプリを開発している人の参加は、瀬戸デ部メンバー以外はいなさそうだった。いや、学生はしているみたいだったかな。興味があるというレベルとかの方が大多数。もくもくとデバッグしていました。結局わからなかったけど…。

その後、瀬戸デ部のメンバーでご飯食べたり、カラオケに行ったりなど。開発者同士だと濃い話もできるので、非常に参考になるし面白い。来週は自分ちで、再来週は広島でAndroidイベントやるので、どんどんこういう輪を拡げていきたいと思います。

実際にHT-03Aでアプリが停止しているのを見てしまったので、なんとかせんといかんと思って、DQBの開発者の@puddingatさんに協力いただいて、ソース提供してもらいました。感謝!早速参考にしながらソース修正して、twitter上でアプリのテスター募集したら、複数の方々にテストしていただける運びに…。本当にありがたい…(>_<) 1日無事にアプリが動いていたら、BatteryCrystalのアップデート版をリリースしようと思います。


日記:IS01購入 と LuvPad予約

今日はAndroidデーでした。
会社の近くにパソコンショップがあるのですが、そこでマウスコンピュータが発売するAndroid 2.2 のタブレットのLuvPadの予約が開始とあったので、就業時間後に早速予約してきました!iPadの対抗であると共に、2.2ですから、いろいろといじりがいがありそうです。もし使ってみてよさそうだったら、親に買ってもいいかなと思います。なにせPCよりもとっつきやすいし、軽いし、起動も速いし、安いし。39,800円だったかな。

あと、Androidの検証端末がもっと欲しいなぁと思って中古ケータイショップ巡りをしていたのですが、そこでIS01を発見!!なんと12,200円という価格だったので、即決で購入してしまいました。あぁ、もうAndroidに関しては金銭感覚がなくなっていってる。でも相当安いんですよ、これ!!横にガラパゴスケータイの最新機種が 40,000円で売られてましたからね…。本当はDoCoMoのHT-03Aが欲しいんですが、まだ見つからず

IS01はSIMがなくても動作するので(もちろん3Gは使えませんが)、WiFi環境では普通にインターネットにも繋がるし、Android Marketも使えました。なんかメールソフトにバグが発生していて現在販売中止になっているようですが…。大丈夫かな?音声エディタ試してみたんですが、音声エディタはIS01との相性が一番よさそうです。なんだかんだでパンタグラフキーボードも打ちにくいし、喋って入力して、誤変換起こしたところだけ、パンタグラフキーボード使って修正ってのがよさげです。

明日はAndroid勉強会なので、勉強してきまーす!!IS01も持っていきます!!