"race condition" oder den static-Konstruktor nicht verstanden?

Hallo zusammen

Mir ist zwar klar, dass der static-Konstruktor bei Java äusserst selten gebraucht wird. (Wie ich gehört bei Klassen mit ausschliesslich statischen Funktionen, um dort, falls notwendig, .dll- (Win) oder .so-Libraries zu inkludieren - das sei quasi „best practise“, aber für andere Sachen werde dieser stat. Konstruktor kaum verwendet…(?))

Nun frage ich mich über die Reihenfolge, wie das genau läuft, wann dieser stat. Konstruktor nun genau aufgerufen wird… habe immer gemeint bevor die Klasse zum ersten mal „berührt“ wird - ob nun rein statisch oder als Instanz spielt auch keine Rolles gemäss meinen aktuellen Vorstellungen davon. (?)

Es geht dabei um die Android-Library des Kreditkarten-Zahlungs-Anbieters SumUp… aber da deren Support relativ schlecht ist, frage ich mal hier nach was daran sein könnte.

Um mich aber nicht wiederholen zu müssen, verweise ich euch (hoffe dies ist kein Problem) auf die von mir dazu verfasste GitHub-Issue:

(Na ja, vielleicht mache ich mich damit bei den Leuten welche die SumUp-Github-Seite verwalten lächerlich und ggf. haben die auch genug von den Leuten welche nicht ganz genau wissen was sie tun…:wink: und deshalb antworten die nicht oder erst nach sehr langer Zeit. Aber irgendwie bin ich doch ziemlich verwirrt über die ganze Sache…)

Besten Dank für eure Feedbacks.

Na ja… vielleicht ist bei Android halt doch einfach vieles ein Bisschen anders als beim „gewöhnlichen“, plattformunabhängigen Java?

Off-topic: Das mit meinem selbst programmierten HTTP-Client für die Android-App war auch total schräg, dass dieser in einem eigenen Thread gestartet werden muss:

Klasse RequestRunnable (HTTPRequestRunnable wäre zwar passender, egal wie dem auch sei! ;-))

package com.example.myapplication;

import com.xaxada.httpclient.components.NVPair;
import com.xaxada.httpclient.impl.AndroidCompatibleHttpClientImpl;

public class RequestRunnable implements Runnable
{
    private String response = null;

    private final AndroidCompatibleHttpClientImpl androidCompatibleHttpClientImpl;

    private String mPath = null;

    private NVPair[] mNVPairArr = null;

    private boolean usePostRequest = false;

    public RequestRunnable()
    {
        super();
        androidCompatibleHttpClientImpl = (AndroidCompatibleHttpClientImpl) AndroidCompatibleHttpClientImpl.createInstance();
    }

    public AndroidCompatibleHttpClientImpl getJavaHttpClientImplAndroidCompatibleImpl()
    {
        return this.androidCompatibleHttpClientImpl;
    }

    public void setUsePostRequest(final boolean caUsePostRequest)
    {
        this.usePostRequest = caUsePostRequest;
    }

    public void setPath(final String caPath)
    {
        this.mPath = caPath;
    }

    public void setParameters(final NVPair[] caNVPairArr)
    {
        this.mNVPairArr = caNVPairArr;
    }

    @Override
    public void run()
    {
        try {
            if (this.usePostRequest) {
                response = androidCompatibleHttpClientImpl.doHttpPost(this.mPath, this.mNVPairArr);
            } else {
                response = androidCompatibleHttpClientImpl.doHttpGet(this.mPath, this.mNVPairArr);
            }

            if (Thread.interrupted()) {
                throw new InterruptedException();
            }
        }
        catch(final InterruptedException caException)
        {
            return;
        }
        catch(final Exception caException)
        {
            throw new RuntimeException(caException);
        }
    }

    public String getResponse()
    {
        return this.response;
    }
}

