2013/05/18

Android:AlermManagerとネットワークアクセスを組み合わせる際のマナー


AlermManagerを使用すれば、任意の時間に処理を実行することができます。
Android Developers : AlermManager.set (int, long, PendingIntent)

時間指定の方法には次のタイプがあります。
  • 絶対時間で指定するRTCタイプ
  • 相対時間で指定するELAPSED_REALTIMEタイプ
○○時○○分に処理したい場合はRTC、今から○○分後に処理したい場合はELAPSED_REALTIMEといった具合です。

どちらのタイプを使用するかはアプリ要件に左右されますが、もしあなたのアプリケーションが
 タイマー発火を契機にネットワークアクセスする仕様、かつRTCタイプを採用しようとしている
のであれば一度次のことを検討してみましょう。
この処理/仕様は、相対時間指定(○○分後)で実現可能か。あるいはそのように処理/仕様を変更できないか。
もし、この問いに"YES"であるならRTCタイプではなくELAPSED_REALTIMEタイプを採用するのがよい選択です。

これには次の理由があります。
絶対時間指定の場合、アプリからのネットワークアクセスが集中する
数百万(あるいは数千万!)DLされたアプリが、特定の時間に一斉にネットワークアクセスを開始すればどうなるかは容易に想像できますね。

ネットワークアクセスのタイミングをRTCタイプでしか実現できないのであれば、
「端末によって絶対時間指定の時刻をずらせないか」
を検討するのも良い案です。
ネットワークアクセス時刻を設定/初期化する際に、分散の大きい乱数でも加えてあげればネットワークアクセスも分散されます。

重要なのは"ネットワークアクセスを集中させないこと"です。
ELAPSED_REALTIMEタイプを採用したからといって"AM8:00"までの時間差分を指定しては問題の解決にはなりません。

この手の話題は"アプリのマナー"に関わる部分です。
マナーを守るためにはソースコードの修正が必要かもしれません。
もしかすると、あなたのアプリのパフォーマンスがほんのちょっぴり低下するかもしれません。
適用したからといって、ユーザレビューの評価点にはそれほど影響しないでしょう。
しかし、こういったマナーを守るアプリが増えれば、きっとその反対よりは良い世の中になりそうです。
プロとして"気の利いたアプリ"を作っていきたいものですね。

以上です。
2013/05/15

Android:商用署名(キーストア)をデバッグ用に変更する手順

チームでAndroidアプリ開発をする上で
 「複数人で開発する際に、デバッグキーを共有したい
という方は多いでしょう。

また、セキュリティ上危険ではありますが、
 「商用署名を一時的にデバッグキー(デバッグ署名)として運用したい
なんてこともあるかもしれません。

これらを、Eclipse/ADTのデバッグ機構で実現するには少しコツが入ります。

本稿では、Eclipse/ADTで商用署名(キーストア)をデバッグキーとして運用するために必要な
  商用署名(キーストア)をデバッグ用に変更する手順
についてまとめました。

一般的に、商用署名は厳重に管理すべきものであり、無闇に複製/編集すべきものではありません。
商用署名を直接編集するなら、破損しても復旧できるようにバックアップは必ず取りましょう。
本稿で使用するパスワードはテスト用です。本番環境ではセキュアなパスワードを指定してください。

今回の検証環境
---
 OS: WindowsXP
 Eclipse: 3.7.1(Indigo)
 ADT: 21.1
---

検証で使用するキーストア情報。
---
 キーストアファイル名
   yuki.keystore
 キーストアパスワード
   yukipass
 エイリアス
   yukialias
 エイリアスパスワード
   yukialiaspass
---

●Eclipseにデバッグキーを設定する方法

下記の項目から設定します。
[Window]>[Preferences]>[Android]>[Build]

図の①に、使用するデバッグ用キーストアファイルのパスを指定します。
ここに予め作成しておいたyuki.keystoreを指定すると...
次のエラーで弾かれてしまいます。

