Android Tutorial 💡 Tic-Tac-Toe. Simple XUI cross platform WiFi game example B4A vs B4i vs B4J - Newer developers

Requires B4A V9.90 or higher.

Hello fellow B4X developers.
Here is my example of using a broadcast address to automatically find another devices IP address on a network, then to use that IP address to play a game (in this case Tic-Tac-Toe) across that WiFi network (device 1 vs device 2, Android, iOS or Desktop). This tutorial not only shows you how to use UDPSocket (GetBroadcastAddress) to manipulate your routers broadcast address, but I've also used Canvas.DrawLine, SetVisibleAnimated, a Timer, XUI, Images, MediaPlayer (for sound effects), UDPPacket, ServerSocket, B4XSerializator and also B4XView to help create this example which is now based on Erels original example with some changes.

The two modules named TicTacToe.bas and ConnectPlayer.bas are in fact shared modules, thus you should place them in your shared projects modules folder as these two modules work with B4A, B4i and also B4J without any changes necessary. You DO NOT need them in your main projects folder, but if you are not using B4i or B4J then you might as well leave them where they are currently located.

What is a broadcast address (taken from wikipedia): A broadcast address is a network address at which all devices connected to a multiple-access communications network are enabled to receive datagrams, which comprise UDP and TCP/IP packets, for instance. A message sent to a broadcast address may be received by all network-attached hosts

>>> CLICK HERE <<< to download all three Tic-Tac-Toe code examples.

The B4i example can be found below

The B4J example can be found below

Please note: There are multiple ways to achieve what I've done here, I chose this way as I believe that this is one of the easiest for newer B4X developers to follow and to learn from. I would usually only use AsyncStreams (AStream.Write) with ServerSocket (Server.Listen) to send and receive data over a WiFi network, but because I wanted the devices to automatically find and connect to each other without any user interaction I decided to use UDP.

04/06/2020: Released V1.0.0.0
05/06/2020: Updated V1.0.0.1 - Replaced 3 x #If B4A / #If B4i conditions in shared modules
10/06/2020: Remove TTT sub from the main module
14/09/2023: Updated V1.0.1.0 - B4A code to work with SDK 31 and above by adding Bluetooth Runtime Permission.

Libraries used:-
1591260559033.png


Android screenshot
TTTAndroid.jpg


YouTube video showing Android vs iOS



Enjoy...
 
Last edited:
Thanks for awaited nice sharing for good learning... 🙂

Now is there any chance to make this game to play over the internet if we know the IP via WhatsApp etc.? Thanks.
 

Peter Simpson

Expert
Licensed User
Longtime User
Now is there any chance to make this game to play over the internet if we know the IP via WhatsApp etc.? Thanks.

Download and study V1.0.0.1, I've updated the code to better incorporate cross platform solutions.

The IP address, over the internet through a network to a device. That would mean knowing the IP address of the router location on the web, and setting up port forwarding through the receiving router to the mobile device. You would be better of using MQTT.

I was originally thinking about making this an MQTT tutorial in the first place, but then B4X developers would first need to sign up to a free MQTT service, or setup their own locally on their WiFi network before trying out my tutorials. So I decided to go for the UDP option.

My personal MQTT service of choice is cloudmqtt.com, I 100% recommend using them to everyone. But saying that, I do have my own MQTT message broker setup on my onlne server so I don't really need to use cloudmqtt.com anymore.

@Erel has create a great MQTT tutorial (link below), you can easily adapt his code example to my Tic-Tac-Toe code above.
 
Last edited:
Dear Peter Simpson,
Thanks for worthy guidelines. I am old VB6 person and new to B4X and wanted to make it my future favorite.
I will try to make on online but you can do it in a better way so keep developing with MQTT while I will learn from current version and understand basics of wifi communications.
Hopefully then I will get your updated example. Basically I am aimed to make some educational stuff for my kids to work on their tabs with inter-communication with each other. I hope you and Erel will not mind to guide me for some productive work with B4X. Thanks.
 

Peter Simpson