(Die Sache mit explizit den Thread beenden finde ich auch relativ „lustig“ - na ja, man muss es halt mit Humor nehmen auch wenn’s manchmal so ziemlich zum kotzen ist mit den ganzen Problemen beim Programmieren - vor allem wenn’s für den Chef wieder mal besonders schnell gehen muss! :wink: Meine Codes sind teilweise vielleicht etwas „speziell“ (bin nach 9 Jahren Java-Pause und mit neu mit Android ein schon wenig gefordert… aber lernen tu ich immer wieder gerne was neues, genau auf die Art wie man jemand hier im Forum festgestellt hat, wie es bei uns läuft: „Von heute auf morgen“… oder war das im alten java-Forum… hmm… keine Ahnung mehr, ehrlich gesagt…)

Aufruf so, die hardcodierte IP ist natürlich nur zum prototypen:

final RequestRunnable requestRunnable = new RequestRunnable();
        requestRunnable.getJavaHttpClientImplAndroidCompatibleImpl().setConnectionData("192.168.80.120", 80, false);

        requestRunnable.setUsePostRequest(true);
        requestRunnable.setPath("/");

        final NVPair<String>[] nvp = new NVPair[1];
        nvp[0] = NVPair.getValuePair("carnumber", lCarNumber);

        requestRunnable.setParameters(nvp);
        final Thread t = new Thread(requestRunnable);
        t.start();
        ThreadUtilStatic.waitForThreadReadiness(requestRunnable, this.mActivity, 1000);

        final String response = new String(requestRunnable.getResponse());
        t.interrupt();

-> Wie fügt man bei Android Studio .jar-Libraries zu einem Android-Java-Projekt hinzu?

Naja, Static Initializer werden an der Stelle ausgeführt, an der sie stehen…

https://docs.oracle.com/javase/tutorial/java/javaOO/initial.html

A class can have any number of static initialization blocks, and they can appear anywhere in the class body. The runtime system guarantees that static initialization blocks are called in the order that they appear in the source code.

Das ist also ganz trivial; statische Variablen davor werden davor initialisiert, statische Variablen danach werden danach initialisiert.

Also kein Zauberwerk. Ich brauch’ die manchmal, wenn die Initialisierung einer Variablen in einem try-catch sein mus… :neutral_face: Allgemein finde ich statische Methoden nicht schlecht, aber das wäre jetzt ein gaaanz anderes Fass aufzumachen.

Damit liegst du richtig - der statische Konstruktor wird aufgerufen, bevor du „von außerhalb“ irgendwas mit der Klasse machen kannst.

In diesem Fall wird SumUpState.init(mMainActivity); also ausgeführt, bevor mMainActivity von dir auf einen sinnvollen Wert gesetzt wird, effektiv ist es also ein SumUpState.init(null);, was dann auch der Fehler sein dürfte.

Das ist durchaus sinnvoll - es zwingt den Programmierer, ein paar Grundregeln zu beachten :slight_smile:

Gut, du umgehst das dann wieder und machst es genau falsch, aber das ist eine andere Baustelle…

Je nachdem, was ThreadUtilStatic.waitForThreadReadiness, ist t.interrupt(); entweder sinnlos oder „versteckt“ einen Fehler - sehr „lustig“ :wink:

Er wird mit dem Laden der Klasse aufgerufen. Wenn du sicher gehen möchtest, dass er zu einem bestimmten Zeitpunkt aufgerufen wurde, dann führst du Class.forName(„fullQualifiedName“) aus.

Ist z.B. das übliche Vorgehen wenn du mit Datenbanken arbeitest:

Class.forName("com.mysql.jdbc.Driver");

Danke für die Feedbacks.

Also dann kann es durchaus sein, dass die SumUp Lib einen Race Condition Bug drin hat…?

Das init macht ggf. einen eigenen Thread und die andere Funktion ggf. ebenfalls… also nix mit sequentiell. Und wenn man den beiden nicht genügend Zeitabstand lässt (also mit „static“ arbeitet, damit init mach und dann sofort, unmittelbar login aufruft), dann hat’s man die race condition…

Einen anderen Grund sehe ich aktuell für dieses Verhalten leider nicht.

Das ThreadStaticUtil wartet schlicht, bis alles parat ist, bis der HTTP-Client nicht mehr „null“ zurückgibt was er ja ohne Thread macht:

package com.example.myapplication;

import android.app.Activity;

public abstract class ThreadUtilStatic {
    private ThreadUtilStatic()
    {
        super();
    }

    public static void waitForThreadReadiness(final RequestRunnable caRequestRunnable, final Activity caActivity, final long caTimeout)
    {
        long timeoutCounter = 0;

        while(caRequestRunnable.getResponse() == null)
        {
            try {
                Thread.sleep(1);
                timeoutCounter++;

                if(timeoutCounter > caTimeout)
                {
                    throw new Exception("Timeout for thread readiness reached: More than " + timeoutCounter + " ms ... ");
                }
            }
            catch(final TimeoutException ex)
            {
                Dialog.showAlertDialog(ex.getClass().getName(),"Exception message: " + ex.getMessage(), caActivity);
            }
            catch(final Exception e) {
                throw new RuntimeException(e);
            }
        }
    }
}

Threads explizit abzubrechen über den Wurf von Exceptions, das habe ich bei Stackoverflow gefunden. In Java „an sich“ gäbe es soweit keine explizite Möglichkeit, dies zu tun. Und sobald ich die Response vom HTTP-Client habe, bin ich nicht mehr an diesem Thread interessiert, somit will ich ihn „killen“…

Nein, siehe Antwort von @mrBrown

private static MainActivity mMainActivity = null;

    private SumUpProxy()
    {
        super();
    }

    static
    {
        SumUpState.init(mMainActivity);
    }

    public static void setMainActivity(final MainActivity caMainActivity)
    {
        mMainActivity = caMainActivity;
    }

Der static Block wird abgearbeitet wenn mMainActivity null ist. Da sind keine Threads im Spiel.

Oh… hatte wieder mal das totale „Brett vor dem Kopf“ !!

Das init die Activity beinhalten muss, ist ja ziemlich offensichtlich… aber habe das einfach mal von A nach B verschoben ohne auf das Funktionsargument zu schauen.

Tja, das kommt halt davon wenn man erst zum programmieren kommt nachdem man 8 Stunden sonstiges getan hat („Junge für alles“) und am Abend alle gegangen sind…

Vielen Dank euch allen, Problem somit gelöst! :slight_smile:

Das ist problematisch, da du damit die UI blockierst - genau aus diesem Grund können HTTP-Requests auch nicht in diesem abgesetzt werden.

Sobald du den Response des HTTP-Clients hast, ist der Thread fertig und muss nicht mehr beendet werden.

"Das ist problematisch, da du damit die UI blockierst - genau aus diesem Grund können HTTP-Requests auch nicht in diesem abgesetzt werden."

"Sobald du den Response des HTTP-Clients hast, ist der Thread fertig und muss nicht mehr beendet werden."

Vielen Dank, werde das überarbeiten!

Und auch vielen Dank allen anderen!!

Und, P.S.: Ich hätte auch gedacht „sequentiell“ sei eigentlich richtig. Respektive „nicht-nebenläufig“. (?) Aber darum geht’s ja hier auch nicht unbedingt… ICH habe mit meinem Beitrag (wegen dem gestrigen „Brett vor dem Kopf“, wo mir das Argument der Funktion gar nicht mehr aufgefallen ist) im Prinzip schon einen relativ sinnfreien Thread erstellt! :wink:

(Gut dass aber geklärt wurde, was der „static“-Konstruktor nun genau macht und für was er da ist)

Weitere Diskussionen zum wirklich richtigen Begriff erachte ich an dieser Stelle für eher unnötig - habe eure und auch meine Zeit schon genug in Anspruch genommen, nun besser wieder was „produktives“ machen… (und ihr wohl auch! ;-))

