notifydatasetchanged threadsicherheit

T

tim1602

Neues Mitglied
0
Ich habe ein kleines Problem mit meinem ListViewAdapter:

Code:
UIActivity.runOnUiThread( new Runnable() {
    @Override
    public void run()
    {
       ShowLodWriteLock.lock();
       try
       {

         ShowListofDevices = ListofDevices.getCopy();
         notifyDataSetChanged();

    }
    finally
    {
         ShowLodWriteLock.unlock();
    }


Ich habe eine ListView welche die Daten aus ShowListofDevices darstellt.
alle Zugriffe sind mit dem ShowLodWriteLock abgesichert.
Jedoch werden die Aufrufe der getView Methode erst nach dem verlassen des Locks ausgeführt, nachdem das Runnable ausgeführt worden ist.

Wie kann ich die Aufrufe von getView mit dem obigen Lock absichern?
 
Genauer: Wie kann ich ein Update der ListView innerhalb des try blocks erzwingen?

Der Adapter erbt von Baseadapter.
 
@tim1602

Der UiThread ist der MainThread deiner App, und ist für das User Interface zuständig (GUI). Mit runOnUiThread() führst du Code auf den MainThread aus. Damit blockierst du alle andere Methoden im UiThread. Wenn dein Code beendet wird, kann der UiThread erst die GUI aktualisieren.

Am besten du liest Dir folgenden Artikel durch:
Processes and Threads | Android Developers
 
Kenne mich damit recht gut aus.
Die Anweisungsfolge läuft auch komplett aufm UI-Thread, geht ja auch garnicht anders.
Ich habe mich glaube ich nicht gut genug ausgedrückt.
Mein Problem ist Folgendes:

Code:
UIActivity.runOnUiThread( new Runnable() {
    @Override
    public void run()
    {
       ShowLodWriteLock.lock();
       try
       {

           ShowListofDevices = ListofDevices.getCopy();
           notifyDataSetChanged();

       }
       finally
       {
            ShowLodWriteLock.unlock();
       }
    }
});

//Erst wird der oben aufgeführte Code ausgeführt, und nachher erst getView mit Position 1,getView mit Position 2 etc.

Diese getView Aufrufe befinden sich dann nicht innerhalb des Locks. (getView greift auf showlistofdevices zu)
Damit aber kein Fehler auftritt, müssen sich alle Aufrufe von getView in einem Lock befinden.
Es genügt dann natürlich nicht, den Rumpf von getView mit dem Lock oder nem synchronized Block zu versehen, es ShowListofDevices könnte sich ja auch zwischen den aufrufen ändern.

Was ich bräuchte:
Eine Möglichkeit die ListView explizit zu aktualisieren, also ohne Notifydatasetchanged.
Dann könnte ich explizit alle Aufrufe von getView innerhalb des Locks erfolgen lassen.

Natürlich bin ich auch für jede andere Lösung offen.

Ich würde mich auch sehr über eine Dokumentation von notifydatasetchanged freuen, die Methode ist halt immer noch so ne Art Blackbox für mich und meiner Meinung nach auf der Android developer seite echt schlecht beschrieben.

Aber danke schonmal
 
Mir ist zwar nicht klar, was Du wirklich mit der lock - Methode erreichen willst. Mein Gefühl sagt mir, dass Du das nicht brauchst.

Wenn du Probleme mit Reihenfolge der ausgeführten Methoden hast, hast Du zwei Möglichkeiten.

a) Du baust den Adapter um. Das ist nicht ganz trivial, und um Dir dabei zu helfen, fehlt einfach zu viel Code.

b) Du baust dir ein eigenen ListView ohne Adapter. Dafür brauchst du die Methode LayoutInflater().inflate() und die Methoden ViewGroup.addView() und ViewGroup.removeView().

LayoutInflater | Android Developers

ViewGroup | Android Developers
ViewGroup | Android Developers
[doublepost=1466703869,1466703618][/doublepost]Nachtrag:

Es macht kein Sinn auf dem MainThread irgendetwas zu locken. Das ist kontraproduktiv, und kann im schlimmsten Fall zu ein ARM führen. Der MainThread ist für die komplette GUI zuständig, wenn du Ihn blockiert, kann er nicht mehr auf Ereignisse reagieren,
 
Klar ists kontraproduktiv den UI-Thread zu locken,
jedoch sehe ich keine andere Möglichkeit um einer eventuellen IndexOutOfBoundsException vorzubeugen.

Doofe Frage an dich:

Wie denkst du soll ich die Zugriffe auf ShowListofDevices sonst regeln, damit die Threads welche auf dieses Objekt zugreifen nicht interferieren?????????

Ich werde mir Wahrscheinlich jetzt ne eigene "ListView" basteln in welcher ich einfach nen LinearLayout in ne Scrollview packe und dann halt (wie du schon sagst) programmativ die Views einfüge und entferne.

Wieso ich nicht mehr Quellcode hier rein schreibe:

1. Ich darf keinen Code veröffentlichen

2. Es wäre einfach viel zu viel Code, allein die Adapterklasse umfasst 2300 Zeilen Quellcode

Mir ist zwar nicht klar, was Du wirklich mit der lock - Methode erreichen willst. Mein Gefühl sagt mir, dass Du das nicht brauchst

Ich denke wohl das ich sie brauche, Aber wenn du mir sagen kannst wie ich verschiedene Threads ohne synchronized Methoden, synchronized Blöcken, ReentrantLocks, ReentrantReadWriteLocks usw synchronisieren kann, dann nur zu.

Es macht kein Sinn auf dem MainThread irgendetwas zu locken. Das ist kontraproduktiv, und kann im schlimmsten Fall zu ein ARM führen. Der
MainThread ist für die komplette GUI zuständig, wenn du Ihn blockiert, kann er nicht mehr auf Ereignisse reagieren,

Im Grunde hast du Recht, aber das Warten auf die unterschiedlichen Locks ist bei der Anzahl der ListView Elemente nicht spürbar.
Was meinst du mit ARM?

Aber danke schonmal
 
Ich glaube das 'ARM' war ein Tipfehler und sollte 'ANR' heißen.

ANR:= App Not Responding; Das ist eine 'Fehler-Situation' in Android, wenn der Main-Thread zu lange blockiert ist.
 
  • Danke
Reaktionen: markus.tullius
@Kiwi++Soft
Danke, genau das meinte ich. :)
[doublepost=1466794588,1466787656][/doublepost]@tim1602

