2

在我的任何应用程序中,我发现一个常见问题是用户可能会不小心对同一个按钮执行多次单击,从而导致 onClick 侦听器逻辑的多次执行。通常,我对这些 onClickListeners 的业务逻辑通常包括启动一个执行 HTTP 请求并随后修改 UI 的繁重进程 AsynTask。

我防止异步任务多次执行的方法是在侦听器方法的开头禁用按钮并再次启用它作为 onPostExecute 的第一条语句。这通常对我有用,或者至少我没有收到任何关于这种情况的问题。

最近,一位同事向我指出了这种无法启用按钮方法的潜在问题。如下面的代码所示,由两个按钮“+”和“-”组成,快速和交替按下这些按钮会导致应用程序因 ArrayOutOfIndex 异常而崩溃。

这一事实让我开始思考我管理 onClickListener 事件并发性的方法,以及是否真的有可能在使用上述方法完成第一个 asyntask 之前启动第二个 asyntask 的情况。

您对处理这种情况有什么建议?

对于那些建议应用一些逻辑来拒绝第二次启动 asyntask 直到第一个 asyntask 完成的建议,是否真的值得将该逻辑普遍应用于按钮执行 http 请求的常用应用程序?

崩溃活动.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);
}
}

主要的.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 回答