Expert
Licensed User
Longtime User
i have error occured. UDP. getbroadcast is error? how to fix it?
Hello,
Hmm I'm away for a few day but this code should work perfectly fine and 100%.

I'm not in my laptop I'm on my phone currently, but I'm going to presume that you've changed the target SDK to 31 which you should be doing. Can you please try setting the target SDK to 29 or 30 and let me know if you are still having an issue.

It sounds like a permission issue to me, but without my laptop I can't really give you a definitive answer. Anyway please try setting the target SDK to 29 or 30;and get back to me.
 

Neil Rohan

Member
Licensed User
Hello,
Hmm I'm away for a few day but this code should work perfectly fine and 100%.

I'm not in my laptop I'm on my phone currently, but I'm going to presume that you've changed the target SDK to 31 which you should be doing. Can you please try setting the target SDK to 29 or 30 and let me know if you are still having an issue.

It sounds like a permission issue to me, but without my laptop I can't really give you a definitive answer. Anyway please try setting the target SDK to 29 or 30;and get back to me.
Greetings Peter,
I applied your UDPSocket socket methodology to a previously published app and used a Nokia phone running Android 10 as my test device. I was very pleased with the result. I published this new version and asked a friend to download it and give me feedback. She reported that the app would barely load (screen flash) and then crash. It turns out that she was using a Galaxy phone with Android 12. I've searched the forum for clues but have not found any threads that specifically address potential problems implementing the UDPSocket option under Android 12. I might also add that the B4i and B4J versions of this app both function flawlessly.
 

Peter Simpson

Expert
Licensed User
Longtime User
I've searched the forum for clues but have not found any threads that specifically address potential problems implementing the UDPSocket option under Android 12.
Hello,
Thank you for the heads up, I completely forgot about this. The issue you reported took about 5 minutes to fix as I just added Runtime Permissions for SDK > 31 and that was it.

>>> CLICK HERE <<< to download all three Tic-Tac-Toe code examples.

Note: I added code in the main module and also the manifest. Bluetooth is used to get the device name.
 

Neil Rohan

Member
Licensed User
Thanks for the quick response Peter. I'll upload new version today and report any issues. The fix works fine on my test device.
Cheers.
Greetings once again, Peter.
I obtained a Galaxy Note 10 running Android 12 and tried the suggested fix that you provided earlier. The program crashed when it tried to install. I recorded the following logs and screenshots:

My Manifest:

1695132366538.png


Here is the error log:

Logger connected to: samsung SM-N970U

--------- beginning of main

Copying updated assets files (120)

