Android Tutorial [B4X] AsyncStreams Tutorial

Status
Not open for further replies.
New video tutorial:


AsyncStreams allows you to read data from an InputStream and write data to an OutputStream without blocking your program. The reading and writing are done with two separate threads.

When new data is available the NewData event is raised with the data.
When you write data to the OutputStream the data is added to an internal queue and then sent in the background.

AsyncStreams is very useful when working with slow streams like network streams or Bluetooth streams.

If you work with such streams using the main thread your program may hang or block while waiting for a value.
The way we tried to deal with it in the Serial tutorial and Network tutorial is with a Timer that checks whether there are bytes waiting in the buffer. However even if there are bytes available there is a chance that not enough are available and then our program will hang or block until those are available.
Using AsyncStreams is usually simpler and safer.

AsyncStreams can work in two modes. Regular mode and "prefix mode". Both work as described above.

The following code demonstrates a simple program that sends text to a connected device or computer:
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
         'Can only use prefix mode if both sides of the connection implement the prefix protocol!!!
        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)
    Log("AStreams_Error")
End Sub

Sub AStreams_Terminated
Log("AStreams_Terminated")
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
Once there is a connection we initialize the AsyncStreams object:
B4X:
AStreams.InitializePrefix(Socket1.InputStream, False, Socket1.OutputStream, "AStreams")
Then when there is new data available we convert the bytes to string and show it:
B4X:
Sub AStreams_NewData (Buffer() As Byte)
    Dim msg As String
    msg = BytesToString(Buffer, 0, Buffer.Length, "UTF8")
    ToastMessageShow(msg, False)
End Sub
When the user enters text in the EditText, the text is being sent:
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 mode

When the object is initialized in prefix mode the data is expected to adhere to the following protocol: every message (bytes array) should be prefixed with the bytes array length (as an Int). So if another device sends us a message made of 100 bytes. The stream is expected to include 4 bytes with a value of 100 and then the 100 bytes.
The NewData event will be raised with the 100 bytes. It will not include the 4 prefix bytes.
When you send data with Write or Write2 the bytes length will be added automatically as the prefix of this message.
If you can work in prefix mode, which is usually only possible if you are implementing both sides, it is highly recommended to do so. Under the cover AsyncStreams uses the message length prefix to make sure that the NewData event is always raised with complete messages. If for example it expects 100 bytes and only 60 bytes arrived, it will wait till the other 40 bytes arrive. In regular mode the event will be raised twice and you will need to handle the two parts of the message.
AsyncStreams also handles the case where 100 bytes are expected and more than 100 bytes arrived (which means that there are several messages).

Note that the WriteStream method that is only available in prefix mode uses an internal protocol which includes error detection.

Errors

The Error event will be raised if there is any error. You can use LastException to find the reason for the error. In most cases you will want to close the connection when an error occurs.

The Terminated event will be raised when the connection is unexpectedly terminated.

Related links:
AsyncStreamsText class offers an alternative to prefix mode when working with streams of text.
New example that uses the Starter service and B4XSerializator: https://www.b4x.com/android/forum/threads/network-asyncstreams-b4xserializator.72149/

Which mode / framework to choose?

There are three modes or frameworks available: AsyncStreams, AsyncStreams in prefix mode and AsyncStreamsText class.

Prefix mode can only be used if both sides of the connection follow the "prefix" protocol. In most cases you can only use this mode when you implement both sides of the connection.


If you are communicating with a non B4X app (and cannot implement the prefix protocol) then you have two options:
- If the data sent and received is text based and each message ends with an end of line character(s) then you should use AsyncStreamsText. For example if you are connecting to an external GPS. Note that it is also possible to modify the class and support different delimiters.
The advantage of using AsyncStreamsText is that it will build the messages correctly. You will not receive partial messages (or multiple messages together).
- In other cases you should use AsyncStreams in regular mode. This means that there is no guarantee that each sent message will be received as a single message. In fact it is more or less guaranteed not to happen. So your code is responsible for correctly collecting and receiving the message.
You can use BytesBuilder class to collect and parse the messages.

Notes

