Android Tutorial [B4X] Cross platform example

i_view64_27Givs3HpE.png


This is a simple example that demonstrates a recommended method of sharing code and files between B4A, B4i and B4J.
It is based on these tips: https://www.b4x.com/android/forum/threads/xui2d-cross-platform-tips.96815/#content

The main idea is to implement everything that is possible in one or more classes and share these classes.
The project structure looks like this:
dopus_HyKeOAvka1.png

The shared classes are in the root folder and the shared classes are added as references from the parent folder:
lliExMdUps.png


There is a "shared files" folder where we put all the resource files that will be copied to the assets folders.
This is done with this line (it is in the shared class):
Code:
#CustomBuildAction: folders ready, %WINDIR%\System32\Robocopy.exe,"..\..\Shared Files" "..\Files"
Robocopy will only copy modified files so it will not slow down compilation.

In this example the logo image file is the only shared file.
Note that the layouts were created using the relatively new feature: [B4X] Sharing layouts between platforms

Example depends on:

- BCToast: https://www.b4x.com/android/forum/threads/111046/#content
- BCTextEngine: https://www.b4x.com/android/forum/threads/106207/#content
These are cross platform b4xlibs and they should be put in the B4X folder: https://www.b4x.com/android/forum/threads/b4x-additional-libraries-folder.103165/#content

A more complex example: https://www.b4x.com/android/forum/threads/b4x-corona-cases-cross-platform-example.115107/#content

B4i_lHVZTb71N9.png
 

Attachments

Last edited:
Two tips:

1. When you add new files to the 'Shared Files' folder they will not appear automatically in the projects Files folder. A simple way to update them is to run the project and then click on Files tab - Sync.
2. If you add a new module it will be added in one of the projects folders. You can either keep it there or drag it to the file system, to the root folder, and then drag it back to the project and add it as a referenced module. In the later case it is recommended to then delete the copy left in the projects folder.
 
@Erel ,
I have been doing something very similar to this, but the shared classes etc are in their own subdirectory.

One thing that I have noticed is that when you create a new class it is created in the subdirectory for the IDE you are using.
I then have to:
  • close the IDE
  • manually copy the file,
  • reopen the IDE (an error message pops up, which is expected)
  • Select add existing modules and link the class using the relative option
It would be really useful if the "Add New Module" option allowed you to automatically create the class file in the right directory. Should I add this as a Wish?

I also create a Panel which is passed to the child rather than the Activity/Page.


[CODE lang="b4x" title="Sample Code - B4i"]
Sub Process_Globals
'These global variables will be declared once when the application starts.
'Public variables can be accessed from all modules.
Public page As Page
Private xui As XUI 'ignore

Private hp As HomePageClass

Private backpanel As Panel
End Sub

Sub PageLoad
page.Initialize("page")
page.RootPanel.LoadLayout("Backpanel")
page.RootPanel.Color = Globals.gcol_primaryback


End Sub


Private Sub Page_Resize(Width As Int, Height As Int)
'Display within Iphone X safe areas
Private r As Rect = page.SafeAreaInsets
backpanel.SetLayoutAnimated(0,1,r.Left, r.Top, Width - r.Right - r.Left, Height - r.Bottom - r.Top)
hp.resize(Width - r.Right - r.Left,Height - r.Bottom - r.Top)
End Sub


Sub pagestart
If Not(page.IsInitialized) Then PageLoad

hp.Initialize("homepage")
hp.ShowPage(Me,backpanel)
End Sub



Sub page_Appear
hp.Resuming
End Sub

Sub page_Disappear
hp.Pausing
End Sub
[/CODE]

[CODE lang="b4x" title="Sample Code - B4a"]Region Activity Attributes
#FullScreen: False
#IncludeTitle: False

#End Region

Sub Process_Globals
'These global variables will be declared once when the application starts.
'These variables can be accessed from all modules.
Private first_time As Boolean

End Sub

Sub Globals
'These global variables will be redeclared each time the activity is created.
'These variables can only be accessed from this module.
Private hpClass As HomePageClass
Private backpanel As Panel

End Sub

Sub Activity_Create(FirstTime As Boolean)
Activity.LoadLayout("backpanel")

hpClass.Initialize("hpclass")

first_time = FirstTime
End Sub

Sub Activity_Resume
If first_time Then
hpClass.ShowPage(Me,backpanel)
End If
first_time = False
CallSubDelayed(Me, "ResumeHomePage")
End Sub

Sub ResumeHomePage
hpClass.Resuming
End Sub

Sub Activity_Pause (UserClosed As Boolean)
If hpClass.IsInitialized Then
hpClass.Pausing
End If
End Sub

Sub Activity_KeyPress (KeyCode As Int) As Boolean
' May result in a callback to hpClass_closeapp
Return hpclass.keypress(KeyCode)
End Sub

Sub hpClass_closeapp
ExitApplication
End Sub

[/CODE]
 