*** Service (starter) Create ***
** Service (starter) Start **
** Activity (main) Create (first time) **
Error occurred on line: 26 (ConnectPlayer)
java.lang.reflect.InvocationTargetException
at java.lang.reflect.Method.invoke(Native Method)
at anywheresoftware.b4a.keywords.Common.CallSubDebug2(Common.java:1087)
at com.turbosoftsolutions.numberspy.b4xpagesmanager._createpageifneeded(b4xpagesmanager.java:1087)
at com.turbosoftsolutions.numberspy.b4xpagesmanager._showpage(b4xpagesmanager.java:444)
at com.turbosoftsolutions.numberspy.b4xpagesmanager._addpage(b4xpagesmanager.java:264)
at com.turbosoftsolutions.numberspy.b4xpagesmanager._addpageandcreate(b4xpagesmanager.java:278)
at com.turbosoftsolutions.numberspy.b4xpagesmanager._initialize(b4xpagesmanager.java:170)
at com.turbosoftsolutions.numberspy.main._activity_create(main.java:421)
at java.lang.reflect.Method.invoke(Native Method)
at anywheresoftware.b4a.shell.Shell.runMethod(Shell.java:732)
at anywheresoftware.b4a.shell.Shell.raiseEventImpl(Shell.java:351)
at anywheresoftware.b4a.shell.Shell.raiseEvent(Shell.java:255)
at java.lang.reflect.Method.invoke(Native Method)
at anywheresoftware.b4a.ShellBA.raiseEvent2(ShellBA.java:157)
at com.turbosoftsolutions.numberspy.main.afterFirstLayout(main.java:105)
at com.turbosoftsolutions.numberspy.main.access$000(main.java:17)
at com.turbosoftsolutions.numberspy.main$WaitForLayout.run(main.java:83)
at android.os.Handler.handleCallback(Handler.java:938)
at android.os.Handler.dispatchMessage(Handler.java:99)
at android.os.Looper.loopOnce(Looper.java:226)
at android.os.Looper.loop(Looper.java:313)
at android.app.ActivityThread.main(ActivityThread.java:8663)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:567)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1135)
Caused by: java.lang.RuntimeException: java.lang.reflect.InvocationTargetException
at anywheresoftware.b4a.debug.Debug.CallSub4(Debug.java:336)
at anywheresoftware.b4a.debug.Debug.CallSubNew2(Debug.java:285)
... 25 more
Caused by: java.lang.reflect.InvocationTargetException
at java.lang.reflect.Method.invoke(Native Method)
at anywheresoftware.b4a.debug.Debug.CallSub4(Debug.java:318)
... 26 more
Caused by: java.lang.reflect.InvocationTargetException
at java.lang.reflect.Method.invoke(Native Method)
at anywheresoftware.b4a.keywords.Common.CallSubDebug2(Common.java:1087)
at com.turbosoftsolutions.numberspy.b4xpagesmanager._createpageifneeded(b4xpagesmanager.java:1087)
at com.turbosoftsolutions.numberspy.b4xpagesmanager._addpageandcreate(b4xpagesmanager.java:281)
at com.turbosoftsolutions.numberspy.b4xpages._addpageandcreate(b4xpages.java:60)
at com.turbosoftsolutions.numberspy.b4xmainpage._b4xpage_created(b4xmainpage.java:308)
... 28 more
Caused by: java.lang.RuntimeException: java.lang.reflect.InvocationTargetException
at anywheresoftware.b4a.debug.Debug.CallSub4(Debug.java:336)
at anywheresoftware.b4a.debug.Debug.CallSubNew2(Debug.java:285)
... 34 more
Caused by: java.lang.reflect.InvocationTargetException
at java.lang.reflect.Method.invoke(Native Method)
at anywheresoftware.b4a.debug.Debug.CallSub4(Debug.java:318)
... 35 more
Caused by: java.lang.reflect.InvocationTargetException
at java.lang.reflect.Method.invoke(Native Method)
at anywheresoftware.b4j.object.JavaObject.RunMethod(JavaObject.java:132)
at com.turbosoftsolutions.numberspy.connectplayer._initialize(connectplayer.java:78)
at com.turbosoftsolutions.numberspy.b4xfindnumberpage$ResumableSub_B4XPage_Created.resume(b4xfindnumberpage.java:756)
at com.turbosoftsolutions.numberspy.b4xfindnumberpage._b4xpage_created(b4xfindnumberpage.java:587)
... 37 more
Caused by: java.lang.SecurityException: Need android.permission.BLUETOOTH_CONNECT permission for AttributionSource { uid = 10349, packageName = com.turbosoftsolutions.numberspy, attributionTag = null, token = android.os.BinderProxy@34d03db, next = null }: getName
at android.os.Parcel.createExceptionOrNull(Parcel.java:2438)
at android.os.Parcel.createException(Parcel.java:2422)
at android.os.Parcel.readException(Parcel.java:2405)
at android.os.Parcel.readException(Parcel.java:2347)
at android.bluetooth.IBluetoothManager$Stub$Proxy.getName(IBluetoothManager.java:1194)
at android.bluetooth.BluetoothAdapter.getName(BluetoothAdapter.java:2345)
... 42 more
Caused by: android.os.RemoteException: Remote stack trace:
at com.android.server.BluetoothManagerService.checkPermissionForDataDelivery(BluetoothManagerService.java:5032)
at com.android.server.BluetoothManagerService.checkConnectPermissionForDataDelivery(BluetoothManagerService.java:5050)
at com.android.server.BluetoothManagerService.getName(BluetoothManagerService.java:2835)
at android.bluetooth.IBluetoothManager$Stub.onTransact(IBluetoothManager.java:542)
at android.os.Binder.execTransactInternal(Binder.java:1215)
Message longer than Log limit (4000). Message was truncated.
** Activity (main) Resume **

