Android4.0でdo-whileが実行されない場合がある
Android4.0端末でのみ変数の値がおかしくなるバグを追いかけていて気がつきました。
端的に言うと、
Android4.0端末で、ネストされたループ内のdo-while文が実行されない場合がある
「実行されない場合がある」なんてボンヤリした表現になってますが、原因はちょっとよくわかりません。
一応条件っぽいのを挙げておきます。
- Android4.0端末である
- for文 -> for文 -> do-while文 の入れ子になっている
- 二番目のfor文内でメソッド呼び出しをしていない
- do-while文内でメソッド呼び出しをしていない
次のコードで、実機、シミュレータ共に確認できました。
public class LoopTestActivity extends Activity { @Override public void onCreate(final Bundle savedInstanceState) { super.onCreate(savedInstanceState); final LinearLayout layout = new LinearLayout(this); setContentView(layout); final Button button = new Button(this); button.setText("ボタンを押したらループが走るよ"); button.setOnClickListener(new OnClickListener(){ @Override public void onClick(final View view) { loop(); } }); layout.addView(button); } private void loop(){ for(int j = 0; j < 100; j++){ // 下のfor文をアホみたいに100回繰り返す。 int counter = 0; // 次のfor文の外でsysoutを呼びたいので、ここで宣言しておく。 for(int i = 0; i < 1; i++){ // ここでの繰り返しは1回でも再現する。 counter = 0; do { counter++; // ここが実行されない } while (counter < 5); } if(counter != 0) System.out.println("counter=" + counter); // counter=5 のはず。 if(counter == 0){ System.err.println("counter=" + counter); break; // エラーが出たらすぐわかるように break する。 } } } }
ボタンをぽちぽちしながらlogcatを見てると、結構な頻度で counter=0 が出力されます。
一番外側のfor文(この例では100回回してるやつ)は、私の環境では大体8回くらいからでもcounter=0の出力を得ました。
内側のfor文は1回回すだけでも再現します。
do-whileかその親のスコープ内でなんらかのメソッドを呼んでいると、正常に処理が進みます。
上記コードのsysoutがスコープ外にあるのはそのためです。
次のように、何もしないメソッドを呼んでも処理が走るようになりました。
private void doNothing(){ }
for(int i = 0; i < 1; i++){ counter = 0; // doNothing(); // ここでもいい do { doNothing(); // メソッドを呼び出せば処理される counter++; } while (counter < 5); }
また、do-while文をfor文やwhile文に変えても再現しなくなります。
再現するまんまのapkをデバッグモードで実行しても再現しなくなりました。
なんだかよくわかりませんが、JITコンパイラの最適化方式が4.0で変わったんでしょうかね。
そうそう出くわす状況でもなさそうですが、参考にしていただければ幸いです。