Anomalie bei Bitmap-Transfer übers Netzwerk

M

Minilulatsch

Neues Mitglied
0
Hallo Leute,

ich programmiere im Moment eine App, die unter anderem auch Bilder zwischen einem Server und einem Client austauschen soll. Bevor ich zum eigentlichen Problem komme, erzähle ich erstmal etwas über die Funktionsweise.

Also auf einem Rechner läuft eine Server-Anwendung (in Java geschrieben). Dabei handelt es sich um einen Multithreaded-Server der über SSL verschlüsselt. Für jede eingehende Verbindung wird also ein neuer Thread gestartet und ein DataInputStream und ein DataOutputStream angelegt. Der Server hat nun ein Bild in einer Datenbank gespeichert, welches er ausliest und mit folgender Funktion an den Client schickt (wobei os der DataOutputStream)

Code:
private void sendBitmapBytes(byte[] imageBytes, int packetSize) {
          try {
            os.writeInt(imageBytes.length);
            os.flush();
            int packets = (imageBytes.length-(imageBytes.length%packetSize))/packetSize;
            int rest = imageBytes.length%packetSize;
            for(int i = 0; i < packets*packetSize;i+=packetSize) {
                os.write(imageBytes,i,packetSize);
                os.flush();
            }
            os.write(imageBytes, imageBytes.length-rest,rest);
            os.flush();

        } catch (IOException e) {
            e.printStackTrace();
        }
    }

Damit wird das Bitmap also in gleichmäßigen Paketen verschickt. Der Client empfängt diese Bild folgendermaßen

Code:
    public static Bitmap receiveImage(DataInputStream is, int packetSize) {
        try {
            int size = is.readInt();
            if(size != 0) {
                byte image[] = new byte[size];
                int packets = (size-(size%packetSize))/packetSize;
                int rest = size%packetSize;
                for (int i = 0; i < packets*packetSize; i+=packetSize) {
                    is.read(image, i, packetSize);
                }
                is.read(image, size-rest,rest);
                return (BitmapFactory.decodeByteArray(image, 0, image.length));
            } else {
                return null;
            }
        } catch (IOException e) {
            e.printStackTrace();
            return null;
        }
    }

Jetzt ist es so, dass das bei Tests zwischen dem Server und der App auf einem Emulator mit Android 7 (API 24) auch problemlos funktioniert, das Bild das in der Datenbank gespeichert ist wird so wie es sein soll beim Client angezeigt. Sobald ich aber einen Emulator mit z.B. Android 4.4.2 (API 19, aber auch jede darunter) verwende, werden die Bytes nicht mehr richtig eingelesen. Im Debugger sehe ich, dass das erste Byte in image[] noch das richtige ist, danach folgen nur noch Nullen (Am Server wurde nichts verändert, das Problem ist durch einfaches wechseln der benutzten API beliebig reproduzierbar). Irgendwas muss also beim Lesen des InputStreams schief gehen, aber leider habe ich keine Ahnung was. Ergänzt wird das ganze Verhalten durch ein weiteres Phänomen.
Normalerweise rufe ich die sendBitmapBytes- und die receiveImage-Funktionen mit einer packetSize von 2048 auf. Was bei API 24 funktioniert, führt dann bei API 19 zum genannten Fehler. Sobald ich die packetSize aber auf 1 reduziere, funktioniert der Bild-Transfer auf einmal auch mit API 19, jedes einzelne Byte kommt an wie es soll. Auch hier frage ich mich: Warum? Denn bei API 24 funktioniert es auch mit 2048 Bytes pro Transfer. Und die packetSize auf 1 reduzieren kann eigentlich auch nicht die Lösung sein, da jedes Byte einzeln versenden und empfangen da eher nicht der richtige Weg ist, oder?

Ich hoffe ihr könnt mir bei meinem Problem helfen, ich kann leider gar nichts sinnvolles daraus schließen, vielleicht bin ich aber auch vor lauter Rumprobieren so blind, das ich irgendwas essenzielles übersehe.
Falls ich irgendwelche Infos vergessen habe, reiche ich die natürlich gerne nach.

Vielen Dank schonmal und Lg
Minilulatsch
 
Hallo Lulatsch,

bevor ich hier vermute, eine Frage vorweg an Dich :

a) Hast du deinen Transfer (App seitig) in einem Thread ?
b) warum gibst du eine "static" im Getter zurück ?
 
Hallo Stefan,

Server seitig läuft der Transfer in einem Thread, App seitig jedoch nicht, da wird einfach die Activity gestartet und sobald der entsprechende Knopf gedrückt wird, fordert die App das Bild vom Server an, ohne das in einen eigenen Thread auszulagern.

Und zu b) (mit Getter meinst du ja wahrscheinlich die receiveImage-Funktion?): static wird ja nicht zurückgegeben, der Rückgabetyp ist ja ein Bitmap. Aber die Funktion selber ist statisch. Beim Server wird für jede einkommende Verbindung ein eigenes Object "UserConnection" erstellt, dass unter anderem auch die Funktionen für die Kommunikation auf seinen Streams beinhalten. Ist halt etwas praktikabler, weil da ja mehrer Verbindung verarbeitet werden. Da die App aber immer nur eine Verbindung gleichzeitig hat, liegen sind die Funktionen zur Kommunikation da alle statisch, damit sie zwar alle in eine Klasse ausgelagert werden können, aber kein Object dieser Klasse erzeugt werden muss (also als Funktionssammlung einfach)