1 Like

Ich habe hier mal ein paar Beiträge gelöscht, die nicht direkt mit dem Thema zu tun hatten, etwas unsachlich formuliert waren, und keinen (für mich erkennbaren) Beitrag zur Diskussion oder Problemlösung darstellten.

Da mögen sich die Ansichten unterscheiden. Einsprüche ggf. per PN an mich.

Und ja, das folgende ist auch „off-topic“, aber ich versuche, es sachlich und kurz zu halten: Wenn ich in einer meiner Libs solche issue reports sehen würde, wie deine in SumUp (z.B. issue 123) dann würde ich mir auch denken: "Aääähmja… :face_with_raised_eyebrow: ": Fünf Kommentare innerhalb von einer Stunde, eröffnet mit einer naiv-zusammenhanglosen Frage, dann gefüllt mit „irgendwelchen“ traces, und zwischendrin ein Kommentar der nur aus dem Wort „Why?“ besteht. Versuch’ dich mal in die Lage derer zu versetzen, die versuchen, anderen (frei und kostenlos) eine Lib anzubieten, und dann mit sowas bombardiert werden.

Inhaltlich kann ich kaum etwas sagen, vor allen, weil hier (mindestens) zwei sehr spezifische Fragen/Themen vermischt wurden, und ich keine Ahnung von Android habe. Das einzige, was ich inhaltlich (wenn auch, darüber bin ich mir im klaren, nicht wirklich sachlich) sagen kann, ist:

Wenn man Code sieht, kann es sehr schwierig sein, zu erkennen, ob dieser Code „gut“ oder „richtig“ ist. Aber oft (nicht immer) ist es einfach, zu erkennen, ob irgendwelcher Code gnadenlos-unterirdischer Muks ist. Und Dinge wie Thread.sleep(1); oder throw new Exception deuten stark auf letzteres hin…

1 Like

Im Prinzip hast du sicher recht.

ABER: Die SumUp-API-Seite macht auf den ersten Blick den Eindruck als sei die SumUp-API oder -SDK (warum haben die dort ne „API“- sowie ne „SDK“-Seite?) WIRKLICH open-source… ist sie aber nicht im geringsten. (Habe auch Verständnis, dass sie nicht alles verraten wollen… schliesslich werden beim Bluetooth-Termin gar PINs von Kreditkarten eingegeben) Das einzige, was dabei open-source ist, ist also die Beispiel-Anwendung.

