AlertDialog oft updaten

J

Juleru

Ambitioniertes Mitglied
5
Hallo zusammen!

Ich benutze einen AlertDialog in meiner Java 11/Android 11 App, um den User über den Fortschritt des Login-Prozesses zu informieren, das schließt auch den Download einiger Dateien ein (natürlich über einen Background Thread, die Anzahl kenne ich im Voraus nicht). Ein AlertDialog deswegen, weil die Dateien heruntergeladenen werden müssen, um die App zu benutzen, und währenddessen darf der User nicht irgendwelche Einstellungen ändern, etc..

Ich füge dem AlertDialog vor jedem größeren Schritt eine Zeile hinzu mit dialog.setMessage(alt + System.lineSeparator() + neu) und das funktioniert problemlos. Nun möchte ich den Dialog aber auch mit dem Namen der Datei, die gerade heruntergeladen wird, updaten. Dafür gebe ich eine Referenz des Dialogs von meiner Activity an die Download-Klasse weiter und mache dann das:
Java:
for(String f:fileNames) {
    Log.d(TAG,(alt+System.lineSeparator()+f));
    dialog.setMessage(alt+System.lineSeparator()+f); //"alt" bleibt immer gleich, damit sich nur der Dateiname ändert!
}
Den Code für den Download der Dateien selbst habe ich noch nicht geschrieben, das ^ rennt also 100%-ig am Main Thread.

Das Problem: Der Dialog updatet erst, wenn die Schleife komplett durchgelaufen ist und man sieht nur den Namen der letzten Datei, in der Konsole werden aber natürlich alle Namen ausgegeben.
Wieso passiert das?
Ich mache das genauso in der iOS-Variante meiner App (funktioniert problemlos), braucht Androids AlertDialog einfach länger, um seine TextView upzudaten? Kann man das irgendwie umgehen bzw. ein Update "erzwingen"?
 
@Juleru

Unter Android musst du peinlichst darauf achten , was im Main-UI-Thread und in einem OperationsThread läuft.
Download-Operationen dürfen nur in einen separaten Thread , nicht in den UI Thread.
Die UI ist lediglich eine separate Thread-Pipe und arbeitet sequentiell alles das ab , mit der man sie füttert.

a) ein AlertDialog ist der falsche View nimm lieber einen CustomDialog/View mit einem inflated Layout -> und das Ganze dann eine separate Class. (ggf vererben)
b) Darin den DownloadProcess , die Dir dann die UI - wiederum im UI Thread - updaten.
c) oder bastel Dir halt eine separate DownloadClass mit Callbacks/Listener

REF : (runOnUiThread)
Activity | Android Developers
 
Zuletzt bearbeitet:
  • Danke
Reaktionen: Nightly
Danke für die schnelle Antwort.

Wie gesagt, den Download-Code habe ich noch nicht geschrieben, aktuell läuft das alles also 100%-ig am Main Thread. Ich werde dafür dann sehr wahrscheinlich einen AsyncTask mit publishProgress() benutzen (ich weiß, ist deprecated, aber es gibt einfach keine vernünftige Alternative, mit der man Methoden für vorher/Hintergrundthread/mittendrin/nachher hat), aber zuerst möchte ich erst einmal, dass das hier funktioniert.

a) Wieso ist AlertDialog die "falsche View"? Ich brauche nicht viel, einfach ein Ding mit einer TextView, die man updaten kann und das über der restlichen UI eingeblendet wird und Tippe auf alles darunter verhindert. AlertDialog ist mMn perfekt dafür, weil man ihn leicht und schnell ein- und ausblenden kann und ich nicht extra ein Layout erstellen muss, das dann nicht 100%-ig zu den anderen Dialogen passt.
b) Die Klassen habe ich schon und daran darf sich auch nichts mehr ändern, für die erste App-Stufe wäre das: 1x Activity mit 2 Fragments in 2 unterschiedlichen Klassen (Login + Einstellungen) und eine Klasse, die sich um alles kümmert, was die Verbindung mit dem Internet angeht (Login, Download, Upload, Dateiliste holen, etc.). Diese Internet-Klasse wird dann übrigens auch von der restlichen App (Activity mit mehreren Fragments nach dem Login) benutzt.
 
Zuletzt bearbeitet:
Ja, ist schon seit 3 Jahren als deprecated ausgewiesen - daher bitte API für Alternativen beachten !


Wieso ist AlertDialog die "falsche View"? Ich brauche nicht viel,
Na dann bin ich mal gespannt, wie du auf die LayoutElemente aus einem Thread zugreifen möchtest - ein AlertDialog ist gekapselt.


Allgemein :
So wie du vorgehst und auch nichts ändern möchtest, bekommst du dein "Problem" halt nicht gelöst.
Und dass man "nicht extra ein Layout erstellen muss" habe ich ja noch nie gehört - 5 Min Arbeit :)

Ich kann Dir nur Tipps geben , die aus der hauptberuflichen Praxis entstehen und auch nichts Anderes sind, als Google selbst vorgibt - und noch extra dafür Funktionen bereit stellt.
 
