Android Code Snippet Multiple Initialize Methods in a B4(A/J) Class

More often than not, I prefer to create my own libraries using the BA IDE and the Compile To Library option in the Project sub-menu, to using Eclipse and Java.

One of the disadvantages of developing classes in B4(A/J) instead of Java, is the restriction to only one Initialize(r), as per the documentation and confirmed by Erel's statement. Sure the initializer may accept arguments as well, but at the end, there can be only one initializer per class. Fortunately, there are various ways of lifting that restriction. One is demonstrated in the code below:

To allow this to happen in a pure BA class you must ensure that your extra initializers call B4A's private method innerInitialize.This is taken care of by the sub InternalInitialize that makes a reflection call to do just that.
B4X:
Private Sub InternalInitialize(ba As Object)
  Dim r As Reflector
  r.Target = Me
  r.Runmethod4("innerInitialize", Array As Object(ba), Array As String("anywheresoftware.b4a.BA"))
End Sub
Therefore by calling InternalInitialize at the beginning of your extra initializers you get the benefit of multiple initializers without having to create your libraries in Java. (It's possible to call your own class Initializer as well and therefore benefit from multiple initializers. I'll describe this method later on.)
B4X:
Sub Class_Globals
Private Language As String
End Sub

Public Sub Initialize
  Language = "English"
End Sub

Public Sub Initialize2(ba As Object, language_translation As String, some_extra_parameter As Double)
  InternalInitialize(ba)
  '
  Language = language_translation
  '.....
End Sub

Private Sub InternalInitialize(ba As Object)
  Dim r As Reflector
  r.Target = Me
  r.Runmethod4("innerInitialize", Array As Object(ba), Array As String("anywheresoftware.b4a.BA"))
End Sub
Edit: 4/12/2015
Following some of the comments this post received, I realize that I should have made it clear what the class initializer does:

Initialize does two main things:
  1. It sets the object's context (the parent activity or service, ie ba object).
  2. It calls the Sub Class_Globals so that the variables are available to the object
Here's a class tutorial if you need a refresher ;)

Second Edit:
I have updated the code in all my posts to remove the hard coded value of ba (i.e. context).
In the original post, it was set to 'main', which is not necessarily the correct context as you may Dim and Initialize the classes in other activities or services.
 
Last edited:

somed3v3loper

Well-Known Member
Licensed User
Longtime User
Thanks but Wouldn't this also work ?


B4X:
Public Sub Initialize
  Language = "English"
End Sub

Public Sub Initialize2(language_translation As String, some_extra_parameter As Double)

  '
  Language = language_translation
  '.....
End Sub

I mean what is the benefit of

B4X:
Private Sub InternalInitialize
  Dim r As Reflector
  r.Target = Me
  r.Runmethod4("innerInitialize", Array As Object(r.GetProcessBA("main")), Array As String("anywheresoftware.b4a.BA"))
End Sub
 

warwound

Expert
Licensed User
Longtime User
Thanks but Wouldn't this also work ?


B4X:
Public Sub Initialize
  Language = "English"
End Sub

Public Sub Initialize2(language_translation As String, some_extra_parameter As Double)

  '
  Language = language_translation
  '.....
End Sub

I mean what is the benefit of

B4X:
Private Sub InternalInitialize
  Dim r As Reflector
  r.Target = Me
  r.Runmethod4("innerInitialize", Array As Object(r.GetProcessBA("main")), Array As String("anywheresoftware.b4a.BA"))
End Sub

If you call your Initialize2 method and then get the class instance's IsInitialized property i think that property will be False.
IsInitialized only returns True if Initialize is called.
 

cimperia

Active Member
Licensed User
Longtime User
Thanks but Wouldn't this also work ?


B4X:
Public Sub Initialize
  Language = "English"
End Sub

Public Sub Initialize2(language_translation As String, some_extra_parameter As Double)

  '
  Language = language_translation
  '.....
End Sub

I mean what is the benefit of

B4X:
Private Sub InternalInitialize
  Dim r As Reflector
  r.Target = Me
  r.Runmethod4("innerInitialize", Array As Object(r.GetProcessBA("main")), Array As String("anywheresoftware.b4a.BA"))
End Sub

It would seem that it would work, but in fact, though in this specific case no error would be raised, the Class variables, declared in Class_Globals would not be declared nor defined.

Imagine you have such a class (MyClass say):
B4X:
'Class MyClass
Sub Class_Globals
  Private Language As String = "English"
  Private Length As Double = 1.50
End Sub

Public Sub Initialize
  Log("--Start Initialize Method--")
  Log(Language)
  Log(Length)
  Log("--End Initialize Method--")
End Sub

Public Sub Initialize2(ba As Object, pLanguage As String, pLength As Double)
  InternalInitialize(ba)
  Log("--Start Initialize2 Method--")
  Log(this.Language)
  Log(this.Length)
  Log("--End Initialize2 Method--")

  'These are local variables, and not the class variables
  Language = pLanguage
  Length = pLength
End Sub
If you Initialize the class with this code:
B4X:
Dim c As MyClass
c.Initialize
The output is as expected:

--Start Initialize Method--
English
1.5
--End Initialize Method--

