2

ユーザーが誤って同じボタンを複数回クリックして、onClick リスナー ロジックが複数回実行される可能性があるという、私のアプリの共通の問題を発見しました。通常、これらの onClickListeners のビジネス ロジックは通常、HTTP 要求を実行し、後で UI を変更する負荷の高い AsynTask の起動で構成されます。

非同期タスクの複数回の実行を防ぐ方法は、リスナー メソッドの先頭にあるボタンを無効にし、onPostExecute の最初のステートメントとして再度有効にすることでした。それは一般的に私にとってはうまくいったか、少なくともこの状況に関して何の問題も受けていません.

最近、同僚が、このボタンを無効にする方法の潜在的な問題を指摘してくれました。2 つのボタン '+' と '-' で構成される以下のコードに示すように、これらのボタンを素早く交互に押すと、ArrayOutOfIndex 例外によってアプリケーションがクラッシュします。

その事実により、onClickListener イベントの同時実行を管理する方法と、前述の方法を使用して最初の ayntask のファイナライズの前に 2 番目の ayntask が起動される可能性がある状況が本当に可能かどうかについて考えさせられました。

この状況に対処するための提案は何ですか?

最初の非同期タスクが完了するまで、非同期タスクの 2 回目の起動を拒否するロジックを適用することを推奨する提案については、ボタンが http 要求を実行する一般的に使用されるアプリケーションにそのロジックを一般的に適用する価値はありますか?

CrashActivity.java

public class CrashActivity extends Activity {

private int mNumbers[] = { 1, 2, 3, 4, 5 };
private int position = 0;

/** Called when the activity is first created. */
@Override
public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.main);

    final TextView result = (TextView) findViewById(R.id.resultTextView);

    final Button minusBtn = (Button) findViewById(R.id.minus_button);
    final Button plusBtn = (Button) findViewById(R.id.plus_button);

    minusBtn.setOnClickListener(new View.OnClickListener() {
        public void onClick(View v) {
            minusBtn.setEnabled(false);
            plusBtn.setEnabled(true);

            result.setText("" + mNumbers[--position]);

            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }

            minusBtn.setEnabled((position > 0));
        }
    });

    plusBtn.setOnClickListener(new View.OnClickListener() {
        public void onClick(View v) {
            plusBtn.setEnabled(false);
            minusBtn.setEnabled(true);

            result.setText("" + mNumbers[position++]);

            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            plusBtn.setEnabled((position <= 4));
        }
    });

    minusBtn.setEnabled(false);
}
}

main.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:orientation="vertical" >

<Button
    android:id="@+id/minus_button"
    android:layout_width="fill_parent"
    android:layout_height="wrap_content"
    android:text="-" />

<Button
    android:id="@+id/plus_button"
    android:layout_width="fill_parent"
    android:layout_height="wrap_content"
    android:text="+" />   

<TextView
    android:id="@+id/resultTextView"
    android:layout_width="fill_parent"
    android:layout_height="wrap_content"
    android:text="hello stackoverflow!" />

</LinearLayout>
4

1 に答える 1

1

両方の方法が同時にUIを更新しようとしないことを保証する解決策は、UI更新コードを同期することです。同期メソッドを作成することもObject、コードブロックのロックとして機能するメソッドを作成することもできます。例:

public synchronized void updateUI() {
    // ...
}

また

private Object mLock = new Object();

public void updateUI() {
    // ...
    synchronized (mLock) {
        // Critical code here.
    }
    // ...
}

これを拡張して、タスク1がタスク2の前に完了し、タスク3の前に完了するようにするなど、どういうわけか、どちらが最初に開始されたかを追跡する必要があります。注:この例では、同期はUIスレッドで発生します。

private List<AsyncTask> mRunningTasks = new ArrayList<AsyncTask>();

public void onClick(View v) {
    v.setEnabled(false);
    if (v == task1View) {
        Task1 task = new Task1();
        mRunningTasks.add(task);
        task.execute();
    }
    else if (v == task2View) {
        Task2 task = new Task2();
        mRunningTasks.add(task);
        task.execute();
    }
    // ...
}

// Copy and paste for each necessary task.
private Task1 extends AsyncTask<Void, Void, Void> {
    protected Void doInBackground(Void... v) {
        // Run task 1 code here.
        while (this != mRunningTasks.get(0)) {
            wait(1000);
        }
        return v[0];
    }

    protected void onPostExecute(Void v) {
        updateUI();
        mRunningTasks.remove(0);
        task1View.setEnabled(true);
    }
}

public void updateUI() {
    // UI update code here.
}
于 2012-05-30T05:42:24.270 に答える