プログラマってこんなかんじ??

アプリ作ったり歌ったりしてます

ダイアログの縦横切替でメモリリーク(XxxActivity has leaked〜)

AlertDialog.Builder#show〜 でダイアログ表示した状態で、
画面の縦横切替 を行うと例外発生してアプリ終了してしまった。


ログを見るとこんなエラーログが出力されている。

XxxActivity has leaked window com.android.internal.policy.impl.PhoneWindow$DecorView@4053b7b0 that was originally added here


リーク発生!!


AndroidManifest.xml の 「android:configChanges」 属性をいじって
「Activityの再生成を行わないようにする」 が対応としては簡単なんだが、
これは根本的解決になっていないので却下。


ではどうしたものか、、というわけで調べてみると公式に記載発見。


Creating Dialogs


ダイアログ表示したいときは

「Activity#showDialog、onCreateDialog」を使ってねー
そうすると ダイアログのライフサイクル も Activity管理 になるよー

とのこと。


簡単に書くと、

  1. Activity#onCreateDialog を override してダイアログ実装しとく(任意idに紐付け)
  2. 任意の場所で Activity#showDialog(1で指定したid) を呼び出す
  3. onCreateDialog が呼び出されて ダイアログが表示される


そうすることで、
ダイアログ表示したままで Activityが再起動(onDestroy〜onCreate) したとしても、
Activityと一緒に自動的にダイアログも再表示される、と。
なるほど。


というわけでさっそく、
ボタン押下でダイアログを表示するだけのサンプルアプリ書いてみた。

public class MainActivity extends Activity {

    private static final int DIALOG_ID_ABESHI = 0;

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        //case1 WindowLeaked発生するパターン(修正前)
        Button buttonLeak = new Button(this);
        buttonLeak.setText("leak");
        buttonLeak.setOnClickListener(new View.OnClickListener() {
            public void onClick(View v) {
                Dialog dialog = createDialog(DIALOG_ID_ABESHI);
                if (dialog != null) {
                    dialog.show();
                }
            }
        });

        //case2 WindowLeaked発生しないパターン(修正後)
        Button buttonNotLeak = new Button(this);
        buttonNotLeak.setText("not leak");
        buttonNotLeak.setOnClickListener(new View.OnClickListener() {
            public void onClick(View v) {
                showDialog(DIALOG_ID_ABESHI);
            }
        });

        LinearLayout layout = new LinearLayout(this);
        layout.addView(buttonLeak);
        layout.addView(buttonNotLeak);
        setContentView(layout);
    }

    @Override
    protected Dialog onCreateDialog(int id) {
        return createDialog(id);
    }

    /**
     * ダイアログ生成
     * @param id
     * @return
     */
    private Dialog createDialog(int id) {
        if (id == DIALOG_ID_ABESHI) {
            AlertDialog.Builder builder = new AlertDialog.Builder(this);
            builder.setMessage("あべし");
            builder.setPositiveButton("OK", new OnClickListener() {
                @Override
                public void onClick(DialogInterface dialog, int which) {
                    Log.d(getClass().getName(), "onClick");
                }
            });
            return builder.create();
        }
        return null;
    }
}


このコードで、ダイアログを表示したままで画面の縦横切替を行うとこんな結果になる。

case1 WindowLeaked〜 が発生する(ダイアログは勝手に閉じてそのままアプリ終了)
case2 ダイアログが表示された状態 で縦横画面表示


修正後の case2 だと、リーク発生しなくなった!!


そこでちょっと沸いた疑問。


もしかして、Activity#showDialog を呼ぶたびに onCreateDialog が毎回呼び出されて
毎回ダイアログ再生成、とかはないよなー??と。


Activity.java のソース見てみるとそんなことはもちろんなく、
一度生成済のダイアログは内部で保持されている様子。
よくできてる。


というわけで、ダイアログ表示する際には、
「Activity#showDialog、onCreateDialog」を使ったほうがいいね、というお話でした!

端末起動時に起動する常駐serviceのデバッグ(ACTION_BOOT_COMPLETED)

表題のような常駐serviceを作成しようとすると

BroadcastReceiver が intent(ACTION_BOOT_COMPLETED) を受けたらそれを契機にservice起動する

という仕組みが主流だと思う。(自分はこれしか知らない)

こんなserviceをeclipseで修正/動作確認しようとすると、

  • eclipseでソース修正->build->apkファイル作成
  • apkファイルuninstall
  • apkファイルinstall
  • emulator再起動

と非常にめんどくさい。
emulatorの再起動は時間がかかるのでなるべく控えたい。。


こんなときにー!


intentはコマンドラインでも投げれたりする。
たとえば、intent(ACTION_BOOT_COMPLETED)を投げる方法はこんなかんじ。

>adb shell
#am broadcast -a android.intent.action.BOOT_COMPLETED
  • eclipseでソース修正->build->apkファイル作成
  • apkファイルuninstall
  • apkファイルinstall
  • コマンドラインで intent(ACTION_BOOT_COMPLETED) 投げる
  • intent(BOOT_COMPLETE) を契機にservice起動

