Android Tutorial [B4X] Cross platform example

Erel

Administrator
Staff member
Licensed User


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:

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


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):
B4X:
#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

 

Attachments

Last edited:

Erel

Administrator
Staff member
Licensed User
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.
 

Andrew (Digitwell)

Active Member
Licensed User
@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.


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
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
 

Erel

Administrator
Staff member
Licensed User
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.
 

Andrew (Digitwell)

Active Member
Licensed User
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.
 

skrjabin

Active Member
Licensed User
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?
 

skrjabin

Active Member
Licensed User
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?
 

skrjabin

Active Member
Licensed User
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:
B4X:
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
Than the initialisation depends on the platform.
B4A in the Starter Service:
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
B4i in the Main CodeModule:
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
And you can use it in every Activity / Page like Main.cs.MySub(...)

Is that correct?
 

Erel

Administrator
Staff member
Licensed User
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.
 

klaus

Expert
Licensed User
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:
Top