Android AsyncTask 中的 ConcurrentModificationException
ConcurrentModificationException in Android AsyncTask
我有一个奇怪的问题,希望我能解释清楚。我的应用程序有两个活动 - MainActivity 和 SearchActivity。我在 MainActivity 上有一个按钮,它触发从设备上的数据库上传到我的网络服务器上的远程数据库。如果我在第一次启动应用程序时单击按钮,没问题,工作正常。如果我切换到 SearchActivity,什么也不做,然后切换回去,然后尝试按钮,应用程序会崩溃并出现 ConcurrentModificationException。
我有一个 AsyncTask,它发送本地数据库的内容(已经从数据库中取出并通过参数作为 ArrayList 发送到线程)。我花了几个小时调试它,但仍然无法弄清楚它在哪里。任何建议将不胜感激。
这是按下按钮时触发的代码,用于从单独的 Databaser 线程请求数据库的内容
Button btnRemoteSync = (Button)findViewById(R.id.btnSync);
btnRemoteSync.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Intent startUpload = new Intent(getString(R.string.broadcast_search_database));
startUpload.putExtra("type-id",1);
LocalBroadcastManager.getInstance(getApplicationContext()).sendBroadcast(startUpload);
}
});
这是 BroadcastReceiver 中的代码,它从数据库中获取每个响应并将它们添加到自定义 ResponseObjects 的 ArrayList 中。当数据库线程发送 bssid 值 DONE 时,AsyncTask 启动,ArrayList 作为参数传入。
@Override
public void onReceive(Context context, Intent intent) {
String bssid = intent.getStringExtra(getString(R.string.data_bssid));
if (bssid.equals("DONE")) {
RemoteDatabaseUploader rdb = new RemoteDatabaseUploader(getApplicationContext());
rdb.execute(databases);
} else {
databases.add(new ResponseObject(getApplicationContext(),
bssid,
intent.getStringExtra(getString(R.string.data_ssid)),
intent.getStringExtra(getString(R.string.data_capabilities)),
intent.getIntExtra(getString(R.string.data_level), 0),
intent.getIntExtra(getString(R.string.data_frequency), 0),
intent.getStringExtra(getString(R.string.data_timestamp)),
intent.getDoubleExtra(getString(R.string.data_latitude), 0),
intent.getDoubleExtra(getString(R.string.data_longitude), 0)));
}
}
下面是 AsyncTask 的 doInBackground 代码
@Override
protected Integer doInBackground(ArrayList<ResponseObject>... params) {
ArrayList<ResponseObject> entries = params[0];
try {
URL url = new URL(insertURL);
for (Iterator<ResponseObject> it = entries.iterator(); it.hasNext();) {
ResponseObject ro = it.next(); // THIS IS WHERE THE EXCEPTION REFERENCES IN THE DEBUG OUTPUT
HttpURLConnection urlConnection = (HttpURLConnection)url.openConnection();
urlConnection.setRequestMethod("POST");
urlConnection.setRequestProperty("USER-AGENT", "Mozilla/5.0");
urlConnection.setRequestProperty("ACCEPT-LANGUAGE", "en-US,en;0.5");
urlConnection.setDoOutput(true);
String postParams = "bssid=" + ro.BSSID
+ "&ssid=" + ro.SSID
+ "&capabilities=" + ro.CAPABILITIES
+ "&level=" + String.valueOf(ro.LEVEL)
+ "&frequency=" + String.valueOf(ro.FREQUENCY)
+ "×tamp=" + ro.TIMESTAMP
+ "&lat=" + String.valueOf(ro.LAT)
+ "&long=" + String.valueOf(ro.LON);
DataOutputStream wr = new DataOutputStream(urlConnection.getOutputStream());
wr.writeBytes(postParams);
wr.flush();
wr.close();
Log.d("RemoteDatabase : ", "Post sent " + ro.BSSID + " || " + String.valueOf(urlConnection.getResponseCode()));
}
} catch (MalformedURLException e) {
e.printStackTrace();
} catch (ProtocolException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
entries.clear();
return null;
}
编辑——我似乎已经将问题追溯到代码的另一部分,点击按钮发送广播。该按钮肯定只发送一次(一直在使用 Log.d 进行检查),但数据库中收到的是两次。现在正在尝试解决此问题。
请在异步任务的 onPreExecute() 方法中插入一个进度条,并在 onPostExecute() 中将其关闭。我认为完成异步任务花费的时间太长,而您在之前再次点击按钮异步任务完成。
已经有一段时间了,但我意识到我忘了添加我是如何解决这个问题的,以防万一它对其他人有帮助。
我发现这里的问题实际上是一些 Android 设备发送广播的多个副本。我正在使用 HTC 手机进行测试,显然出于某种原因,他们发送了 2 份所有广播。我的代码的工作方式是从广播中生成一个线程,这导致 2 个相同的线程对相同的数据进行操作。当这些完成时,他们每个人都发送他们的 "Done" 广播,这导致其中 4 个被主线程接收。一团糟。
我最终不得不为每个广播添加一个唯一的 ID 令牌并在接收端记录值,因此如果相同的 ID 被接收两次,第二次将不会采取任何行动。
我有一个奇怪的问题,希望我能解释清楚。我的应用程序有两个活动 - MainActivity 和 SearchActivity。我在 MainActivity 上有一个按钮,它触发从设备上的数据库上传到我的网络服务器上的远程数据库。如果我在第一次启动应用程序时单击按钮,没问题,工作正常。如果我切换到 SearchActivity,什么也不做,然后切换回去,然后尝试按钮,应用程序会崩溃并出现 ConcurrentModificationException。 我有一个 AsyncTask,它发送本地数据库的内容(已经从数据库中取出并通过参数作为 ArrayList 发送到线程)。我花了几个小时调试它,但仍然无法弄清楚它在哪里。任何建议将不胜感激。
这是按下按钮时触发的代码,用于从单独的 Databaser 线程请求数据库的内容
Button btnRemoteSync = (Button)findViewById(R.id.btnSync);
btnRemoteSync.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Intent startUpload = new Intent(getString(R.string.broadcast_search_database));
startUpload.putExtra("type-id",1);
LocalBroadcastManager.getInstance(getApplicationContext()).sendBroadcast(startUpload);
}
});
这是 BroadcastReceiver 中的代码,它从数据库中获取每个响应并将它们添加到自定义 ResponseObjects 的 ArrayList 中。当数据库线程发送 bssid 值 DONE 时,AsyncTask 启动,ArrayList 作为参数传入。
@Override
public void onReceive(Context context, Intent intent) {
String bssid = intent.getStringExtra(getString(R.string.data_bssid));
if (bssid.equals("DONE")) {
RemoteDatabaseUploader rdb = new RemoteDatabaseUploader(getApplicationContext());
rdb.execute(databases);
} else {
databases.add(new ResponseObject(getApplicationContext(),
bssid,
intent.getStringExtra(getString(R.string.data_ssid)),
intent.getStringExtra(getString(R.string.data_capabilities)),
intent.getIntExtra(getString(R.string.data_level), 0),
intent.getIntExtra(getString(R.string.data_frequency), 0),
intent.getStringExtra(getString(R.string.data_timestamp)),
intent.getDoubleExtra(getString(R.string.data_latitude), 0),
intent.getDoubleExtra(getString(R.string.data_longitude), 0)));
}
}
下面是 AsyncTask 的 doInBackground 代码
@Override
protected Integer doInBackground(ArrayList<ResponseObject>... params) {
ArrayList<ResponseObject> entries = params[0];
try {
URL url = new URL(insertURL);
for (Iterator<ResponseObject> it = entries.iterator(); it.hasNext();) {
ResponseObject ro = it.next(); // THIS IS WHERE THE EXCEPTION REFERENCES IN THE DEBUG OUTPUT
HttpURLConnection urlConnection = (HttpURLConnection)url.openConnection();
urlConnection.setRequestMethod("POST");
urlConnection.setRequestProperty("USER-AGENT", "Mozilla/5.0");
urlConnection.setRequestProperty("ACCEPT-LANGUAGE", "en-US,en;0.5");
urlConnection.setDoOutput(true);
String postParams = "bssid=" + ro.BSSID
+ "&ssid=" + ro.SSID
+ "&capabilities=" + ro.CAPABILITIES
+ "&level=" + String.valueOf(ro.LEVEL)
+ "&frequency=" + String.valueOf(ro.FREQUENCY)
+ "×tamp=" + ro.TIMESTAMP
+ "&lat=" + String.valueOf(ro.LAT)
+ "&long=" + String.valueOf(ro.LON);
DataOutputStream wr = new DataOutputStream(urlConnection.getOutputStream());
wr.writeBytes(postParams);
wr.flush();
wr.close();
Log.d("RemoteDatabase : ", "Post sent " + ro.BSSID + " || " + String.valueOf(urlConnection.getResponseCode()));
}
} catch (MalformedURLException e) {
e.printStackTrace();
} catch (ProtocolException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
entries.clear();
return null;
}
编辑——我似乎已经将问题追溯到代码的另一部分,点击按钮发送广播。该按钮肯定只发送一次(一直在使用 Log.d 进行检查),但数据库中收到的是两次。现在正在尝试解决此问题。
请在异步任务的 onPreExecute() 方法中插入一个进度条,并在 onPostExecute() 中将其关闭。我认为完成异步任务花费的时间太长,而您在之前再次点击按钮异步任务完成。
已经有一段时间了,但我意识到我忘了添加我是如何解决这个问题的,以防万一它对其他人有帮助。
我发现这里的问题实际上是一些 Android 设备发送广播的多个副本。我正在使用 HTC 手机进行测试,显然出于某种原因,他们发送了 2 份所有广播。我的代码的工作方式是从广播中生成一个线程,这导致 2 个相同的线程对相同的数据进行操作。当这些完成时,他们每个人都发送他们的 "Done" 广播,这导致其中 4 个被主线程接收。一团糟。 我最终不得不为每个广播添加一个唯一的 ID 令牌并在接收端记录值,因此如果相同的 ID 被接收两次,第二次将不会采取任何行动。