Here is the line of code in Connect Class that causes the error:
#If B4A
Dim JO As JavaObject
MyDeviceName = JO.InitializeStatic("android.bluetooth.BluetoothAdapter").RunMethodJO("getDefaultAdapter", Null).RunMethod("getName", Null) 'Remember to add Manifest permission
#Else If B4i
Dim Name As NativeObject
MyDeviceName = Name.Initialize("UIDevice").RunMethod("currentDevice", Null).GetField("name").AsString
#Else If B4J
MyDeviceName = GetEnvironmentVariable("COMPUTERNAME", "")
#End If

And finally, the code snippet in my calling program:

Dim Phone As Phone
Dim RP As RuntimePermissions
Dim Permissions As List
If Phone.SdkVersion >= 31 Then
Permissions = Array("android.permission.BLUETOOTH_SCAN", "android.permission.BLUETOOTH_CONNECT", RP.PERMISSION_ACCESS_FINE_LOCATION)
Else
Permissions = Array(RP.PERMISSION_ACCESS_FINE_LOCATION)
End If

For Each Per As String In Permissions
RP.CheckAndRequest(Per)
Wait For Activity_PermissionResult (Permission As String, Result As Boolean)
If Result = False Then
ToastMessageShow("Permission must be granted", True)
' Activity.Finish
B4XPages.ClosePage(Me)
Return
Else
Log($"${Per} Permission granted"$)
End If
Next

I also include the Permissions:

1695132661588.png


Since my application does not require the "devicename", I stripped that code from the Connect Class and the program installs and runs normally. I modified the B4I and B4J Connect Class code in a similar way in order for the devices to communicate.

Sorry for the lengthy post as I know this thread is dated. I included all this for anyone still following as well as for you information. Thanks again for the help.

Cheers!
 

Peter Simpson

Expert
Licensed User
Longtime User
Greetings once again, Peter.
I obtained a Galaxy Note 10 running Android 12 and tried the suggested fix that you provided earlier. The program crashed when it tried to install. I recorded the following logs and screenshots:

Hmm,
Try adding the following line into the manifest
B4X:
AddPermission(android.permission.BLUETOOTH)

Are my original examples working on your GN10?
DOWNLOAD THEM AGAIN onto your GN10 and give it another go.

Let me know if it's working...
 

Neil Rohan

Member
Licensed User
Hmm,
Try adding the following line into the manifest
B4X:
AddPermission(android.permission.BLUETOOTH)

Are my original examples working on your GN10?
DOWNLOAD THEM AGAIN onto your GN10 and give it another go.

Let me know if it's working...
Your latest examples (with the additional permissions code added) work fine on my Galaxy. Now that I have a GN10 I can debug my code and will investigate further. Thanks again.
 

Neil Rohan

Member
Licensed User
Runtimepermissions were previously added. New debug effort leads me to believe I'm apparently not requesting permission earlier enough. I'm slowly closing in on the bug. Thanks for the tip.
 

Neil Rohan

Member
Licensed User
Hmm,
Try adding the following line into the manifest
B4X:
AddPermission(android.permission.BLUETOOTH)

Are my original examples working on your GN10?
DOWNLOAD THEM AGAIN onto your GN10 and give it another go.

Let me know if it's working...
Peter,
Your latest B4A example works as advertised on GN10 (android 12). On the Nokia (android 10), in addition to adding the following line into the manifest:
AddPermission(android.permission.BLUETOOTH)

I also had to slightly modify your code (adding android.permission.BLUETOOTH to the array):

If Phone.SdkVersion >= 31 Then
Permissions = Array("android.permission.BLUETOOTH_SCAN", "android.permission.BLUETOOTH_CONNECT", RP.PERMISSION_ACCESS_FINE_LOCATION)
Else
Permissions = Array("android.permission.BLUETOOTH",RP.PERMISSION_ACCESS_FINE_LOCATION)

End If
Both phones communicate flawlessly. Thanks so much for your help. Definitely a learning experience!
 
Top