Zuletzt bearbeitet:
Siehe mein voriger Post, darin habe ich alles schon beantwortet, was den AsyncTask und den Main Thread angeht. Wie gesagt: Es wird auf den AlertDialog nur vom MainThread zugegriffen (jetzt und auch in Zukunft), das Drumherum ala Task ist egal für die hier gestellte Frage.

swa00 schrieb:
Na dann bin ich mal gespannt, wie du auf die LayoutElemente aus einem Therad zugreifen möchtest - ein AlertDialog ist gekapselt.
Ich verstehe nicht, was du meinst. Ich habe vom MainThread Zugriff auf die TextView des Dialogs und mehr brauche ich nicht. Das Updaten der Nachricht mit setMessage funktioniert ansonsten ja, nur in der Schleife wird erst am Ende upgedatet, warum auch immer.
Zum CustomDialog, meinst du das? Wenn ich ehrlich bin, verstehe ich den Sinn nicht, denn es geht ja nur um eine TextView, die der Original-AlertDialog sowieso schon hat.
 
Zuletzt bearbeitet:
Öhm, jetzt habe ich ernsthaft nicht damit gerechnet, dass ich mich für die (von Google) übliche Arbeitsweise rechtfertigen muss :)


Post #2 beschreibt die Vorgehensweise und deren Möglichkeiten.

Wenn du den (deprecated) AsycTask verwenden möchtest, dann kannst du dass natürlich tun , wird aber wahrscheinlich nur auf deinem Device (API 30) stabil laufen. Es gibt mittlerweile X Alternativen für einen Thread.
Und natürlich kannst du auf dein "setMessage" zugreifen, löst aber wiederum einen Setter einer fertigen overlay Klasse auf.
Und bei dem was du machen möchtest, ist das der tödlichste Weg, den man tun kann.

nur in der Schleife wird erst am Ende upgedatet, warum auch immer.
Weil du der UI keine Luft lässt - Ursache & Lösung wird auch bereits in Post #2 beschrieben

for(String f:fileNames) {
Log.d(TAG,(alt+System.lineSeparator()+f));
dialog.setMessage(alt+System.lineSeparator()+f); //"alt" bleibt immer gleich, damit sich nur der Dateiname ändert!
}
Auch das lässt der UI NULL Luft - das blockiert dir u.U. die gesamte UI bis zum Ende der Schleife


Zum CustomDialog, meinst du das? Wenn ich ehrlich bin, verstehe ich den Sinn nicht, denn es geht ja nur um eine TextView, die der Original-AlertDialog sowieso schon hat.

Dann verstehst du grundsätzlich nicht, wie Android funktioniert und wie man für jede Operation möglichst eigene Layouts/View benötigt.
Ein AlertDialog ist nicht nur ein View mit ein paar Elementen , sondern ist ein vorgefertigtes OverlayView welches auf ein interaktives Eingreifen des Nutzers wartet und darüber hinaus auch noch dismiss() auswertet.
Dies für einen Progress zu nehmen, ist nicht im Sinne des Erfinders.

Ich verstehe ganz ehrlich nicht, weshalb du dich mit allen Mitteln dagegen sträubst, für dein Ziel auch das passende Element zu verwenden.

Wenn du doch eh anderer Auffassung bist, warum "beschwerst" du dich denn im Nachhinein, dass das so nicht funktioniert ? :)
 
Zuletzt bearbeitet:
Ich glaube, wir reden etwas aneinander vorbei, ich fasse daher zusammen:
Auf den AlertDialog greife ich nur vom MainThread zu, eventuelle Downloads, ein AsyncTask oder was auch immer, sind aktuell vollkommen egal. Es geht nur um den Code, den ich im ersten Post gepostet habe und der wird in der "separaten DownloadClass" (wie in deinem c)-Edit; keine Activity!) am MainThread benutzt.

Ich habe es jetzt mit einer "custom view" nach dieser Beschreibung versucht (dein a)-Vorschlag):
Im ersten Fragment (= Login-Screen):
Java:
private void createProgressDialog() {
    AlertDialog.Builder builder = new AlertDialog.Builder((AppCompatActivity) getActivity());
    builder.setTitle("Titel");
    builder.setCancelable(false);
    LayoutInflater inflater = requireActivity().getLayoutInflater();
    builder.setView(inflater.inflate(R.layout.loadingdialog, null));
    loadingDialog = builder.create();
    loadingDialog.show();
}
In der "MachIrgendetwasMitDemInternet"-Klasse:
Java:
TextView textView = (TextView) dialog.findViewById(R.id.loadingText);

for(String f:fileNames) {
    textView.setText(f);
}
Selbes Problem, die TextView zeigt nur den letzten Dateinamen an.

Zu deinem c)-Link:
Was bringt runOnUiThread, wenn das alles sowieso schon auf dem MainThread (=UI Thread) läuft?
Meine "MachIrgendetwasMitDemInternet"-Klasse ist keine Activity und kann runOnUiThread nicht direkt benutzen, daher habe ich es nach diesem und diesem Vorschlag versucht:
Java:
for(String f:fileNames) {
    new Handler(Looper.getMainLooper()).post(new Runnable() {
        @Override
        public void run() {
            textView.setText(f);
        }
    });
}
Selbes Problem - Update kommt erst am Ende.
 
