【Android】Variable is accessed from within inner class, needs to be final or effectively final

　自作の Android アプリで、画面上のボタンを押したら〇〇変数をOFFにする、というコードを作っていたら Variable 〇〇 is accessed from within inner class, needs to be final or effectively final というエラーが出た。
ONにしたい変数をグローバルにする、という簡単な対策でしたが、Android Studio で確認しました。

# 参考

[Variable is accessed from within inner class, needs to be final or effectively final - stack overflow](https://stackoverflow.com/questions/14425826/variable-is-accessed-within-inner-class-needs-to-be-declared-final#:~:text=You%20can%20declare%20the%20variable,inner%20class%20must%20be%20final.)

# 試した環境
Android Studio Bumblebee | 2021.1.1 Patch 3

# エラーの出る例

　[onClickListenerの実装とその後の処理](https://qiita.com/yukiyamadajp/items/758bcce58801c12cd4d5)などを参考に、画面上のボタンを押したらboolean型変数 `onActive`をOFFにする処理を考えました。
変数の宣言場所としては、`Button buttonStart` や `Button buttonStop` と同列だったらいいかと思って以下のような位置にしました。

```Java:MainActivity.java
public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        Log.d("debug", "onCreate");

        boolean onActive = true;    //  ここではＮＧ

        Button buttonStart = findViewById(R.id.button_start);
        buttonStart.setOnClickListener(new View.OnClickListener() {
            public void onClick(View v) {
                // Do something in response to button click
                Log.d("debug", "button_start");
                synchronized (this) {
                    for (int i = 0; i < 30; i++) {
                        Log.d("debug", " i = " + i);
                        try {
                            wait(1000);
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                        if(!onActive) break;
                    }
                }
            }
        });

        Button buttonStop = findViewById(R.id.button_stop);
        buttonStop.setOnClickListener(new View.OnClickListener() {
            public void onClick(View v) {
                Log.d("debug", "button_stop");
                onActive = false;
            }
        });
    }
```

残念ながら、この位置では、

> Variable 'onActive' is accessed from within inner class, needs to be final or effectively final

が出ます。インナークラスから呼ばれるので、finalな変数にするか、事実上finalになるようにする必要あり、ってことですね。が、ボタンクリックで再代入したいのでfinalでは困ります。

# 解決策１

解決策の１つ目は、onCreate の外で変数を定義する(グローバル変数として宣言する)、です。

```Java:MainActivity.java
public class MainActivity extends AppCompatActivity {

    boolean onActive = true;    //    この位置で宣言

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        Log.d("debug", "onCreate");

		onActive = true;
//      boolean onActive = true;    //  ここではＮＧ

        Button buttonStart = findViewById(R.id.button_start);
        buttonStart.setOnClickListener(new View.OnClickListener() {
            public void onClick(View v) {
                // Do something in response to button click
                Log.d("debug", "button_start");
                synchronized (this) {
                    for (int i = 0; i < 30; i++) {
                        Log.d("debug", " i = " + i);
                        try {
                            wait(1000);
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                        if(!onActive) break;
                    }
                }
            }
        });

        Button buttonStop = findViewById(R.id.button_stop);
        buttonStop.setOnClickListener(new View.OnClickListener() {
            public void onClick(View v) {
                Log.d("debug", "button_stop");
                onActive = false;
            }
        });
    }
```
# 解決策２

Android studioの提案する解決策は

> Transform 'onActive' into final one element array

です。final宣言はするけど、配列にすれば要素の値を変えられる、ということのようですね。Android studio任せで修正してもらうと、こうなりました。

```Java:MainActivity.java

public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        Log.d("debug", "onCreate");

        final boolean[] onActive = {true};    //  Android studio の quick fix
//      boolean onActive = true;    //  ここではＮＧ

        Button buttonStart = findViewById(R.id.button_start);
        buttonStart.setOnClickListener(new View.OnClickListener() {
            public void onClick(View v) {
                // Do something in response to button click
                Log.d("debug", "button_start");
                synchronized (this) {
                    for (int i = 0; i < 30; i++) {
                        Log.d("debug", " i = " + i);
                        try {
                            wait(1000);
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                        if(!onActive[0]) break;    // Android studio の quick fix、[0]付きに替わる
                    }
                }
            }
        });

        Button buttonStop = findViewById(R.id.button_stop);
        buttonStop.setOnClickListener(new View.OnClickListener() {
            public void onClick(View v) {
                Log.d("debug", "button_stop");
                onActive[0] = false;    // Android studio の quick fix、[0]付きに替わる    
            }
        });
    }
```

[Stack Overflow](https://stackoverflow.com/questions/14425826/variable-is-accessed-within-inner-class-needs-to-be-declared-final#:~:text=You%20can%20declare%20the%20variable,inner%20class%20must%20be%20final.) にも、時間のないときに採用できる、必ずしも最高ではない、変わった(？ 原文はfunny answer)対策として紹介されています。

# おまけ

残念ながらこのサンプルは、Startボタンを押したら forループが終了するまで Stop ボタンの処理に移行しないので、Stopボタンを押しても breakでの中断はされません...
あくまでエラー回避のサンプルということでお願いします。