TimerTask in Service bringt App zum absturz

Murdock1

Murdock1

Ambitioniertes Mitglied
7
Moin, ich habe in einem Service einen TimerTask, dieser bringt die App zum absturz, sobald die Screenorientation geändert wird. In dem TimerTask, wird ein Wert über Handler an die Fragmente geschickt. Welche Alternativen wären hier angebracht?
 
Murdock1 schrieb:
... dieser bringt die App zum absturz, sobald die Screenorientation geändert wird...

Fehlermeldung? Ich tippe mal, dass der Service versucht auf die Activity zuzugreifen, diese aber dann Screenorientationchange nicht mehr gültig ist ...
 
  • Danke
Reaktionen: Murdock1
Hmm, das ist wirklich seltsam.
Hast du den try catch vielleicht hinter den "überschreibenden Aufruf " (super) gesetzt? Viellicht ist der Programmablauf an einer Stelle schon weiter als die Logik, die du vorsiehst...
 
Murdock1 schrieb:
Was micht aber trotzdem etwas verwundert das der Try/Catch beim senden keine Exception geworfen hat.

Ohne Code zu sehen ist das schon arges im Trüben fischen ;)
 
Ist zwar ohne detailiertere Informationen geraten, doch es wäre denkbar, daß die Handles der Fragmente sich bei einem Wechsel der Screen-Orientierung verändern. Das jedoch würde vermutlich beim Versenden einer Nachricht dem Sender zunächst nicht zwingend auffallen müssen, und folglich auch keine Exception triggern, solange der reine Versandt der Message selbst erfolgreich war. Ob diese dann jedoch im Zielobjekt, der APP, auch erfolgreich verarbeitet werden kann oder aber zu 'Fehlverhalten' führt, bekäme der Versender dann gar nicht mehr mit.
Eine Alternative wäre in einem solchen Fall beispielsweise die Verwendung eines mehr generischen IPC-Mechanismus, wie z.B. die Verwendung eines Sockets (APPseitig als Thread), der die Nachrichten empfangen und an die entsprechenden Fragments weiterreichen kann. Doch meine Annahme über die Ursache des Fehlverhaltens ist natürlich nur so in's Blaue getippt.
 
Zuletzt bearbeitet:
  • Danke
Reaktionen: Murdock1
So, hier der wesentliche Teil des Codes. Im Mainfragment wird der Service in onCreate gestartet und gebunden, in onDestroy wird dieser nur entbunden und gestoppt wenn im Service kein Stream abgespielt wird.
//empfangen
Code:
    class EventManager extends Handler {
        @Override
        public void handleMessage(Message msg) {
            switch (msg.what) {
            case Player.MSG_PEAK:
                //peak in ProgressBar anzeigen
                break;
              default:
                super.handleMessage(msg);
            }
        }
    }
//senden
Code:
    @Override
    public void onCreate() {
        super.onCreate();
        BASS.BASS_Init(-1, 44100, 0);
        BASS_FX.BASS_FX_GetVersion();
        BASS.BASS_SetConfig(BASS.BASS_CONFIG_NET_PREBUF, 750);
        BASS.BASS_SetConfig(BASS.BASS_CONFIG_NET_PLAYLIST, 1);
        BASS.BASS_SetConfig(BASS.BASS_CONFIG_NET_BUFFER, 750);
        BASS.BASS_SetConfig(BASS.BASS_CONFIG_UPDATEPERIOD, 50);    
        Timer timer = new Timer();
        timer.scheduleAtFixedRate(new TimerTask() {
            @Override
            public void run() {
                if(BASS.BASS_ChannelIsActive(stream) == BASS.BASS_ACTIVE_PLAYING){
                    int peak = BASS.BASS_ChannelGetLevel(stream);
                    if (peak != -1){
                        int[] peaks = {BASS.Utils.LOWORD(peak), BASS.Utils.HIWORD(peak)};
                        for (int i = listClient.size() -1; i >= 0; i--) {
                            try {
                                Bundle b = new Bundle();
                                b.putIntArray("peak_values", peaks);
                                Message msg = Message.obtain(null, MSG_PEAK);
                                msg.setData(b);
                                listClient.get(i).send(msg);
                            } catch (RemoteException e) {
                                listClient.remove(i);
                                Log.e("THERADIO", "timer " + e.getMessage());
                            }
                        }
                    }
                }
            }
        }, 75, 75);
    }
 
Zuletzt bearbeitet:
Hallo!

Nun würde ich dir gerne schreiben können, ja, da an dieser Stelle, da ist etwas grundsätzlich verkehrt. Dummerweise liegt das eigentlich Problem vermutlich überhaupt nicht in dem was dein Source macht, sondern vielmehr in dem wie das System damit umgeht.