I have been doing something very similar to this, but the shared classes etc are in their own subdirectory.
I don't recommend using the old "shared folder" feature. Better to build it like I did here.

One thing that I have noticed is that when you create a new class it is created in the subdirectory for the IDE you are using.
I then have to:
  • close the IDE
  • manually copy the file,
  • reopen the IDE (an error message pops up, which is expected)
  • Select add existing modules and link the class using the relative option
Most of these steps are not needed. Just move the module file to the correct place, drag it to the Modules tree and add it as a referenced module.
I also create a Panel which is passed to the child rather than the Activity/Page.
Better to use B4XView instead of panel.
 
I don't recommend using the old "shared folder" feature. Better to build it like I did here.

Slight misunderstanding.


I don't use the shared modules, but my directory structure look like this.

home
-- B4A
-- B4I
-- SharedCode

Most of these steps are not needed. Just move the module file to the correct place, drag it to the Modules tree and add it as a referenced module.

Ah, of course! I hadn't considered moving the file WITHIN the IDE :rolleyes:

Better to use B4XView instead of panel.
Yes, that is true, In future projects, I will move over to that.
 
Super, thanks for this example. I have some questions, to make it more clear:
1. The gmn object (from class GuessMyNumber) is declared in Main activity vs Main CodeModule (B4A vs B4i). It is initialized in Activity_Create vs Application_Start. Am I right, that in B4i it will be only once declared and is always alive, but in B4A it could be destroyed and many times be initialized?
2. To have a object from a shared class with sure same behavior (so in B4A and B4i only once declared, when app is starting and never destroyed), and which can be reached from every Activity / Page, can I declare it in a CodeModule and initialize it in the Starter ServiceModule in B4A?
 
OK, lets say, I have an object "cs" from a "SharedClass". In B4i there is no problem with its lifetime. It will not be destroyed.
In B4A I will initialize it in Service_Create of the starter service. Can I be sure, that it will not be destroyed, when I declare it in Sub Process_Globals of
1. The Starter Service?
2. The Main Activity?

Code modules are limited in B4A
So I should not declare it in a CodeModule? I like to use a CodeModule "modG" in my Projects for global objects like SQL and global Subs, that I can call from all Activities, like modG.RunSQL(...). The initialisation I do in Service_Create of the starter service or in Activity_Create of Main, IF FirstTime = true. I have no bad experience with that. My apps never crashes ;) Is that unsafe to do?
 
So a shared class (or any other class) that you want to be once initialized and never destroyed, you have to declare in the Main B4A-Activity / B4i-Codemodule:
[CODE lang="b4x" highlight="4"]Sub Process_Globals
'These global variables will be declared once when the application starts.
'These variables can be accessed from all modules.
Public cs As SharedClass 'This class file is availible from B4A, B4i, ...
End Sub[/CODE]

Than the initialisation depends on the platform.
[CODE lang="b4x" title="B4A in the Starter Service" highlight="4"]Sub Service_Create
'This is the program entry point.
'This is a good place to load resources that are not specific to a single activity.
Main.cs.Initialize
End Sub[/CODE]
[CODE lang="b4x" title="B4i in the Main CodeModule" highlight="3"]Private Sub Application_Start (Nav As NavigationController)
'SetDebugAutoFlushLogs(True) 'Uncomment if program crashes before all logs are printed.
cs.Initialize

NavControl = Nav
Page1.Initialize("Page1")
Page1.Title = "Page 1"
Page1.RootPanel.Color = Colors.White
NavControl.ShowPage(Page1)
End Sub[/CODE]

And you can use it in every Activity / Page like Main.cs.MySub(...)

Is that correct?
 
In B4A I will initialize it in Service_Create of the starter service. Can I be sure, that it will not be destroyed, when I declare it in Sub Process_Globals of
1. The Starter Service?
2. The Main Activity?
1. Yes.
2. Yes.
3. Process globals of all modules behave exactly the same and will not be destroyed until the process is destroyed.
 
I have added a B4X coss-platform poject, B4XP_xChart_V4_6.zip, in the xChart Class.
Just to show, for those who are interested in, how much the platform specific code can be reduced.
It contains an xMain class module, common to the three platforms, with all the common code, even the event handling.

Just for example the B4A Main module code:
B4X:
Sub Process_Globals

End Sub

Sub Globals
    Private XM As xMain
End Sub

Sub Activity_Create(FirstTime As Boolean)
    XM.Initialize(Activity)
End Sub

Sub Activity_Resume

End Sub

Sub Activity_Pause (UserClosed As Boolean)

End Sub

Almost nothing!
 
Last edited:
Very easy, when you know how to do it.
Yes, that statement is so true. I've just updated some of my example apps, two of which I've also updated on the forum with just one zip file for multiple platforms.

Match Flags
1591863044402.png


Tic-Tac-Toe
1591863116856.png


Corinavirus (COVID-19)
1591863208168.png


Pixabay
1591872546269.png




Thank you...
 
Last edited:
Back
Top