0

Ich habe ein seltsames Problem, das ich hoffe, ich kann es gut genug erklären. Meine App hat zwei Aktivitäten - MainActivity und SearchActivity. Ich habe eine Schaltfläche auf MainActivity, die einen Upload von der Datenbank auf dem Gerät in eine entfernte Datenbank auf meinem Webserver auslöst. Wenn ich beim ersten Start der App auf die Schaltfläche klicke, funktioniert das problemlos. Wenn ich zur SearchActivity wechsle, nichts tue und zurückwechsle, dann versuche die Schaltfläche, die App stürzt mit einer ConcurrentModificationException ab. Ich habe eine AsyncTask, die den Inhalt einer lokalen Datenbank sendet (bereits aus der Datenbank herausgezogen und über die Parameter als ArrayList an den Thread gesendet). Ich habe Stunden damit verbracht, das zu debuggen und kann immer noch nicht herausfinden, wo es ist. Irgendwelche Vorschläge würden sehr geschätzt werden.ConcurrentModificationException in Android AsyncTask

Dies ist der Code auf dem Button drücken, ausgelöst, um die Inhalte der Datenbank von einem separaten 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); 
     } 
    }); 

Dies ist der Code in den BroadcastReceiver zu verlangen, das jede Antwort von dem databaser bekommt und fügt sie zu einer ArrayList von benutzerdefinierten ResponseObjects. Wenn der Database-Thread einen Bssid-Wert von DONE sendet, wird die AsyncTask mit der als Parameter übergebenen ArrayList gestartet.

@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))); 

     } 
    } 

Unten ist der doInBackground Code für die 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) 
        + "&timestamp=" + 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; 
} 


EDIT - Ich erscheinen das Problem zurückgeführt haben abwärts zu einem anderen Abschnitt des Codes, in denen eine Sendung auf Klick auf den Button gesendet wird . Die Taste sendet definitiv nur einmal (wurde mit Log.d geprüft), aber die empfangene in der Datenbank empfängt sie zweimal. Versuche das jetzt zu beheben.

+0

Wissen Sie, was eine 'ConcurrentModificationException' ist? Weißt du, warum es ein Problem sein könnte, ein Element zu einer Liste in einem Thread hinzuzufügen, während du es in einem anderen Thread wiederholst? –

+0

@AndyTurner Ich weiß, was dieser Fehler ist, aber ich sollte es nicht in einem Thread und Iterieren in einem anderen ändern - die Iteration sollte erst beginnen, wenn der Databaser "Done" an den Haupt-Thread sendet, was bedeutet, dass die Liste abgeschlossen ist. – Brae

Antwort

2

Es ist schon eine Weile her, aber ich habe vergessen, hinzuzufügen, wie ich das gelöst habe, nur für den Fall, dass es jemand anderem hilft.

Ich fand das Problem hier war eigentlich, dass einige Android-Geräte mehrere Kopien einer Sendung senden. Ich benutzte ein HTC-Handy zum Testen und anscheinend aus irgendeinem Grund senden sie 2 Kopien aller Sendungen. Die Art, wie mein Code funktionierte, erzeugte einen Thread von einer Übertragung, was dazu führte, dass zwei identische Threads mit den gleichen Daten arbeiteten. Als diese fertig waren, sendeten sie jeweils ihre "Fertig" -Übertragung, was dazu führte, dass 4 von ihnen vom Hauptthread empfangen wurden. Komplettes Durcheinander. Ich musste am Ende jedes Broadcasts ein eindeutiges ID-Token hinzufügen und die Werte am empfangenden Ende aufzeichnen. Wenn also dieselbe ID zweimal empfangen wurde, würde beim zweiten Mal keine Aktion ausgeführt.

1

Bitte fügen Sie einen Fortschrittsbalken in Ihre onPreExecute() Methode der asynchronen Aufgabe und entlassen Sie es in onPostExecute(). Ich denke, es dauert zu viel Zeit, um die asynchrone Aufgabe abzuschließen, und Sie tippen auf die Schaltfläche erneut vor der Abschluss der asynchronen Aufgabe

+0

Die Ausnahme passiert so ziemlich in dem Moment, in dem ich sie zum ersten Mal drücke. Ich war vorsichtig, es aus diesem Grund nicht zweimal zu drücken. Ich sollte sowieso einen Fortschrittsbalken einbauen, aber ich hatte mich nicht darum gekümmert, da dies nur für eine Kursarbeit in der Netzwerkprogrammierung gedacht ist. Technisch muss ich diesen Fehler nicht beheben, da der Netzwerkteil funktioniert, wenn ich es zur richtigen Zeit mache, aber es ärgert mich höllisch. – Brae

+0

Ich denke, Sie haben auf die Schaltfläche getippt, nachdem Sie von der searchActivity zurückgekommen sind.Wie können wir sicherstellen, dass die vorherige asynchrone Task abgeschlossen wurde oder nicht.Um Benutzeraktionen bis zum Abschluss der asynchronen Task zu vermeiden, verwenden wir normalerweise die Fortschrittsanzeige. –

+0

Ja, aber es sollte keine weitere Instanz der AsyncTask erstellt werden, bevor die Schaltfläche gedrückt wird (in der Tat, ich weiß, dass es nicht gibt, wie ich mit Log.d überprüft habe). Ich habe gerade eine Bearbeitung für den ursprünglichen Beitrag vorgenommen - ich denke, ich habe das Problem fast verfolgt. BroadcastReceiver für die Datenbankanforderung scheint zweimal erkannt zu werden, obwohl sie einmal gesendet wurde – Brae