Android Tutorial AsyncStreams Tutorial

Discussion in 'Tutorials & Examples' started by Erel, Feb 3, 2011.

  1. Erel

    Erel Administrator Staff Member Licensed User

    A new object type is available in the RandomAccessFile library named AsyncStreams.
    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:
    Code:
    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, 10dip10dip300dip60dip)
    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:
    Code:
    AStreams.InitializePrefix(Socket1.InputStream, False, Socket1.OutputStream, "AStreams")
    Then when there is new data available we convert the bytes to string and show it:
    Code:
    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:
    Code:
    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 four modes or frameworks available: AsyncStreams, AsyncStreams in prefix mode, AsyncStreamsText class and AsyncStreamsObject class (based on prefix mode).

    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. For example you are building a chat application.
    In this case you should use AsyncStreamsObject. AsyncStreamsObject doesn't expose the progress though it does support sending files of any size. If all you want to do is send very large files then you should consider following the FileTransfer example.

    If you are communicating with a non B4A 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.

    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: Nov 22, 2016
    luke2012, koaunglay and jinyistudio like this.
  2. schimanski

    schimanski Well-Known Member Licensed User

    Hello Erel!

    I have initialize the Astream with
    Code:
    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...
     
  3. Erel

    Erel Administrator Staff Member Licensed User

    The events are always raised in the same Activity that it belongs.

    You can use a Service together CallSub to share an active object between modules (the events should be handled in the Service).
     
  4. DaisyDuke

    DaisyDuke Member Licensed User

    Hi, where can I download the latest RandomAccessFile library for Basic4PPC? thanks
     
  5. Erel

    Erel Administrator Staff Member Licensed User

    There is no RandomAccessFile library in Basic4ppc. It is a Basic4android library.
     
  6. pablocotan

    pablocotan New Member Licensed 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.
     
  7. Erel

    Erel Administrator Staff Member Licensed User

    In order to use prefix mode you will need to have full control over the data that is sent from the serial device. It will not be possible to use prefix mode with most serial devices.
     
  8. cmartins

    cmartins Member Licensed 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?
     
  9. Erel

    Erel Administrator Staff Member Licensed User

    Make sure to use Initialize instead of InitializePrefix as you are not sending the message length.
     
  10. cmartins

    cmartins Member Licensed User

    Erel,


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

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

    Erel Administrator Staff Member Licensed User

    When you initialize AStream you should use Initialize instead InitializePrefix. InitializePrefix can only be used when you are following the prefix mode protocol which is explained in the first post.
     
  12. cmartins

    cmartins Member Licensed User

    works great


    thx
     
  13. a6000000

    a6000000 Member Licensed User

    see
     
  14. Michael eldred

    Michael eldred Member Licensed 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?
     
  15. Erel

    Erel Administrator Staff Member Licensed User

    NewData is executed on the main thread.

    Can you post the code in NewData?

    You can queue the messages and process them with a timer every x milliseconds.
     
  16. krsrn

    krsrn Member Licensed 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?
     
  17. Erel

    Erel Administrator Staff Member Licensed User

    Please start a new thread for this question.
     
  18. Barbadidoua

    Barbadidoua New Member Licensed 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.
     
  19. maleche

    maleche Member Licensed 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.
     
  20. Erel

    Erel Administrator Staff Member Licensed User

Loading...