In diesem Zusammenhang ist es sicherlich ganz gut, sich vor Augen zu halten, hinter Android steht ein Linux, als ein System mit einer Unix ähnlichen Funktionsweise. Unter Unix werden Daemon-Prozesse (Services) auf eine sehr spezielle Weise gestarted, nämlich auf eine, in der der Daemon-Prozess zunächst ein neuer Session-Führer wird, sich mittels fork() cloned. Der Grund dafür ist, wenn ein Benutzer den Daemon manuell startet, gehören alle gestartteten Prozesse zum User, erben also dessen UID (user-ID), sowie auch dessen GID (group ID), und außerdem gehören alle Prozesse zur Session des Benutzers (SID / session-ID).
Würde sich der Benutzer nun ausloggen, würde die session-ID verfallen und alle Prozesse der session-ID werden in Folge vom System beendet. Ein Daemon (Service), wie zum Beispiel ein Apache-Web-Server, soll natürlich weiter laufen und muss deshalb beim Start sich selbst von seiner Benutzerabhängigkeit (UID, GID, SID) lösen, und eine neue, eigene Session starten, um auch nach Beendigung der User-Session autonom weiter existieren zu können.
Und es gibt einen speziellen Grund weshalb ich das erwähne, denn die Services in Android werden jeweils separat mittels einer VM (Dalvik) ebenfalls nach eben der selben Betriebssystem-Mechanik getstartet. Der Grund dafür ist simpel. Android ist ein Linux mit einer speziellen Oberfläche/JavaVM!

Es gibt dazu auch ein ganz gutes Einführungsvideo, was die Isolation von Prozessen betrifft, von 'IO Bytes 2014' Developers@google... siehe: http://youtu.be/Ive8WaeldWA

Das Ganze hat natürlich auch Seiteneffekte, wie beispielsweise, daß der Service(Daemon) in seiner Unabhängigkeit von Benutzer und seinen Benutzerprozessen (APPs) keine eigenständige Wahrnehmung über sich möglicherweise ändernde Handles etc. haben kann. Services gelten im technischen Sinne als mehr oder weniger isoliert, und verfügen über keine unmittelbare Wahrnehmung von sich ändernden Verwaltungsinformationen über Fenster, etc. anderer Prozesse!

Was das nun mit deiner Anwendung zu tun haben könnte, ist folgendes:

Wie bereits als Vermutung angedeutet, kann es sein, daß das System(Android) beim Wechsel der Orientierung die Views fix neu aufbaut, und dabei neue Handles vergibt, sprich die internen Verwaltungsinformationen des Prozesses sich teilweise oder auch gänzlich ändern können.
Die Lebensdauer von Handles für Fensterelemente ist in einer GUI maximal für den Lebenszyklus des jeweiligen Fensters bzw. Fensterelements garantiert, nicht jedoch für den Lebenszyklus der Prozess-ID der APP! Handles können sich also grundsätzlich jederzeit ändern, z.B. wenn ein Fenster fix neu aufgebaut wird! Das gilt sicherlich ganz besonders für Fragmente, welche eine viel kürzere Lebensdauer haben können, als ihr 'einbettendes' Hauptfenster.
Für Fragmente kann dies möglicherweise bereits bei Orientierungswechsel der Fall sein, daß das Fragment von Grund auf neu aufgebaut wurde, sprich OnCreate() etc. neu durchlief. Entsprechend müssen alle Handles eines Fragmentes auch nicht mehr zwingend gültig sein. Es ist nicht auszuschließen, daß Handles nochmals identisch vergeben werden, jedoch nicht garantiert, und in der Praxis auch ehr unwahrscheinlich.

Wenn man sich nun vor Augen hält, eine wie weitreichende Unabhängigkeit dein Service von deiner App aufweisen kann, je nach dem, ob dieser im selben Prozess (PID:GID) läuft oder eben auch nicht, und andererseits wie kurz der Lebenszyklus von Handles, folglich damit auch die Zuverlässigkeit von Message-Passing via Handles ist, liegt die Schlußfolgerung nahe, daß die verwendete Transportmechanik an sich nicht die am besten geeignete sein dürfte. Vermutlich ist sie das auch nicht?!

Die Alternative dazu wäre eben die Verwendung von Transport-Mechniken, die unabhängig sind von Session-ID, Prozess-ID und User-ID:Group-ID. Und das wären dann auf einem 'Unix-System' eben named-pipes bzw. sockets. Wobei auf einem Android-System die Verwendung von Sockets vermutlich die ehr zu empfehlende Methode ist.

Ich kann nur wiederholen, dein Source an sich muss überhaupt nicht fehlerhaft sein, das Problem liegt vermutlich ehr in der zu Grunde liegenden Mechanik im Zusammenspiel zwischen Android und Linux, und den daraus resultierenden Einschränkungen für den Lebenszyklus von Handles im Allgemeinen.