Ich versuche es noch einmal anders herum.

a) Eine BaseAdapter benutzt man, wenn man nicht alle Items eines ListView am Anfang erzeugen möchte. Sprich es werden nur die Views erzeugt, die auf den Screen sichtbar sind (plus ein paar zusätzlichen, damit schnelles Scrollen möglich ist). getView() wird aufgerufen, wenn der View für ein ListItem ereugt wird.
D.h. getView() wird erst aufgerufen, wenn das Item angezeigt werden soll. Das wird vom System getriggert.
Wenn getView() falsche Werte anzeigt, dann versuchst du auf Variable zu zugreifen, die nach dem Neuladen des View andere Werte zugewiesen wurden.
Da kann Dir nur jemand helfen, der den Code kennt.

b) Unter Java fangen Klassen mit ein Großbuchstaben an, und Variablen und Methoden fangen mit einen Kleinbuchstaben an. ShowListofDevices ist wahrscheinlich keine Klasse.
In deinem Fall wäre es schon interessant, zu wissen, ob ListofDevices.getCopy(); eine statische Methode ist. Wenn ja, hat die App einen wirklich bösen Bug.

c) Ein Adapter dient eigentlich nur ein View zu erzeugen, und dieses im ListView anzuzeigen. 2300 Zeilen sind wahrscheinlich 2000 Zeilen zu viel. Lagere alles andere aus (Am besten in eigene Klassen). Unübersichtlicher Code (siehe auch b) erhöhen die Anzahl der Bugs.

d) Statt ListofDevices.getCopy(); im MainThread auszuführen, kann du den Kopievorgang auch in einem Thread ausführen. Und dann nur die Zuweisung des erzeugten Objekt im MainThread durchführen. Dann braucht du den MainThread nicht sperren. Oder anders ausgedrückt, du solltest das Objekt sperren, welches du kopierst. Dies machst du am bestem im Thread, und nimmst nicht den Umweg über den MainThread.
 