emulator再起動せずにこんなかんじで開発できるので便利便利。

訃報

福岡でコミュニティ活動等いろいろな活動に尽力されていた、
@sugmak こと 杉山誠氏 が 2010/7/25 に永眠(享年44歳)されました。


自分が初めて参加した PHP in Fukuoka。
webのいろはもよくわかっていない、
コミュニティのこともよくわからない。
知り合いもほとんどいない。
そんなときに @sugmak に出会ったのをおぼえてます。
それから自分が各種コミュニティ活動に関わるようになって。
毎回会うたびにいろんな話をしてました。


いろいろおぼえてます。


コミュニティ活動だけではなく、
縁あって一緒に仕事させていただくことがあって。


自分が前の会社に初出社する前日。
おそろしく不安になっているときに、
不安にならないようにたくさんフォローしていただいたこと。


同じ案件を一緒に作業するようになったとき。
技術的な部分で自分がひとりでウンウンうなっているときに
声かけてくれていろんなノウハウを教えていただいたこと。


言いたいことを整理できずにうまく話せず、イライラしたり、
意味のわからないことを言っている自分に対しても
とことん最後までつきあってくれたこと。


仕事の途中に一緒に担々麺を週に何回も食べに行ったこと。
いつも大盛激辛食べてたこと。


自分がちょっと心が弱くなったとき、
自分が体調崩しがちになったときに
とことん話聞いてくれたこと。
身体にいい、と運動や身体によさそうなことを
いろいろ提案してくれたこと。


自分が今後のキャリアプランで悩んでいるときに
背中押すような言葉を言ってくれたこと。


飲みにいくと一緒になって大騒ぎ。
一緒にメキシコ料理食べに行ったこと。
アミーゴ!アミーゴ!言いあったこと。


WCF2010では100人超えの懇親会の幹事をすることになり、
経験のないことなので準備から当日まで正直ぱにくってた自分。
そんなときに細かいことまでチェックしてくれて、
いろんな助言もしていただいて。
当日もずっと気にかけてくれて声をかけてくれたこと。


いつも相談にのってもらってばっかり、助けてもらってばっかり。
ぜんぜん恩返しもできていない。


本当に面倒見がよく、アニキ的存在だと思っている人も少なくないと思う。
少なくとも自分はそう思ってました。
アニキのようで、厳しく、親身になっていろんな話を聞いてくれて。


@sugmak から今までたくさんいただいた助言、共有した時間を忘れずに、
今後はもっと気を引き締めて、身体に気をつけて、しっかり生きていかねば。
しょうもないことで悩んでいたらまた笑い飛ばされてしまう。


f:id:dai4649:20080830180540j:image


写真は WBC2008 Summer のときかな。
友人と一緒に3人で撮った写真。
笑顔から @sugmak のやさしさが本当ににじみでてます。
いまだに実感沸かないけど、写真を見ると涙が止まらない。
通夜、告別式の話を聞くとこれが現実なのか、と。


@sugmak 親分、いままで本当にお疲れ様でした。
謹んでご冥福をお祈りいたします。

flickrでGuestPassつき写真(オリジナル)を一括ダウンロードする

みんなで撮った写真を共有するとき、みんな何のwebサービス使ってるのだろう。
自分はオリジナル画像を手元に残しておきたい派なので、
ULしている画像を簡単に一括DL可能なPicasaを使用してて。
まわりのメンバーがよく使用しているflickrには一括DLする方法がないと(勝手に)思っていたので
まったく使っていなかったのだが、実は方法があると知ったのでメモ。

方法的にはいろんな方法があったのだが、普段から使ってるダウンローダーの
DownThemAllを使用するこの方法を参考にさせていただいた。

Flickrにアップロードしたオリジナル画像を、一括再ダウンロードする方法。 | kimihiko Tech

使用するツールはこれ。

手順としてはこれだけ。

  • グリモン(Flickr - Link Original Image)追加
  • アドオン(DownThemAl)追加
  • firefoxflickr開く(オリジナル画像DLしたいページ)
  • 右クリック -> DownThemAll

一括ULも簡単にできるのならこっちに乗り換えてしまおうかな。

日本語変換したら落ちる

昨日の話。いつもどおりSMS作ってたらとつぜん強制リセット。
画面にノイズが走ってぶつっと。appleマークが表示され。

再起動後。SMSの続きを書く。
問題ないなーと思ってると今度はSMSだけが強制終了してホーム画面へ。
ここからはどのアプリでも日本語入力しようとするとアプリ強制終了する、という。

しょうがないから復元しようかなーと調べてたらこんな情報があった。

設定 - 一般 - リセット - キーボード変換学習をリセット

これで回復。

よかった。



追記。ソフトウェアアップデートが出てた。

修正点(3.1.3)

  • iPhone 3GSのバッテリーレベル表示の正確性が向上
  • 他社製アプリケーションが起動しない場合がある問題を修正
  • 日本語かなキーボード使用時にアプリケーションがクラッシュする原因となりうる問題を修正

入ってるしー。