Lg Julian

[EDIT]: Ich hoffe ich hab dich bei b) überhaupt richtig verstanden
 
Hallo Lulatsch,

a) unter Android musst du Netzwerkzugriffe IMMER in einem Thread ausführen.
Das wäre z.b. schon der erste Grund, warum bei dir alles ein wenig strubbelig läuft.
Du kannst z.b. einen AsyncTask verwenden ( nur für kurze operationen ) und im PostExecute dein Bitmap
ermitteln

b) statische Variablen und Funktionen nur immer äussersten Ausnahmefall.
Lieber eine klasse mit new iniitieren und dann den Getter non static.

c) Wenn dir das alles jetzt zu viel ist (einarbeiten) dann nimm doch z.b, ganz einfach dieses hier
GitHub - koush/ion: Android Asynchronous Networking and Image Loading

Das gibt dir ein wunderschönes CallBack zurück.
 
Hi Stefan,

okay, ich werde mich da mal der Reihe nach durcharbeiten. Die Verbindung zum Server hatte ich im Verlauf der Problemsuche bereits als eigene Klasse und als nicht statisch deklariert, sodass ich die Verbindung auch immer als Objekt hatte. Da ich dadurch aber keine Verbesserung hatte, ist es durch diverse Reverts bei Git jetzt wieder alles statisch gewesen, aber du hast natürlich recht, da sollte ich wieder zum nicht statischen zurückkehren. Vielen Dank auch für den Link! Das schaue ich mir mal alles in Ruhe an und hoffe, dass das im Endeffekt den Fehler verursacht hat. Falls nicht, könnte es sein, dass wir uns hier wieder sehen^^

Lg Julian
 
Hallo Julian ,

viel Erfolg und du bist gerne wieder gesehen :)
 
Zuletzt bearbeitet:
Hallo Markus,

nein, bisher habe ich keine URLConnection benutzt, sondern immer direkt auf dem Socket selbst gearbeitet.
Ich konnte erstmal auch natürlich nicht locker lassen, dass Problem zu finden anstatt auf einen alternativen Ansatz zurückzugreifen. Das Problem habe ich jetzt sogar tatsächlich gelöst, schlauer bin ich aber nachwievor nicht. Bei rumprobieren ist mir nämlich dann noch folgendes aufgefallen:

Die Zeile
Code:
int size = is.readInt();
empfängt die Anzahl der einzulesenden Bytes vor dem eigentlichen Transfer, damit die Funktion weiß, wie viele Bytes sie überhaupt zu erwarten hat. Da ich die Größe des Testbildes ja kenne, habe ich 'size' zum Test einmal hardcoded gesetzt. Beim Server hab ich dann dementsprechend den Teil, bei dem die size gesendet wird auskommentiert. Und siehe da, das Bild konnte wieder gesendet werden. Aber auch damit nicht genug. Bisher war es ja so, dass wenn der Fehler auftrat, nur das erste Byte richtig ankam und alle weiteren Bytes, obwohl sie gesendet wurden, 0 waren. Bei weiteren Tests habe ich dann das Senden der size wieder mit reingenommen und App-seitig nicht mit readInt() realisiert, sondern mit 4 readByte()-Aufrufen, die die Bytes in ein Array gelesen haben und daraus die size erstellt haben (was ja äquivalent zu readInt() ist). Der nächste Schritt war dann mal zu schauen, was passiert wenn ich zum Empfangen der size 5 statt 4 Bytes lese. Dadurch wurde dann, wie erwartet, das 1. Byte des Bildes bereits vor dem eigentlichen Bildtransfer gelesen, sodass das Empfangen des Bildes erst mit Byte 2 losging. Und wieder eine neue Überraschung: diesmal wurden auch ab Byte 2 alle Bytes erfolgreich gelesen.

Also viel Text für eine kurze Aussage: aktuell ist das Problem dadurch gelöst, dass ich das erste Byte des Bildes einzeln lese und an die erste Stelle des BytesArrays setzte und alle anderen dann paketweise dahinter. Da das aber insgesamt ziemlich inkonsistent wirkt, wird das keine dauerhafte Lösung darstellen, sodass ich mich jetzt auch mit den von euch genannten Vorschlägen auseinandersetzten werde (wobei die Serververbindung App-seitig bereits in einen eigenen Thread gepackt wurde).

Vielen Dank für eure Hilfe, vielleicht wird einer von euch ja schlau daraus was es damit auf sich hat, ich bin es jedenfalls nicht geworden :confused2:

Liebe Grüße
Julian
 

Ähnliche Themen

D
Antworten
23
Aufrufe
2.383
Data2006
D
M
Antworten
4
Aufrufe
1.147
swa00
swa00
5
Antworten
0
Aufrufe
1.116
586920
5
Zurück
Oben Unten