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を呼び出していれば問題はないのかというと、そんなことはなかった。

続きを読む

Androidアプリからのファイルパーミッションの制御

アプリ間のファイルアクセスの許可/禁止には unix のファイルパーミッションの仕組みが使われています。

Androidではアプリごとにuidとgidが割り当てられていて、パーミッションのうち、otherのread,write,execute ビットを変更することで他アプリからのアクセスを許可/禁止することができます。

パーミッションの設定はネイティブコードならchmod(2)を使うんですが、Javaからだとそのまま呼び出せるようにはなっていません。

そこで今回はアプリからファイルパーミッションを制御するにはどのようなAPIを使えばよいか確認してみます。

続きを読む

Windows Live ID が停止されてる件でたらいまわし続行中

  • SkyDrive Explorerで手持ちの画像を3GBくらいupしてみる
  • 原因はわからないがアカウント停止される
  • Webからサインインできない
  • カスタマー サポートにお問い合わせください。 いくつかの質問をさせていただき、お客様のアカウントが安全であるかをご確認いたします。」
  • MSKKのサイトで問い合わせ先は「Windows Live はコミュニティでのサポートをご利用ください。」
  • Windows Live Solution Center はサインインしないと使用できない
  • 別件でMSの人と話したら「別のメールアドレスでLive IDを作って問い合わせるといいよ」
  • フォーラムに投稿したら「SkyDriveの迷惑行為」の投稿先を伝えられる
  • SkyDriveの迷惑行為 のフォームに書いたら
現在、当サポート窓口では、サーバー側にて問題が発生しているとの
報告を受けておりませんため、本件につきましては、恐れ入りますが、
担当部署に報告し、確認をさせていただきたく存じます。

…どこまで続くんだコレ

Bitmapのカドを丸くする

「アイコン画像を角丸にしてくれ」 とか、割とよくある話だと思うんです

	public static Bitmap clip_radius(Bitmap src,int out_w,int out_h,float radius_x,float radius_y){
		// 入力と出力の矩形
		Rect rect_in = new Rect(0,0,src.getWidth(),src.getHeight());
		RectF rect_out = new RectF(0,0,out_w,out_h);
		// 出力ビットマップとその描画オブジェクトを作成する
		Bitmap dst = Bitmap.createBitmap(out_w,out_h,Bitmap.Config.ARGB_8888);
		Canvas c = new Canvas(dst);
		Paint paint = new Paint();
		paint.setAntiAlias(true);
		paint.setFilterBitmap(true);
		// ゼロフィルして
		c.drawARGB(0,0,0,0);
		// 角丸矩形を書いて
		paint.setColor(0xffffffff);
		c.drawRoundRect (rect_out,radius_x,radius_y,paint);
		// 転送モードを工夫してから
		paint.setXfermode(new PorterDuffXfermode(Mode.SRC_IN));
		// ビットマップをコピー スケーリングあり
		c.drawBitmap(src,rect_in,rect_out,paint);
		//
		return dst;
	}

注意点

Bitmapはネイティブヒープというリソースを消費していて、これが解放されるのはrecycleが呼ばれるか、finalizeが呼ばれるかした時です。ただしjavaではファイナライズが呼び出されることは保証されていません。

purgeable bitmap の場合はOS側が適当なタイミングでネイティブヒープ側のリソースを管理してくれます。BitmapFactory.decode* で作った場合かビットマップか、レイアウトXMLで指定したdrawable resourceから自動的にbitmap drawableが生成された場合はビットマップをpurgeable bitmap にすることができます。参照と解放がそれほど頻繁でない場合は、OSによるpurgeに頼っても特に問題はありません。

Bitmap#createBitmap や Bitmap#copy で作ったビットマップはpurgeable にはできないので、自動ではネイティブヒープの解放は行われません。参照されなくなったビットマップは必ずUI階層からの参照を切って明示的にrecycle()する必要があります。地味に管理と検証が面倒くさいです。