Windows Phone の WebException のハンドリング
Windows Phone の WebRequest で リクエストを送った際にネットワークやサーバ由来のエラーがあると WebException 例外が発生するんですが、この例外のStatusプロパティやResponseプロパティにはクセがあって以下のような現象が発生します。
- 存在するサーバの、存在しないリソースをリクエストすると、例外のStatusはWebExceptionStatus.UnknownErrorだったり WebExceptionStatus.ProtocolError だったりする。例外のResponse にはサーバからの404レスポンスが正しく格納されている。
- 接続できないサーバの、存在しないリソースをリクエストすると、例外のStatusはWebExceptionStatus.UnknownErrorになる。例外のResponse には適当にでっち上げられた404レスポンスが格納されている。
サーバに接続できてもいないのに適当にレスポンスをでっち上げるという実装ポリシーも関心しませんが、そのレスポンスのステータスが404だというのも関心しません。接続できないという現象は別に永続的なものではないはずです。
実際、このレスポンスをそのまま信用すると、呼び出し側としては接続のリトライを早急に諦めるか、サーバが404を返しているにも関わらずリトライを続けることになってしまいます。
続きを読むExpression Blend 4 でカスタムコントロールとそのテンプレートを作ってみる
前回はWPFのスタイルやテンプレートについて書いたので、今回は実際に Expression Blend 4 でカスタムコントロールの作成とテンプレートの編集を行なってみます。
続きを読むWindows Phone アプリのUIレイアウトXAMLから文字列リソースを参照する
普通アプリを書く時は端末の言語設定によってテキストリソースを切り替えられるようにする訳ですが、Windows Phone 7.1 SDK でそれをやるのに多少苦労したのでメモしておきます。
もっといい方法を御存知の方は是非教えて下さい。
続きを読むSharedPreferences と MODE_MULTI_PROCESS がイマイチよろしくない件
運悪くAndroidで複数プロセスのアプリを作ったり、アプリ間で SharedPreferences を相互参照するハメになってしまった場合に役に立つ…いや、たぶん立たないメモ。
Context#MODE_MULTI_PROCESS フラグはどのように使われているか
このフラグはContext#getSharedPreferences(String name, int mode) の第二引数に設定するもので、API Level 11で設定された。Context | Android Developersではこう説明されている。
SharedPreference loading flag: when set, the file on disk will be checked for modification even if the shared preferences instance is already loaded in this process. This behavior is sometimes desired in cases where the application has multiple processes, all writing to the same SharedPreferences file. Generally there are better forms of communication between processes, though.
This was the legacy (but undocumented) behavior in and before Gingerbread (Android 2.3) and this flag is implied when targetting such releases. For applications targetting SDK versions greater than Android 2.3, this flag must be explicitly set if desired.
実際にこのフラグが参照されているのは ContextImpl#getSharedPreferences(String name, int mode) の中だけだ。他の箇所では一切参照されていない。
@Override public SharedPreferences getSharedPreferences(String name, int mode) { SharedPreferencesImpl sp; synchronized (sSharedPrefs) { sp = sSharedPrefs.get(name); if (sp == null) { File prefsFile = getSharedPrefsFile(name); sp = new SharedPreferencesImpl(prefsFile, mode); sSharedPrefs.put(name, sp); return sp; } } if ((mode & Context.MODE_MULTI_PROCESS) != 0 || getApplicationInfo().targetSdkVersion < android.os.Build.VERSION_CODES.HONEYCOMB) { // If somebody else (some other process) changed the prefs // file behind our back, we reload it. This has been the // historical (if undocumented) behavior. sp.startReloadIfChangedUnexpectedly(); } return sp; }
SharedPreferencesImpl#startReloadIfChangedUnexpectedly は内部で hasFileChangedUnexpectedly() を呼び出していて、そこでは前回から自分が書き込みを行ったかどうか確認したり、ファイルのmtimeとsize が前回ロードしたものと同じかどうか確認したりしている。
まとめるど、このフラグは「getSharedPreferences を呼び出した時にmtimeとsizeを見て適当にロードし直す」というものだ。他の効果は一切ないし、自プロセスが書き込みを行なっていた場合もロードし直さないし、単に0を1に変えるような変更を前回のロードから1秒未満に行った場合もロードし直さない。端末の時計が補正された場合なんかもきっとうまくないケースがあるだろう。穴だらけとしか思えない実装なのだが、今後改善される可能性もあるのでフラグの存在自体は尊重するべきかもしれない。
ついでに言うと、もしあなたが取得した SharedPreferences をどこかクラス内のフィールドに保存しているのなら、それは再利用の際に別プロセスからのアクセスをチェックしていないまずいコードだということになる。
…困ったことに、PreferenceManager がまさにそういう実装になっている。
嫌な汗が流れてきたが、まだ話は終わらない。
では PreferenceManager を使っていないアプリでは MODE_MULTI_PROCESS をちゃんと設定して、かつ頻繁にgetSharedPreferencesを呼び出していれば問題はないのかというと、そんなことはなかった。