Because the Class_Globals, among other things, gets called when Initialize is invoked.
However, if you try this
B4X:
Dim r As Reflector
Dim c As MyClass
c.Initialize2(r.GetActivityBA, "French", 1.5)
The ouput will be:

--Start Initialize2 Method--
0
--End Initialize2 Method--

because Class_Globals was not called. Furthermore, the variables that you think are class variables, are in fact local variables that'll disappear as soon as the sub exists. As mentioned in my first post there's only one initializer: Initialize.
However if you call InternalInitialize within Initialize2 as I mentioned in my original post, the class will be initialized correctly and the output for MyClass.Initialize2 will be correct.

I have updated my original post to clarify what Initialize does.

If you want absolute proof that calling InternalInitialize is the fix to that issue, try this:
B4X:
Sub Class_Globals
Private Language As String = "English"
Private Length As Double = 1.50
Private this As MyClass = Me
End Sub

Public Sub Initialize
  Log("--Start Initialize Method--")
  Log(this.Language)
  Log(this.Length)
  Log("--End Initialize Method--")
End Sub

Public Sub Initialize2(ba As Object, pLanguage As String, pLength As Double)
  'InternalInitialize(ba)
  Log("--Start Initialize2 Method--")
  Log(this.Language)
  Log(this.Length)
  Log("--End Initialize2 Method--")

  'These are local variables, and not the class variables
  Language = pLanguage
  Length = pLength
End Sub

Private Sub InternalInitialize(ba As Object)
  Dim r As Reflector
  r.Target = Me
  r.Runmethod4("innerInitialize", Array As Object(ba), Array As String("anywheresoftware.b4a.BA"))
End Sub

And you'll get that exception raised in Initializer2:

--Start Initialize Method--
English
1.5
--End Initialize Method--
--Start Initialize2 Method--
myclass_initialize2 (java line: 62)
java.lang.NullPointerException: Attempt to read from field 'java.lang.String b4a.example.myclass._language' on a null object reference
at b4a.example.myclass._initialize2(myclass.java:62)
at b4a.example.main._activity_create(main.java:328)
at java.lang.reflect.Method.invoke(Native Method)
at java.lang.reflect.Method.invoke(Method.java:372)
at anywheresoftware.b4a.BA.raiseEvent2(BA.java:169)
at b4a.example.main.afterFirstLayout(main.java:102)
at b4a.example.main.access$000(main.java:17)
at b4a.example.main$WaitForLayout.run(main.java:80)
at android.os.Handler.handleCallback(Handler.java:739)
at android.os.Handler.dispatchMessage(Handler.java:95)
at android.os.Looper.loop(Looper.java:135)
at android.app.ActivityThread.main(ActivityThread.java:5257)
at java.lang.reflect.Method.invoke(Native Method)
at java.lang.reflect.Method.invoke(Method.java:372)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:903)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:698)


Uncomment InternalInitialize and no exception will be raised and the output will be correct.

 
Last edited:

LucaMs

Expert
Licensed User
Longtime User
It would seem that it would work, but in fact, though in this specific case no error would be raised, the Class variables, declared in Class_Globals would not be declared nor defined.

You can call Class_Globals from Initialize2, it works.

The only problem is...
If you call your Initialize2 method and then get the class instance's IsInitialized property i think that property will be False.
IsInitialized only returns True if Initialize is called.
 

cimperia

Active Member
Licensed User
Longtime User
You can call Class_Globals from Initialize2, it works.

The only problem is...

Calling Class_Globals is a solution I posted here but it is in fact incorrect. Though the class variables would be available through out the class, the context (activity or service) object (ba) would not.

In the examples I posted, it would not be an issue, but if you required access to the ba object (please read Erel's tutorial here for details), then an exception would be raised, and please read the Edit in post# 1 describing what Initialize does.

Finally to make it crystal clear, here's an extract of Erel's tutorial which makes it obvious why calling Class_Globals is not sufficient:

"Classes store a reference to the context of the activity or service module that called the Initialize sub. This means that classes objects share the same life cycle as the service or activity that initialized them."

What InternalInitialize does is providing context and calling Class_Globals
B4X:
Private Sub InternalInitialize(ba As Object)
  Dim r As Reflector
  r.Target = Me
  r.Runmethod4("innerInitialize", Array As Object(ba), Array As String("anywheresoftware.b4a.BA"))
End Sub
PS: I have removed hard coded values for ba as mentioned in post#1 edit.
 
Last edited:

cimperia

Active Member
Licensed User
Longtime User
So, B4A classes (or should I say Android classes?) are "special" classes (because of Context).
The code I have given will work unchanged for B4A and B4J classes, hence the title of this thread.
The only difference would be the way you invoke non-standard initializers:

In B4A you could use something like this, if you're initializing from an activity:
B4X:
Dim r AsReflector
Dim c As MyClass
c.Initialize2(r.GetActivityBA, "French", 1.5)
whereas in BAJ you could use something like this, if you're calling the initializer from the main module:
B4X:
Dim r AsReflector
Dim c As MyClass
c.Initialize2(r.GetModuleBA("main"), "French", 1.5)
or even more simply from anywhere:
B4X:
c.Initialize2(r.GetBA, "French", 1.5)
 
Last edited:
Top