エラー:Keystore was tampered with, or password was incorrect


「キーストアが改ざん、あるいはパスワードが正しくない」旨のエラーです。
Eclipse/ADTで使用できるデバッグキー(キーストア)情報は下記の条件を満たす必要があります。
---
 キーストアファイル名
   任意
 キーストアパスワード
   android
 エイリアス
   androiddebugkey
 エイリアスパスワード
   android
---

つまり、デバッグキーとして使用するキーストアパスワードは
  android
である必要があるのです。
yuki.keystoreのキーストアパスワードは現在"yukipass"であるため、デバッグキーとして使用できません。

それでは、yuki.keystoreをデバッグキーとして使用するためにキーストア情報を変更します。


●キーストアのパスワードを変更する方法

$keytool -storepasswd -keystore yuki.keystore
キーストアのパスワードを入力してください: <現在のキーストアパスワード>
新規 keystore password: <新しいキーストアパスワード> # ★ android を指定
新規 keystore password を再入力してください: <新しいキーストアパスワードを再入力>
Windowsなら下記を実行(JDK_HOMEのパスは自環境用に読み替えて下さい)
C:\Program Files\Java\jdk1.6.0_12\bin>keytool.exe -storepasswd -keystore yuki.keystore
このキーストアをEclipseに設定してもダメです。
次のエラーで弾かれます。

エラー:Unable to find debug key in keystore!

「キーストア内にデバッグキーが見つからない」旨のエラーです。
デバッグキーとして使用するエイリアスは
  androiddebugkey
である必要があります。


●エイリアスを変更する方法

$keytool -changealias -alias yukialias -keystore yuki.keystore
destination の別名を入力してください: <新しいエイリアス> # ★ androiddebugkey を指定
キーストアのパスワードを入力してください: <現在のキーストアパスワードを入力> # 既に android に変更済なら android で
<yukialias> の鍵パスワードを入力してください。 <現在のエイリアスパスワードを入力>
Windowsなら下記を実行。
C:\Program Files\Java\jdk1.6.0_12\bin>keytool.exe -changealias -alias yukialias -keystore yuki.keystore
このキーストアをEclipseに設定してもまだダメです。
↓のエラーで弾かれます。

エラー:Cannot recover key

「キーを復元できない」旨のエラーです。
デバッグキーとして使用するエイリアスパスワードもやはり
  android
である必要があります。


●エイリアスパスワードを変更する方法

keytool -keypasswd -alias androiddebugkey -keystore yuki.keystore
キーストアのパスワードを入力してください:
<androiddebugkey> の鍵パスワードを入力してください。 <現在のエイリアスパスワード>
新規 <androiddebugkey> の鍵のパスワード: <新しいエイリアスパスワード>  # ★ android を指定
新規 <androiddebugkey> の鍵のパスワード を再入力してください:<新しいエイリアスパスワードを再入力>
Windowsなら下記を実行。
C:\Program Files\Java\jdk1.6.0_12\bin>keytool.exe -keypasswd -alias androiddebugkey -keystore yuki.keystore
このファイルをEclipseに指定すれば見事デバッグキーとして使用できます。


●おわりに

本稿では、わざと数回に分けてキーストア情報を変更しましたが、keytoolのオプションを工夫すればコマンドをまとめることができます。
keytoolコマンドに限りませんが、(テスト目的を除き)安全でないシステムで実行する場合は、コマンドやスクリプトにパスワード情報を含めてはいけません。コマンドやスクリプトはヒストリとして蓄積され、パスワードが漏洩する可能性があります。
オプションで必要なパスワードを指定しなかった場合は、パスワードの入力を求められるのでそこで入力するようにします。

参考:
keytool - 鍵と証明書の管理ツール
Android Developers - アプリケーションへの署名

以上です。

2013/05/13

Android:Notification領域のmoreアイコン

Notificationアイコンが通知領域に収まらない時、オーバフローを表現するために"more icon"が表示されます。