Nein, wir reden hier eindeutig nicht aneinander vorbei :

Denn deine letzten Code-Versuche zeigen, dass du weder die Threads, noch die UI des Android Systems verstanden hast. (und vllt. auch ein wenig Schwierigkeiten mit OOP)

a)
Nehmen wir deinen Source:

Code:
TextView textView = (TextView) dialog.findViewById(R.id.loadingText);

for(String f:fileNames) {
    textView.setText(f);
}

Selbes Problem, die TextView zeigt nur den letzten Dateinamen an.
Logisch , denn hier "ballerst" du die UI zu und gibst ihr nicht die Luft , jedes " textView.setText(f);" zu verarbeiten.
Demensprechend Skipped die UI deine vorherigen Aufrufe und rendert nur den Letzten.
Deine App macht also genau das falsch , was du ihr falsch an Anweisung gibst.

Man kann unter Android kein UI in einer Loop bedienen.
Schau dir einfach mal bitte den kompletten Log deiner eigenen App an - dort wirst du mit an Sicherheit grenzender Wahrscheinlichkeit Einträge finden , wie z.b. .

Skipped xx frames! The application may be doing too much work on its main thread.

Das erkläre ich allerdings jetzt schon zum 4tem Male - und ich weis ehrlich gesagt nicht , wie oft ich es noch schreiben soll.

Android muss im UI Aufbau komplett anders behandelt werden, als iOS.
Wenn du dir z.b. den Source von REACT anschaust, separieren diese auch je nach System .


b) Dein Thread - "Versuch"
Auch dieser Code ist leider falsch ( Handler)
Hier rufst du jedes mal einen Thread in der Loop auf. Die Loop muss allerings innerhalb des Threads und darin erst runOnUiThread . (und übrigens ist dieser Code auch deprecated - woher hast du denn den ?)




c)
Code 2:
Code:
private void createProgressDialog() {
    AlertDialog.Builder builder = new AlertDialog.Builder((AppCompatActivity) getActivity());
    builder.setTitle("Titel");
    builder.setCancelable(false);
    LayoutInflater inflater = requireActivity().getLayoutInflater();
    builder.setView(inflater.inflate(R.layout.loadingdialog, null));
    loadingDialog = builder.create();
    loadingDialog.show();
}


Und immer wieder das Gleiche in Grün:
Das ist kein CustomDialog, dass ist wieder ein AlertDialog.

Und egal wie oft du auch auf den AlertDialog hinaus willst - er ist und bleibt für dein Vorhaben der falsche Weg.
 
Zuletzt bearbeitet:
  • Danke
Reaktionen: jogimuc
swa00 schrieb:
Schau dir einfach mal bitte den kompletten Log deiner eigenen App an - dort wirst du mit an Sicherheit grenzender Wahrscheinlichkeit Einträge finden , wie z.b. .
Skipped xx frames! The application may be doing too much work on its main thread.
Nein, diese Meldung gibt es nur einmal ganz am Anfang beim Start der App im Emulator (bevor das erste Fragment angezeigt wird), danach nicht mehr.
swa00 schrieb:
Auch dieser Code ist leider falsch ( Handler)
Hier rufst du jedes mal einen Thread in der Loop auf. Die Loop muss allerings innerhalb des Threads und darin erst runOnUiThread .
runOnUiThread gibt es bei mir nicht, weil es sich bei dieser Klasse nicht um eine Activity handelt. Als Alternative (Source habe ich in meinem vorigen Post verlinkt) wurde dieser Code vorgeschlagen. Wenn ich das Ganze in einem Thread verpacke, wäre das dann (außen nach innen): 1. Thread, 2. Schleife, 3. Handler, 4. UI Update
Java:
new Thread(() -> {
    for(String f:fileNames) {
        new Handler(Looper.getMainLooper()).post(() -> textView.setText(f));
    }
}).start();
swa00 schrieb:
(und übrigens ist dieser Code auch deprecated - woher hast du denn den ?)
Bei mir gibt es keine "deprecated"-Warnung dazu.
Welche API-Version benutzt du und was schlägt er dir als Ersatz vor?
swa00 schrieb:
Das ist kein CustomDialog, dass ist wieder ein AlertDialog.

Und egal wie oft du auch auf den AlertDialog hinaus willst - er ist und bleibt für dein Vorhaben der falsche Weg.
Wie schon mehrmals gefragt: Was ist der "richtige" Weg, wenn man eine Art Popup braucht, das über dem aktuellen Fragment liegt, es aber nicht komplett abdeckt und in dem man den Text regelmäßig updaten kann? Die Suche nach "CustomDialog" liefert mir Androids "Dialog"-Seite, die ich oben verlinkt habe (mit dem "inflated" custom layout).
 

Ähnliche Themen

CreepaZz
Antworten
0
Aufrufe
608
CreepaZz
CreepaZz
K
Antworten
3
Aufrufe
1.133
swa00
swa00
J
  • Juniper
Antworten
11
Aufrufe
1.398
swa00
swa00
Zurück
Oben Unten