Ich gebe zu, ich bin immer noch dabei zu raten, was die konkreten Gründe für die Abstürze deiner APP bei Orientierungswechsel sind. Nach über 20 Jahren Unix-Development sind die oben genannten Gründe (Isolation/Lebenzyklus von Handles) jedoch, zumindest nach meiner persönlichen Einschätzung, die wahrscheinlichsten Kandidaten für diese Problematik in der Kommunikation zwischen Service(Daemon) und APP via Handler.
Verallgemeinernd kann man sagen, Kommunikation über Prozessgrenzen hinweg sollte stets unabhängig von Prozessinformationen der kommunizierenden Prozesse sein!
Möglich, daß ich mich in deinem speziellen Fall auch irre! Doch das wäre zumindest meine persönliche Vermutung dazu, was die technischen Hintergründe angeht.


Um das Problem zu lösen, würde ich empfehlen dem Service einen Socket zu spendieren, ebenso der App, möglicherweise APP-seitig in einem eigenen Thread, und die Kommunikation über die Sockets, je nach Präferenz via TCP, ablaufen zu lassen. Das kann grundsätzlich all das, was Du ohnehin tun wolltest, sprich eine Nachricht versenden, jedoch völlig unabhängig von Aspekten wie der Prozesszugehörigkeit.
( Die Verwendung von UDP würde ich in diesem Fall nicht empfehlen, da UDP keine Garantie für den Transport verspricht. UDP-Datagramme dürfen vom System jederzeit auch verworfen werden.)

Im Grunde könnte dein Service sogar auf einer ganz anderen Maschine laufen, es würde selbst dann, WLAN/LTE/GPRS/etc. Verbindung vorausgesetzt, immer noch zuverlässig funktionieren, denn diese Transport-Mechanik ist Lageunabhängig! :) (Ein Georg Schramm würde an dieser Stelle vermutlich noch hinzufügen, diese letzte Anmerkung sei als kleiner Scherz gedacht! Ich schließe mich dem an.)


Jedenfalls sollte das zumindest dein Problem lösen, sofern Du noch keinen anderen Lösungsansatz gefunden haben solltest.
Und mein Vorposter hatte ja auch noch einen entsprechenden eigenen Vorschlag dazu!


Mit besten Grüßen aus Berlin, Andreas.

ps. Mit Android L kommt ein 'scheduling service', wenn ich das richtig in Erinnerung habe. Ob dieser jedoch die von dir benötigte Funktionalität beinhalten wird, ist mir im Detail nicht bekannt. Einen Vorteil hätte ein eigener TimerService mit Socket jedoch in jedem Fall, auch andere deiner Apps könnten diesen für periodische oder auch aperiodische Aufgaben nutzen.
Beispiel: App A öffnet einen Socket, um dem Service mitzuteilen, bitte sende mir um xx:yy:zz Uhr eine Benchrichtigung, damit ich eine Aufgabe rechtzeitg erledigen werde. Was jedoch 'APP B' ebenfalls tun könnte, wie z.B.: "sende mir bitte in zzz Sekunden eine Nachricht auf meinen Socket als Erinnerung für die Erledigung einer bestimmten Aufgabe". Ein solcher Timer-SocketService böte zumindest die Möglichkeit für mehrere deine APPs unabhängig voneinander verfüg- und nutzbar zu sein. Aber das nur als Idee am Rande dazu.
 
Zuletzt bearbeitet:
  • Danke
Reaktionen: Murdock1
Guten Abend,

ich danke für die ausführliche Erklärung. Ich habe jetzt aus dem Fragment heraus in onPause und in onStop dem Service eine Message geschickt und darauf hin wird aus dem Service heraus nichts mehr geschickt, bis onResume eintritt und das im Service bekannt wird. Es funktioniert zwar relativ gut, musste trotzdem (bisher)einen Absturz hinnehmen. Deshalb werde ich mir die erwähnten Alternativen mal anschauen und ausgiebig testen. Noch einmal danke an alle:thumbup:,

nette Grüße,

Murdock1
 
Ich habe nun die tatsächliche Ursache gefunden, hatte den timer laufen, aber im Fragment die Message nicht ausgewertet, da lief alles einwandfrei, als ich dann wieder die Werte in ProgressBars anzeigen lassen hab, kam es bei der Orientierungänderung zum Knall, da war mir klar, die Progressbars sind noch = null.
 
Hehe! So kann's kommen... :)
 

Ähnliche Themen

R
  • Robby1950
2
Antworten
23
Aufrufe
1.003
Robby1950
R
Manny87
  • Manny87
Antworten
11
Aufrufe
159
swa00
swa00
D
Antworten
23
Aufrufe
2.499
Data2006
D
Zurück
Oben Unten