Zuletzt bearbeitet:
@Markus

a) ich weiss wofür man einen BaseAdapter benutzt.
Ich dachte ich benutze eine ListView, da diese relativ einfach zu implementieren ist.

b) ich bin mir über die Java Konventionen im klaren, jedoch sind die Objekte ListofDevices und ShowListofDevices von zentraler Bedeutung.
Ich habe sie bewusst mit Großbuchstaben geschrieben um sie von anderen Objektinstanzen hervorzuheben.

c)Du hast vollkommen recht

d) Denk nochmal scharf darüber nach warum ich getCopy() auch im MainThread ausführen muss wenn ich meinen geposteten Code weiterhin synchronisiert behalten will.

Hab mir vorhin so ne Art eigenen ListViewersatz gebastelt die ich zum aktualisieren zwingen kann.
Wer Interesse am Code?
 
a), b), d) - Ich frage mich nur, warum du dann Probleme hast. Ein guter Entwickler stellt auch mal seine Prämissen in Frage.

Die einzige Metthode neben ListofDevices.getCopy(), welche du sperrst (wenn es überhaupt funktioniert, was ich nicht beurteilen kann, da ich den Code nicht kenne) ist notifyDataSetChanged().
Warum getView() auch gesperrt sein soll, ist mir nicht ersichtlich. Oder anders gesagt, woher soll die Methode getView() wissen, dass du sie sperrst.
Aber auch sonst sieht die Konstruktion nicht vertrauenerweckend aus. Wie Du ja weißt, kann ein InterruptedException auftreten. Die Fehlerhandlung dürfte bei Dir recht interessant sein.
 
Also,

der Aufruf von notifyDataSetChanged führt dazu das die ListView benachrichtigt wird, dass ihr Inhalt veraltet ist.
Daraufhin wird die getView Methode für alle Sichtbaren "Kinder" der ListView aufgerufen.
Wie du eben weisst, oder auch nicht...

Mein Gedanke ist es gewesen einfach die Methode notifyDataSetChanged in das Lock zu packen, damit keine andere Methode in der Lage ist ShowListofDevices zu manipulieren während die getView Methoden ausgeführt werden.

Wie kann ich ein Update der ListView innerhalb des try blocks erzwingen

Ich wollte, das alle Aufrufe von getView durch das Lock geschützt sind.

Die Verwendung der Standard ListView fällt jedoch flach, weil ich nicht beachtet habe, dass nicht sichtbare Views keine Kinder der ListView sind
und die getView Methoden auch beim Scrollen aufgerufen werden.


Könntest du mir mal genau erklären wo eine Interruptedexception auftreten kann??????

Ich sehe kein Thread.sleep, Thread.interrupt oder Thread.interrupted in meinem obigem Codeabschnitt...

Da keine InterruptedException auftritt muss ich auch keine behandeln, logisch oder?

a), b), d) - Ich frage mich nur, warum du dann Probleme hast. Ein guter Entwickler stellt auch mal seine Prämissen in Frage.

Die Quelle meines hier beschriebenen Problems kommt nicht wegen einem schlechten Programmierstil.....
 
Die eigentlich Sache ist, das du an der komplett falschen Stelle ein Lock versuchst. Du blockst den Code in einem Handler (siehe GC: Activity - android.app.Activity (.java) - GrepCode Class Source Zeile 5288), und nicht im Thread.

Zweitens ,wenn man mit Threads arbeitet, sollte man immer darauf achten, dass sie beendet werden. Sicher geht das eigentlich nur mit Thread.interrupt(). Niemand mag einen Thread, der im Speicher rumgeistert, wenn die dazugehörige Activity zerstört wurde.
Es gibt aber auch einen anderen Grund, warum ein InterruptedException auftreten kann. Sinn einer Sperre ist es, dass nur eine begrenzte Anzahl von Threads auf das Objekt zugreifen dürfen, alle anderen Threads müssen warten. Wie man so etwas implementiert, weiß du ja. Also sollte Dir auch klar sein, das ein InterruptedException auftreten kann (InterruptedException | Android Developers).
 