Und ich bin mir von „normalen“ Java her halt auch gewöhnt dass man einfach ein .jar bekommt. (Oder halt per Maven oder sonstwie heruntergeladen bekommt. Wie das bei Gradle geht, ist mir nun auch klar)

Im meiner Naivität hab ich dem Chef gesagt:

„Kein Problem, die bieten dort ne Android-Lib in Java an, alles voll open-source… und da ich Java einigermassen kenne, wird es kein Problem sein diese auch mit anderen Geräten (z.B. mit einem „normaler“ PC) zu verwenden, kleinere Anpassungen vorausgesetzt… ggf. muss ich auch beim Bluetooth-Zugriff auf ein spez. (plattformabhängiges) API oder sonstwas zurückgreifen, da dies in Java wohl nicht ganz direkt geht…“

Denkste…

Wenn die SumUp-Leute mir von Anfang an gesagt hätte: Nein, gibt es nicht / geht nicht, vergiss es, das einzige was open-source ist, ist die Demo-App!!", dann wäre mir es sofort klar geworden. Soviel dazu.

Und: Das Zeugs mit dem Thread (explizit) unterbrechen ist sicher ein ziemlicher ein Murks - full acknowledge. Aber da thread.stop() offenbar deprecated ist, hab ich halt auf diesen Stackoverflow-Beitrag gehört…

Thread.sleep(1) bau ich übrigens äusserst gerne in Endlosschleifen ein, so kann man bestimmen wie schnell diese läuft und kann auch die Zeit messen um damit z.B. zu Timeouts machen…

Meine Github-Issue bezügl. diesem Thread würde ich am liebsten löschen (schäm!!), leider konnte ich dort aber nur Beitrag #2 löschen der zum hiesigen Forumsbeitrag verweist - na ja, immerhin…:wink:

Man bekommt auch eine „normale Jar“, nur halt gepackt im Android-Format (in dem Fall eine aar, in der eine normale jar liegt).

Inwiefern ist denn dafür relevant, ob die Implementierung Open-Source ist oder nicht? O.o

Bei solchen Fehlern hilft auch Gucken in der Source nicht, und forken willst deren Lib ja nicht, nur nutzen.,

An der Stelle ist ein Stoppen des Threads unnötig. Wenn du an der Stelle ankommst, ist der Thread bereist fertig - der Thread den du versuchst zu stoppen läuft dann gar nicht mehr.

Stoppen macht nur Sinn, wenn der Thread noch läuft, und du den abbrechen möchtest. In diesem Fall zB, wenn der Timeout vorbei ist. In genau dem Fall erreichst du die Stelle zum Abbrechen aber gar nicht, da du vorher eine Exception wirfst.

Vielen Dank für dein Feedback.

"Man bekommt auch eine „normale Jar“, nur halt gepackt im Android-Format (in dem Fall eine aar, in der eine normale jar liegt)."

Ja, dies hier:
https://maven.sumup.com/content/repositories/releases/com/sumup/merchant-sdk/

"Inwiefern ist denn dafür relevant, ob die Implementierung Open-Source ist oder nicht? O.o"

Das was mit dem kompilierten Code vorgegeben ist (nicht nur dekompilierbares Java, sondern auch .so-Dateien mit Maschinencode, welche man bestenfalls in Assembler-Code umwandeln könnte) deckt sich leider nicht ganz mit unseren Anforderungen, bezüglich Use-Cases und Prozessen. SumUp scheint aber trotzdem das „geringste Übel“ unter all den Anbietern zu sein…

"An der Stelle ist ein Stoppen des Threads unnötig. Wenn du an der Stelle ankommst, ist der Thread bereist fertig - der Thread den du versuchst zu stoppen läuft dann gar nicht mehr.

Stoppen macht nur Sinn, wenn der Thread noch läuft, und du den abbrechen möchtest. In diesem Fall zB, wenn der Timeout vorbei ist. In genau dem Fall erreichst du die Stelle zum Abbrechen aber gar nicht, da du vorher eine Exception wirfst."

Danke für den Hinweis; Thread & Multithreading sind/ist auch nicht unbedingt mein Spezialgebiet da ich bisher vor allem mit Webapps zu tun gehabt habe.

-> Die „SumUp Library“ ist eigentlich nicht wirklich ne „pure“ Library mit reinen API-Calls im Hintergrund, sondern bringt fertige Fenster zum Vorschein welche man vorher über die Funktionsargumente „füttert“. Nämlich genau die Gleichen, wie bei der von SumUp zur Verfügung gestellten App vorzufinden sind: https://play.google.com/store/apps/details?id=com.kaching.merchant&hl=de_CH