When a new message is available the background thread that is responsible for reading data sends a message to the main thread message queue. This message causes the NewData event to be raised. If you are receiving many messages repeatedly and you show a modal dialog (like Msgbox) then it is possible that the order of events will be changed.
 
Last edited:

schimanski

Well-Known Member
Licensed User
Longtime User
Hello Erel!

I have initialize the Astream with
B4X:
  AStreams.Initialize

in the second activity. Than I want to work with the new data in the main activity. But b4a is always looking for the Sub AStreams_NewData (Buffer() As Byte) in the second activity. Is it not possible to get the data in the main-activity? I have declared the Astream in the Main-Activity under Process_Global.

Thanks for help...
 

pablocotan

New Member
Licensed User
Longtime User
AStreams.InitializePrefix prefix

Hello,

This is my first message.:sign0144:

I'm just starting to learn how to use this great tool and I need a little of help from any of you.

I managed to make the AsyncStream library to work in a serial communication, but not using the "InitializePrefix" option.

I know it could be a very simple problem for you, but I’d like to know how to get the 4 bytes of the prefix in the correct order from the string length I already have.:confused:

Please let me know which sentence could help me on this. :sign0163::sign0085:

Thank you very much.
 

cmartins

Member
Licensed User
Longtime User
Erel,



I am trying to test the AsyncStreams using Android and Basic4PPC desktop with code below:

Android side as the same code that you wrote.

PPC Desktop side:

Sub Globals

End Sub

Sub App_Start
Form1.Show
End Sub


Sub btnSend_Click
Dim test as Byte
test = 56
binary1.New1(Client.GetStream,False)
binary1.WriteByte(test)
Client.Close
End Sub

Sub Button1_Click
Client.New1
Client.Connect(TextBox1.Text, 5500)
label1.Text = "Connection established."
End Sub


===========================


Connection ok

When I send a message from PPC to Android the system shows no information. What is wrong?
 

cmartins

Member
Licensed User
Longtime User
Erel,


I really sorry, but I did not understand what you said.

I also tried to use array but the android application frezzen.
 

a6000000

Member
Licensed User
Longtime User
see
Once there is a connection we initialize the AsyncStreams object:
Code:

AStreams.InitializePrefix(Socket1.InputStream, False, Socket1.OutputStream, "AStreams")
 

Michael eldred

Member
Licensed User
Longtime User
Serial comms and Async

Hello to All,

I have got Async streams working even in Prefix mode now,

But I am trying to increase the speed of the communications, reliably.

I have a Microcontroller sending 11 bytes of data in every message. I am able to change the speed of send on the microcontroller side.

the data that is recieved is parsed then screen "gages" are updated.

the problem has been when I start raising the communication speed, it seems that the Async_New_Data event is being called so fast and so many times that none of the other procedures (like the screen update procedure) are functioning, or maybe they are called but in the middle of the routine another New_data event occurs and pulls out of the screen update procedure.


Is the New_data event really in another thread?

again when I lower the comms speed the screen updates fine.

for my app to really work well I need the higher speed.

When the microcontroller is transmitting at at 115,200bps all seems pretty good, anything faster and everything locks up except the data reception

the data that is recieved is placed into a list of parameter variables,

the screen routines, look at these variable from within a timer routine every 100 miliseconds

any ideas on this one?
 

krsrn

Member
Licensed User
Longtime User
I m trying to monitor a particular folder on my android device for a file. Should this file exist then I give the user an indication that a new file exists.

My question is how I get this file to the location on the device. In .NET I was able to use active sync to directly connect to the device and copy files to and from the device.

Is there such a facility in Basic4Android. I ve seen desktop applications created in Basic4Android also but I m not sure how to create these?
 

Barbadidoua

New Member
Licensed User
Longtime User
Hello all,

I would like to use AStreams to exchange streams between Android and PC using Visual Basic 2010.
Do you have sample of DotNet Program matching the Erel code?

Thanks.
 

maleche

Active Member
Licensed User
Longtime User
How can this be used for Bluetooth instead of file?
I tried to follow the code, but had difficulty. I'm trying to make an OBDII reader after sending command data, i.e. ATZ, ATV, etc.
Thanks.
 
Status
Not open for further replies.
Top