しかし、Android標準の不具合によりこの機能はうまく動作しません。
#各キャリア端末ではこの不具合は改修されて再現しませんが、Nexus7等の標準機であれば確認できます。

エミュレータで動作確認する際に"more icon"が表示されない場合はこの不具合である可能性があります。
不具合のfixソースはandroid.gitへ既にコミットされています。

commit cd231432ff16cb35aa08cd7b9ca801d26bef261f
"[+&gt;" more icon was never shown in PhoneStatusBar

"[+&gt;" more icon was never show in status bar because
the member variable for this icon was not initialized
from resources. This fix enables "[+&gt;" icon to appear
in status bar when the number of indications in status
bar becomes large.
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
@@ -415,6 +415,7 @@
     mSystemIconArea = (LinearLayout) mStatusBarView.findViewById(R.id.system_icon_area);
     mStatusIcons = (LinearLayout)mStatusBarView.findViewById(R.id.statusIcons);
     mNotificationIcons = (IconMerger)mStatusBarView.findViewById(R.id.notificationIcons);
+    mMoreIcon = mStatusBarView.findViewById(R.id.moreIcon);
     mNotificationIcons.setOverflowIndicator(mMoreIcon);
     mStatusBarContents = (LinearLayout)mStatusBarView.findViewById(R.id.status_bar_contents);
     mTickerView = mStatusBarView.findViewById(R.id.ticker);
from android.git

以上です。
2013/05/07

Android:toLowerCase/toUpperCaseに注意

最近のLintは賢くなって、String.toLowerCaseやtoUpperCaseをLocale引数無しで使用すると警告を出すようになりました。
Locale指定がないと、予期せぬ事態を招く可能性があるからです。
本稿はLocaleについて少し触れてみたいと思います。

突然ですが、下記の評価式はtrue or falseどちらを返すでしょうか。
"YUKI".toLowerCase().equals("yuki")
答えは true 、といきたいところですがfalseにもなり得るのです。

評価結果がfalseとなるのは、たとえばデバイスの言語設定がトルコ語であった場合です。


トルコ語では"I"の小文字は"i"ではありません。
そのため、"YUKI"を小文字化しても"yuki"にはならないためfalseと評価されます。
つまり、Localeを指定しないString型の文字列比較では、言語設定(Locale)によって評価結果が変化する可能性があるということです。
これを回避するには、ロケールに依存しないようにtoLowerCase(Locale.ENGLISH)を使用します。

String.toLowerCase(Locale)リファレンス
http://docs.oracle.com/javase/6/docs/api/java/lang/String.html#toLowerCase(java.util.Locale)


それでは、下記の評価式はtrue or falseどちらを返すでしょうか。
"YUKI".equalsIgnoreCase("yuki")
答えは、ロケールに関係なく true が返されます。

先述のtoLowerCaseの件を考えると、equalsIgnoreCase(String)もロケールに依存するように思えます。
しかし、equalsIgnoreCaseの評価はロケールに依存しないのです。

これは、equalsIgnoreCaseで用いられるのは、String.toLowerCase(), String.toUpperCase()ではなく、
Character.toLowerCase(char), Character.toUpperCase(char)であるためです。

Characterクラスが持つそれぞれのメソッドはロケールに依存したケースマッピングをサポートしていません。
そのため、言語設定がトルコ語になっても評価に影響することがないのです。

String.equalsIgnoreCase(String)リファレンス
http://docs.oracle.com/javase/6/docs/api/java/lang/String.html#equalsIgnoreCase(java.lang.String)


i18nの敷居の高さが伺える事例でした。
ただ、下記のような状況があり得るというのには多少違和感を感じますね。
 "YUKI".toLowerCase().equals("yuki")=false
 "YUKI".equalsIgnoreCase("yuki")=true

その他参考:
Local:
http://developer.android.com/reference/java/util/Locale.html#default_locale

以上です。