Android Tutorial [B4X] Network + AsyncStreams + B4XSerializator

Discussion in 'Tutorials & Examples' started by Erel, Oct 19, 2016.

Thread Status:
Not open for further replies.
  1. Erel

    Erel Administrator Staff Member Licensed User

    New video tutorial:






    [​IMG]


    [​IMG]

    [​IMG]


    This is a simple and important example. It demonstrates several good practices related to network communication.

    1. The network related code is in the Starter service. The network state is also stored in the starter service.
    2. The Activity is only responsible for the UI.
    3. Using B4XSerializator it is simple to work with types instead of strings or bytes. In this case the message structure is:
    Code:
    Type MyMessage (Name As String, Age As Int, Image() As Byte)
    - Bitmaps cannot be directly serialized so we need to convert the bitmap to bytes and vice versa.
    - B4XSerializator is cross platform compatible. This means that you can send the exact same structure to B4J or B4i applications.

    Platform specific notes

    - The code in B4J and B4i is a bit simpler as there is no need to use a second module (starter module). All modules are always active in B4J and B4i.
    - In B4i we need to recreate the server socket when the application becomes active as the socket can become stale while the app is in the background (and not killed).
    - Windows firewall blocks incoming connections so if you want to connect to the B4J app, you need to first allow incoming communication on the relevant port.
     

    Attached Files:

    Last edited: Feb 22, 2018
    toby, Toley, MarcoRome and 17 others like this.
  2. Joerg Rothballer

    Joerg Rothballer Member Licensed User

    Hi. Great possibility. This is what I am looking for.
    How do I get these data to an Windows-Desktop-Application written in VB.Net
    Does anyone has an sample code to get the Type and Bitmap by an vb.net (SERVER) application?
    Thanks
     
  3. Erel

    Erel Administrator Staff Member Licensed User

    It will be trivial to implement the desktop side with B4J. You cannot use B4XSerializator with a .Net application.

    For further discussion please start a new thread.
     
  4. Joerg Rothballer

    Joerg Rothballer Member Licensed User

    Thanks Erel. I will use B4J
     
  5. Erel

    Erel Administrator Staff Member Licensed User

    A B4J implementation is attached.

    [​IMG]

    Note that if you want to connect from the Android to the PC then you need to configure the firewall to allow incoming connections on the relevant port.

    It should be simple to convert this code to B4i.
     

    Attached Files:

  6. asales

    asales Well-Known Member Licensed User

    There is a code to use this options and send files like FileTransfer?
     
  7. Erel

    Erel Administrator Staff Member Licensed User

    asales likes this.
  8. Asim A Baki

    Asim A Baki Member Licensed User

    Serializator only supports objects less than 16K length, it truncate other bytes,
    Any chance to get over this so I can send Files, and images?
     
  9. Erel

    Erel Administrator Staff Member Licensed User

    There is no such limit. Please start a new thread in the questions forum for further discussion.
     
  10. a n g l o

    a n g l o Member Licensed User

    hello and thank you

    1. on both sides, in the 'ConnectToServer' sub, the 'client (socket)' variable is declared locally inside the sub, although there's a global variable having same name.
    is it on purpose, and if yes - why ?

    2. on both, in the 'btnSend_Action' sub, you use the 'out (OutputStream)' after it was closed. it seems that the 'close' should come after the usage, or it does'nt matter since the stream is already filled up ?

    thank you
     
  11. Erel

    Erel Administrator Staff Member Licensed User

    1. The code is:
    Code:
    Public Sub ConnectToServer(Host As String)
       
    Log("Trying to connect to: " & Host)
       CloseExistingConnection
       
    Dim client As Socket
       client.Initialize(
    "client")
       client.Connect(Host, PORT, 
    10000)
    End Sub
    Local variables in B4X never hide global variables. This means that this code initializes the global variable again. This is on purpose as you cannot reuse a socket object.

    2:
    Code:
    Dim out As OutputStream
    out.InitializeToBytesArray(
    0)
    cvs.Bitmap.WriteToStream(out, 
    100"PNG")
    out.Close
    mm.Image = out.ToBytesArray
    The OutputStream is a memory stream. It is safe to call ToBytesArray after it was closed.
     
    spairo and a n g l o like this.
  12. a n g l o

    a n g l o Member Licensed User

    thank you !
     
  13. a n g l o

    a n g l o Member Licensed User

    hi,
    still learning & testing this example, i've tried Erel's reply here 2 month back :
    implementing the b4a as a server on android, and the b4j as a client on windows(desktop),
    and setting both sides clocks to exact same time,
    the changes to Erel's original code are :
    b4a :
    Code:
    Sub btnSend_Click
                    
    Dim mm As MyMessage
                    
    Dim out As OutputStream
                   
                    mm.Initialize
                    mm.Age = edtAge.Text
                    mm.Name = edtName.Text
                   
                    
    'convert the bitmap to bytes
                   
            
    'replace the 4  folowing lines       
                    'out.InitializeToBytesArray(0)
                    'cvs.Bitmap.WriteToStream(out, 100, "PNG")
                    'out.Close
                    'mm.Image = out.ToBytesArray
            'with the folowing 8 lines       
                    Dim sharedDir As String
                    
    Dim fileName As String
                    
    Dim bAR () As Byte
                    sharedDir=
    File.DirRootExternal & "/tmp"
                    fileName=
    "testo.mp3"
                    bAR=
    Bit.InputStreamToBytes(File.OpenInput(sharedDir, fileName))
                    mm.Image=bAR
                    mm.Name=
    "3.8 MB file test"
            
    'end replace-with
                   
                    
    CallSub2(Starter, "SendData", ser.ConvertObjectToBytes(mm))
    End Sub
    and :
    Code:
    Public Sub SendData (data() As Byte)
        
    'new code
                    Log(DateTime.Time(DateTime.Now))
        
    'end new code           
                    If connected Then astream.Write(data)
    End Sub
    b4j:
    Code:
    Sub AStream_NewData (Buffer() As Byte)
                
    Dim mm As MyMessage
                
    Dim in As InputStream
                
    Dim bmp As Image
        
    'new code
                Log(DateTime.Time(DateTime.Now))
        
    'end new       
                mm= ser.ConvertBytesToObject(Buffer)
                edtAge.Text = mm.Age
                edtName.Text = mm.Name
       
                
    'convert the array of bytes to image
        'delete the folowing 3 lines       
                'in.InitializeFromBytesArray(mm.Image, 0, mm.Image.Length)
                'bmp.Initialize2(in)
               
                
    'draw the image
                'cvs.DrawImage(bmp, 0, 0, cvs.Width, cvs.Height)
        'end delete           
    End Sub
    i've taken the times for that 3.8mb file transfer about 30 times.
    all times were between 40-65 seconds, which is very SLOW (less then 100k/sec).
    my network is fast and nothing heavy was occupying it.
    is that the best i can get, or am i missing something ?

    Thank you
     
  14. Erel

    Erel Administrator Staff Member Licensed User

    Please start a new thread for this in the questions forum.
     
  15. Gnappos

    Gnappos Member Licensed User


    Cmpiled with b4a v 5.02(1):
    by running this app on my smartphone with Android 6 I get the following error:


    Note: log switch off, only log_main and log_events will have logs!
    --------- beginning of main
    ** Activity (main) Create, isFirst = true **
    ** Activity (main) Resume **
    Error occurred on line: 36 (Main)
    java.lang.RuntimeException: Object should first be initialized (FloatLabeledEditText).
    at anywheresoftware.b4a.AbsObjectWrapper.getObject(AbsObjectWrapper.java:50)
    at anywheresoftware.b4a.objects.FloatLabeledEditTextWrapper.getEditText(FloatLabeledEditTextWrapper.java:68)
    at java.lang.reflect.Method.invoke(Native Method)
    at anywheresoftware.b4a.shell.Shell.runMethod(Shell.java:636)
    at anywheresoftware.b4a.shell.Shell.raiseEventImpl(Shell.java:302)
    at anywheresoftware.b4a.shell.Shell.raiseEvent(Shell.java:238)
    at java.lang.reflect.Method.invoke(Native Method)
    at anywheresoftware.b4a.ShellBA.raiseEvent2(ShellBA.java:121)
    at anywheresoftware.b4a.BA.raiseEvent(BA.java:171)
    at b4a.example.main.afterFirstLayout(main.java:106)
    at b4a.example.main.access$100(main.java:17)
    at b4a.example.main$WaitForLayout.run(main.java:78)
    at android.os.Handler.handleCallback(Handler.java:743)
    at android.os.Handler.dispatchMessage(Handler.java:95)
    at android.os.Looper.loop(Looper.java:150)
    at android.app.ActivityThread.main(ActivityThread.java:5621)
    at java.lang.reflect.Method.invoke(Native Method)
    at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:794)
    at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:684)



     
    Last edited: Mar 21, 2017
  16. Erel

    Erel Administrator Staff Member Licensed User

    FloatLabeledEditText requires v6.3+. You will need to delete the view and replace it with a regular EditText.
     
    Gnappos likes this.
  17. Erel

    Erel Administrator Staff Member Licensed User

    A new version of the example was uploaded to the first post. It uses the new Wait For keyword to simplify the code. It requires B4A v7+ (beta will be released this week).

    This allows listening for new connections with this code:
    Code:
    Private Sub ListenForConnections
       
    Do While working
         
    server.Listen
         
    Wait For Server_NewConnection (Successful As Boolean, NewSocket As Socket)
         
    If Successful Then
           CloseExistingConnection
           client = NewSocket
           astream.InitializePrefix(client.InputStream, 
    False, client.OutputStream, "astream")
           UpdateState(
    True)
         
    End If
       
    Loop
    End Sub
     
    DonManfred, MarcoRome and lemonisdead like this.
  18. Erel

    Erel Administrator Staff Member Licensed User

    B4J and B4i examples were uploaded to the first post.
     
  19. rkwan

    rkwan Member Licensed User

    Thank you very much, Erel!

    However, I am still too dummy to get it working with my original ESP8266 Wifi server, which is just sending numeric strings all the time...

    1. I can modify your B4i sample and build it ok to my iPhone.

    However, unlike my B4a version, the connection to my ESP8266 Wifi server is no longer static but broken easily and I don't see any trigger to AStream_NewData() tor receive the hard-coded numeric string like "123" even though I already simplified it into:

    Code:
    Sub astream_NewData (Buffer() As Byte)
        
    Dim Rx_String As String

            Rx_String = 
    BytesToString(Buffer, 04 , "UTF8")
           edtName.Text = Rx_String
    End Sub
    2. Since I could not the Rx side working, I decided to modify the Tx side to see if my ESP8266 Wifi Server can receive from B4i app.

    Again, I simplified:-

    Code:
    Sub btnSend_Click
       
    Dim conv As ByteConverter
        
    Dim data() As Byte = conv.StringToBytes(edtName.Text, "UTF8")
        SendData(data)
    End Sub
    so when I put "12345" in the edtName.Text and press "Send",
    I can see from the ESP8266 UART log that a special character + "12345" got received.

    I am curious if I can remove the special character at the beginning easily?!
    But I verified that the Send function is ok in B4i, not the Receive function I need.

    Any other suggestion please?

    If possible, I would like to preserve my ESP8266 Wifi server code the same for both B4a and B4i app
    but if it's not possible, I will try to modify my ESP8266 code then.
    My ESP8266 code is again pretty straight-forward if I just send hard-coded data out to B4a and B4i app:

    Code:
    void loop() {
      client1 = server1.available();  
       
      
    String readString;
       
      // 
    Wait until the client sends some data  
      
    Serial.println("new client");  
      
    while( client1.connected() )  
      {  
        char buffer[
    10];

        //Hard-coded 
    string to be sent to B4a & B4i app for testing 
        readString = 
    "S123";
       
          
    if (readString.substring(0,1) == "S")
          {
             char charBuf[
    4];
             int count = 
    0;
             readString.substring(
    1).toCharArray(charBuf, 4) ;
             
    for (int i=0; i<readString.length(); i++)
             {
                
    if (isDigit(charBuf[i]))
                {
                    count++;
                
    }
             }
             if (count == readString.length()-1)
             {
                distance = atoi(&charBuf[0]);
             }
              snprintf(buffer,5,"%4d",distance);  
               
              client1.print(buffer);  // Sending "123" to B4a & B4i app, B4a - OK but not B4i
              client1.flush();
           }
          readString="";

      } // while (client.connected)

      Serial.println("new client disconnected");  
      client1.stop();  
    }

    Thanks & Best Regards,
    Robert
     
  20. Erel

    Erel Administrator Staff Member Licensed User

    It is better to discuss it in B4i forum. BTW, you should use B4R to develop the Arduino program. It will allow you to use B4RSerializator which is supported by all platforms.
     
Thread Status:
Not open for further replies.
Loading...
  1. This site uses cookies to help personalise content, tailor your experience and to keep you logged in if you register.
    By continuing to use this site, you are consenting to our use of cookies.
    Dismiss Notice