Android Tutorial AsyncStreams Tutorial

klaus

Expert
Licensed User
Longtime User
Der Englische Originaltext.

Ein neuer Objekt-Type, AsyncStreams, befindet sich in der RandomAccessFile Library.

AsyncStreams ermöglicht, Daten aus einem InputStream zu lesen und Daten in einen OutputStream zu schreiben ohne das Programm zu blockieren. Das Lesen und Schreiben wird in zwei separaten Threads vorgenommen.

Wenn neue Daten verfügbar sind, wird das NewData Event ausgelöst und enthält die Daten.
Wenn man Daten in einen OutputStream schreibt, werden diese in einer internen Warteschlange aufgenommen und dann in den Hintergrund geschickt.

AsyncStreams ist sehr nützlich wenn man mit langsamen Streams wie Netzwerk-Streams oder Bluetooth-Streams arbeitet.

Wenn man mit solchen Streams im Haupt-Thread arbeitet, kann das Programm, während dem Warten auf einen Wert, hängen bleiben oder blockieren.

Wir hatten im Serial Tutorial und im Network Tutorial versucht mit einem Timer zu arbeiten der überprüft ob Bytes im Puffer zur Verfügung stehen. Doch selbst wenn Bytes verfügbar sind, gibt es ein Risiko daß nicht genügend vorhanden sind und dann unser Programm hängen bleibt oder blockiert bis diese verfügbar sind.
AsyncStreams ist in der Regel einfacher und sicherer.

AsyncStreams kann in zwei Modi arbeiten. Regelmäßiger Modus und "Präfix-Modus". Beide arbeiten wie oben beschrieben.


Der folgende Code demonstriert ein einfaches Programm, das Text an ein angeschlossenes Gerät oder einen angeschlossenen Computer sendet:


B4X:
Sub Process_Globals
    Dim AStreams As AsyncStreams
    Dim Server As ServerSocket
    Dim Socket1 As Socket
End Sub
Sub Globals
    Dim EditText1 As EditText
End Sub

Sub Activity_Create(FirstTime As Boolean)
    If FirstTime Then
        Server.Initialize(5500, "Server")
        Server.Listen
        Log("MyIp = " & Server.GetMyIP)
    End If
    EditText1.Initialize("EditText1")
    EditText1.ForceDoneButton = True
    Activity.AddView(EditText1, 10dip, 10dip, 300dip, 60dip)
End Sub

Sub Server_NewConnection (Successful As Boolean, NewSocket As Socket)
    If Successful Then
        ToastMessageShow("Connected", False)
        Socket1 = NewSocket
        AStreams.InitializePrefix(Socket1.InputStream, False, Socket1.OutputStream, "AStreams")
    Else
        ToastMessageShow(LastException.Message, True)
    End If
    Server.Listen
End Sub

Sub AStreams_NewData (Buffer() As Byte)
    Dim msg As String
    msg = BytesToString(Buffer, 0, Buffer.Length, "UTF8")
    ToastMessageShow(msg, False)
    Log(msg)
End Sub

Sub AStreams_Error
    ToastMessageShow(LastException.Message, True)
End Sub

'press on the Done button to send text
Sub EditText1_EnterPressed
    If AStreams.IsInitialized = False Then Return
    If EditText1.Text.Length > 0 Then
        Dim buffer() As Byte
        buffer = EditText1.Text.GetBytes("UTF8")
        AStreams.Write(buffer)
        EditText1.SelectAll
        Log("Sending: " & EditText1.Text)
    End If
End Sub

Sub Activity_Pause(UserClosed As Boolean)
    If UserClosed Then
        Log("closing")
        AStreams.Close
        Socket1.Close
    End If
End Sub
Sobald eine Verbindung da ist initilisieren wir das AsyncStreams Objekt:
B4X:
AStreams.InitializePrefix(Socket1.InputStream, False, Socket1.OutputStream, "AStreams")
Dann, wenn neue Daten bereit sind, konvertieren wir diese ihn in einen String und zeigen ihn an:
B4X:
Sub AStreams_NewData (Buffer() As Byte)
    Dim msg As String
    msg = BytesToString(Buffer, 0, Buffer.Length, "UTF8")
    ToastMessageShow(msg, False)
End Sub
Wenn der Benutzer Text in die EditText eingibt, wird der Texte abgesandt:
B4X:
Sub EditText1_EnterPressed
    If AStreams.IsInitialized = False Then Return
    If EditText1.Text.Length > 0 Then
        Dim buffer() As Byte
        buffer = EditText1.Text.GetBytes("UTF8")
        AStreams.Write(buffer)
        EditText1.SelectAll
        Log("Sending: " & EditText1.Text)
    End If
End Sub
Prefix Modus

Wenn das Objekt im Prefix Modus initialisiert ist, müssen die Daten folgendes Protokoll einhalten: jede Nachricht (Byte-Array) muß mit der Länge des Byte-Arrays (als Int) beginnen. Das heißt daß, wenn ein anderes Gerät eine Nachricht von 100 Bytes sendet, beginnt der Stream mit 4 Bytes mit einem Wert von 100 und dann folgen die 100 Bytes.

Das NewData Event wird ausgelöst und enthält die 100 Bytes. Die 4 Präfix-Bytes sind im Event nicht enthalten.
Wenn man Daten mit Write oder Write2 sendet wird die Arraylänge automatisch als Präfix der Nachricht voran gesetzt.
Es ist dringend empfohlen im Präfix-Modus zu arbeiten, was aber nur möglich ist wenn man ihn auf beiden Seiten implementiert. AsyncStreams nutzt die Länge des Byte-Arrays aus, um sicherzustellen, daß das NewData Event nur mit vollständigen Meldungen ausgelöst wird. Wenn zum Beispiel 100 Bytes erwartet werden aber nur 60 Bytes angekommen sind, wird es warten, bis die restlichen 40 Bytes ankommen sind. Im normalen Modus wird das Event zweimal ausgelöst, und man muß die beiden Teile der Nachricht separat behandeln.

AsyncStreams behandelt auch den Fall wenn 100 Bytes erwartet werden und mehr als 100 Bytes angekommen sind (was heißt daß es mehrere Nachrichten gibt).

Ein Error Event wird ausgelöst falls es irgendeinen Fehler gibt. Man kann LastException benutzen um den Grund des Fehlers zu finden.

 
Top