2012-10-1
android: 異なるスコープ(ブロック、スレッド)間での値の受け渡し
症状:
異なるスコープ(ブロック、スレッド)間での値の受け渡しができない。
例えば、以下のようなコードを書いたとする。
int = a final AlertDialog.Builder adb = new AlertDialog.Builder(this); adb.setMessage("SELECT") .setPositiveButton("YES", new DialogInterface.OnClickListener() { public void onClick(DialogInterface dialog, int which) { a = 100; } }) .setNegativeButton("NO", new DialogInterface.OnClickListener() { public void onClick(DialogInterface dialog, int which) { a = 200; } }) .show(); TextView tv = (TextView) findViewById(R.id.textview1); tv.setText("Result: " + a);
ここで、はじめの変数 a の宣言を final にすると alertdialog ブロック内の a に値を代入できず、かといって static にすると今度は a が参照できない、と言われてしまう。
- final にした場合:
- エンクロージング型に定義されているため final のローカル変数 a を代入することができません
- static にした場合:
- 異なるメソッドで定義されたインナー・クラス内で非 final 変数 a を参照できません
alertdialog ブロック内で TextView に値を格納して、あとから取り出すようにすると、コンパイルは通り、実行するとダイアログは表示され、選択もできるが、結果は 100 でも 200 でもなくつねに 0 …。全く解決になってない。
解決法:
こいつは今まで(つっても1か月半くらいだけど)で一番手強かった。
final 以外は受け付けないという強硬な奴と、static でなければ言うことを聞かないという意地悪な奴とに板挟みになり、まさに無間地獄でテンパって詰んでいる状態。
解決の糸口が見えずインターネットの海を彷徨い続けた挙句、やっと愚鈍人 - HandlerとMessage - 別スレッドでのGUI操作に漂着した。あなたは決して愚鈍な方などではありませんよ。まさに救世主です!
インターネットの海をひたすら漂っている間、intent、クラス変数、setter / getter、message、bundle、handler と様々な島影が見ゑては消ゑ、消ゑてはまた見ゑ(どうでもいいけど、「消ゆ」も「見ゆ」も古文では同じヤ行下二段活用)…
もとい、結果こうなりました。
(eclipse の Handler のところに出ている黄色の波線、This Handler class should be static or leaks might occur. は、とくに「リーク」という言葉が気になるけれど、だからと言って static にしても同じこと言うし、また final ぢゃなきゃだめとか言うので、とりあえず)
final Handler handler = new Handler() { public void handleMessage(Message msg) { Bundle bd = msg.getData(); TextView tv = (TextView) findViewById(R.id.textview1); tv.setText("Result: " + bd.getInt("a")); } }; final AlertDialog.Builder adb = new AlertDialog.Builder(this); adb.setMessage("SELECT") .setPositiveButton("YES", new DialogInterface.OnClickListener() { public void onClick(DialogInterface dialog, int which) { Message msg = new Message(); Bundle bd = new Bundle(); bd.putInt("a", 100); msg.setData(bd); handler.sendMessage(msg); } }) .setNegativeButton("NO", new DialogInterface.OnClickListener() { public void onClick(DialogInterface dialog, int which) { Message msg = new Message(); Bundle bd = new Bundle(); bd.putInt("a", 200); msg.setData(bd); handler.sendMessage(msg); } }) .show();