Nen Lock im Thread, du meinst innerhalb der run Methode oder was????

Also ich habe drei Threads, sie greifen alle auf das Objekt ShowLod zu.
Ich habe alle Zugriffe auf ShowLod in Methoden geregelt in welchen ich sie mit dem ShowLodWriteLock mit lockk, unlock abgesichert habe.
Diese Methoden werden in den jeweiligen run() Methoden der Threads ausgeführt.

Also run() -> sontige Methode -> Codeabschnitt mit Lock
Wo ist dein Problem?

markus.tullius schrieb:
Zweitens ,wenn man mit Threads arbeitet, sollte man immer darauf achten, dass sie beendet werden. Sicher geht das eigentlich nur mit Thread.interrupt(). Niemand mag einen Thread, der im Speicher rumgeistert, wenn die dazugehörige Activity zerstört wurde.

Woher willst du wissen, dass ich die Threads nicht beende?
Du siehst ledeglich nen Codeschnipsel ner Methode welche nen Thread ausführt und nicht die Klassendefinition der Threads.
Ich lasse meine Threads ganz normal terminieren, da diese nach folgendem Prinzip aufgebaut sind:

boolean beende = false

while(!beende)
{
-Anweisungen vom Thread
}


setze die Variable beende = true und er terminiert ganz ohne interrupt
[doublepost=1467266336,1467265623][/doublepost]
markus.tullius schrieb:
Es gibt aber auch einen anderen Grund, warum ein InterruptedException auftreten kann. Sinn einer Sperre ist es, dass nur eine begrenzte Anzahl von Threads auf das Objekt zugreifen dürfen, alle anderen Threads müssen warten. Wie man so etwas implementiert, weiß du ja. Also sollte Dir auch klar sein, das ein InterruptedException auftreten kann (InterruptedException | Android Developers).

Völliger Blödsinn, sorry.
Steht doch sogar in dem Link den du mir geschickt hast.....

Thrown when a thread is waiting, sleeping, or otherwise occupied, and the thread is interrupted, either before or during the activity. Occasionally a method may wish to test whether the current thread has been interrupted, and if so, to immediately throw this exception. The following code can be used to achieve this effect:

Ich sehe in meinem Codeschnipsel immernoch kein wait, sleep, Interrupt oder interrupted
 
tim1602 schrieb:
Ich sehe in meinem Codeschnipsel immernoch kein wait, sleep, Interrupt oder interrupted

Nein? Dann wahrscheinlich, weil du Deinen Code selber nicht kennst. Du ruft ein Handler() auf, und da haben wir schon ein wait() und ein InterruptedException.
Du hast eine potentiell endlos laufende while - Schleife in einem Thread. Dieser wurde von einem Objekt gestartet, das unvorhergesehen zerstört werden kann (Lifecycle). Und du glaubst, ein Objekt im Speicher, dass keine Referenzen mehr hat (Insel), ist kein Problem? So etwas nennt man normalerweise ein Memory Leak.

Noch besser, du möchtest einen anderen Thread sperren. Was machst Du, wenn der sperrende Thread abstürzt. Auf eine eingefrorene GUI schauen? Und hoffen, das irgendjemand den Fehler behebt?

Die Java API hat nicht umsonst Methoden wie interrupt(), wait() oder sleep(). Sie helfen dabei, sichern, funktionieren Code zu programmieren. Ein guter Entwickler benutzt den foobar, weil es relativ mühselig ist, dass selber zu programmieren. Es kosten Zeit und Geld Und Fehler in der Programmierung von Threads zu suchen, ist so das Schlimmste, was einen Entwickler passieren kann. Es gibt dafür sogar ein eigener Begriff dafür - Heisenbug.
[doublepost=1467316789,1467316208][/doublepost]-------
Korrektur: Im zweiten Satz: Das muss Handler stehen.
 
Zuletzt bearbeitet:
  • Danke
Reaktionen: Kiwi++Soft

Ähnliche Themen

I
Antworten
3
Aufrufe
1.479
xstream
X
Zurück
Oben Unten