我发现在我的任何应用程序中都存在一个常见问题,即用户可能会意外地对同一按钮执行多次单击,从而导致多次执行onClick侦听器逻辑。通常,我对onClickListeners的业务逻辑通常包括启动一个执行HTTP请求并稍后修改UI的重进程AsynTask。
我防止asyntask多次执行的方法是在侦听器方法的开头不能使用按钮,并再次启用它作为onPostExecute的第一个语句。这通常对我有用,或者至少我没有收到任何关于这种情况的问题。
最近,一位同事指出了这种无法启用按钮方法的潜在问题。如下面的代码由两个按钮'+'和' - '组成,快速和替代按下这些按钮会导致应用程序因ArrayOutOfIndex异常而崩溃。
这个事实让我想到了我管理onClickListener事件的并发性的方法,以及是否真的有可能在使用上述方法完成第一个asyntask之前启动第二个asyntask的情况
您有什么建议来处理这种情况?
对于那些建议应用某些逻辑拒绝asctiask的第二次启动直到完成第一个asyntask的建议,是否真的值得将该逻辑应用于按钮执行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>
答案 0 :(得分:1)
可以保证两种方法不会同时尝试更新UI的解决方案是同步UI更新代码。您可以创建一个synchronized方法,也可以创建一个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.
}