sola's note -2ページ目

sola's note

日ごろのメモを書いていきます。何か新しいものを作るために。

写真共有アプリ作りました。
https://play.google.com/store/apps/details?id=net.jp.found.mysnap

sola's note
sola's note

端末内の写真を読み込み表示する写真ビューワです。
指定した期間に撮影された写真だけを表示する機能や、
アプリ同士で写真をそのまま共有する機能を持っています。

動作確認端末は
SO-02C(Xperia acro)、P-05D(Disney Mobile)、P-01D、SC-04D(GALAXY NEXUS)
です。
他の端末も動作しているものもありますが、テストとして行っていないのでまだ列挙致しません。
※1年以上前に発売された端末だと、初回の表示が少々遅い場合があります。ご了承下さい。

写真にメッセージを付けられるので、
メールの代わりに写真+メッセージなんていいかもしれません。

ポイントは「写真をそのまま共有できること」です。
メールで写真を送ると画質が劣化しがちですが、
このアプリではメールのように画質が劣化したりすることはありません。
是非試してみてください。

何かありましたらこちらでもリンク先のメールアドレスでもご意見受け付けております。
Android開発において、画像情報を取得する方法は大きく2通り。(だと思ってる。)

1.画像ファイルのパスを指定して、直接取得する。

2.画像ファイルのIDを指定して、ContentResolverを通して取得する。

使うメソッドはそれぞれ以下の通り。
2のパターンは、取得する画像に応じてメソッドが複数存在します。

1.
Bitmap bmp = BitmapFactory.decodeFile(String path);

2.
Bitmap bmp = MediaStore.Images.Thumbnails.getThumbnail(ContentResolver cr, long origId, int kind, Options options);
Bitmap bmp = MediaStore.Images.Media.getBitmap(ContentResolver cr, Uri url);

(※上記メソッドは代表例で、引数の数が異なるものが存在します。(オーバーロードされてます。))

二つの違いは大きく考えると、ContentProviderを使うか、使わないかにあります。
ContentProviderはAndroid端末内のアプリケーション同士でデータを共有する際に用いるものなので、
結果的に上記の二つはアプリケーション独自でファイルを取得するか、共有して取得するかの違いとなりそうです。


そして、ここからは検証していないので、あくまで体感上の話となりますが、

1はアプリで完全にコントロール出来ますが、遅いです。
アプリでデータを取得していなければ取得する必要がありますし、最終的に破棄しないとアプリがバンバン落ちます。

2はリソースが端末上の共有となりますので、アプリによる完全なコントロールは出来ませんが、早いです。
アプリでデータを取得していなくても、共有で持っている場合があるので、
一度読み込んだ事がある画像であればそれなりの速度で読み込みます。

但し、読み込むリソースが大量になると、2の方が端末のVMが重くなるような感じがしました。
なんというか読み込みスピードは上がりますが、落ちやすくなっているような気がします。
とは言っても、2Mbyteくらいの画像1000ファイルをListViewとかで読みこんで馬鹿みたいにスクロールした場合…なので、
小さなサイズのファイルだとそこまで感じないかもしれません。

この点の検証も必要ですが、次回は各メソッドの引数の用意の仕方について書こうと思います。
定期的な処理を実行する時に、
AlarmManagerとBroadcastReceiverを使ってバックグラウンド処理を実装すると思いますが
AlarmManager側のバグっぽいものにぶつかってハマりました。

以下簡単な設定です。

・HogeActivity→呼び出し用Activity
・HogeReceiver→BroadcastReceiverを拡張した受け取りクラス

--呼び出し側ソース(一部)--

Intent intent = new Intent(HogeActivity.this,
        HogeReceiver.class);
PendingIntent sender = PendingIntent.getBroadcast(
        HogeActivity.this, 0, intent,
        PendingIntent.FLAG_UPDATE_CURRENT);

//初回実行時間の設定
Calendar calendar = Calendar.getInstance();
calendar.setTimeInMillis(System.currentTimeMillis());
calendar.add(Calendar.SECOND, 10);

AlarmManager alarmManager = (AlarmManager) getSystemService(Context.ALARM_SERVICE);
alarmManager.setInexactRepeating(AlarmManager.RTC, calendar.getTimeInMillis(),
        AlarmManager.INTERVAL_HOUR, sender);

こんな感じにしたのですが、
デバッグしてみると、HogeReceiver#onReceiveがまあ呼ばれない

色々試した結果
バグor失敗率急上昇の要因はAlarmManager#setInexactRepeatingの第三引数に
AlarmManager.INTERVAL_HOUR(または3600000)を設定していた事と判明。

これが、デバッグ時限定なのかというのは確認していないので分かりません。
Android Developersのリファレンスを見てみると
次のように書いてあります。

--引用--
interval in milliseconds between subsequent repeats of the alarm. If this is one of INTERVAL_FIFTEEN_MINUTES, INTERVAL_HALF_HOUR, INTERVAL_HOUR, INTERVAL_HALF_DAY, or INTERVAL_DAY then the alarm will be phase-aligned with other alarms to reduce the number of wakeups. Otherwise, the alarm will be set as though the application had called setRepeating(int, long, long, PendingIntent).
--引用ここまで--

まあ、ざっくり言えば
「特定の時間(※1)を起動間隔として設定すると、他のアラーム設定と一緒に扱うよ」
という事らしいのですが、これが関係しているのか分かりませんが、

第三引数のAlarmManager.INTERVAL_HOURを上記で指定されている秒数以外である
AlarmManager.INTERVAL_HOUR + 1にしたらデバッグ時に動作が確認出来ました。

ただ、AlarmManagerには
setInexactRepeating()以外にsetRepeating()という、
殆ど同じ動作をするものがあって、違いが起動時間間隔の正確さ(0秒丁度に起動するかどうか)
みたいなので、

setInexactRepeating()に"特定の時間"以外を指定して使う

という事をするならば、
多分setRepeating()の方が無駄が無いのではないでしょうか。
ちなみにsetRepeating()に変更するとAlarmManager.INTERVAL_HOURでも問題なく動きました。

ちなみにのちなみにですが、本件「バグかどうか」はわかっておりません。
1msec違うだけで指定した時間にブロードキャストが実行されなかったので、ほぼそうだと思いますが
ソースファイルは解析していないので悪しからず。

※1 特定の時間
INTERVAL_FIFTEEN_MINUTES, INTERVAL_HALF_HOUR, INTERVAL_HOUR, INTERVAL_HALF_DAY, or INTERVAL_DAY
を指していて、
15分、30分、1時間、12時間、24時間が該当するようです。