I have a strange issue, which I'm hoping I can explain well enough. My app has two activities - MainActivity and SearchActivity. I have a button on MainActivity which triggers an upload from the database on the device to a remote database on my web server. If I click the button when I first launch the app, no problem, works fine. If I switch to the SearchActivity, don't do anything, and switch back, then try the button, the app crashes with a ConcurrentModificationException. I've got an AsyncTask which sends the contents of a local database (already pulled out of the database and sent to the thread through the parameters as an ArrayList). I've spent hours debugging this and still can't work out where it is. Any suggestions would be greatly appreciated.
This is the code triggered on the button press, to request the contents of the database from a separate Databaser thread
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);
}
});
This is the code in the BroadcastReceiver which gets each response from the databaser and adds them to an ArrayList of custom ResponseObjects. When the databaser thread sends a bssid value of DONE, the AsyncTask is launched with the ArrayList passed in as a parameter.
@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)));
}
}
Below is the doInBackground code for the AsyncTask
@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;
}
It's been a while but realised I forgot to add how I solved this, just in case it is a help to anyone else.
I found the issue here was actually that some Android devices send multiple copies of a broadcast. I was using an HTC handset for testing and apparently for some reason they send 2 copies of all broadcasts. The way my code was working I was spawning a thread off a broadcast, which was resulting in 2 identical threads operating on the same data. When these completed they were each sending their "Done" broadcast, which was resulting in 4 of them being received by the main thread. Complete mess. I ended up having to add a unique ID token to each broadcast and record the values at the receiving end, so if the same ID was received twice no action would be taken the second time.