Android Tutorial Inline Java Code

The next versions of B4A (4.30) and B4J (2.80) will allow you to embed Java code inside your B4X code. B4i supports similar feature with Objective C.

The purpose of this feature is to make it easier to access third party SDKs and also to allow developers to add existing Java code snippets to their projects.

Adding Java code is done with an #If Java block:
B4X:
#If JAVA
public String FirstMethod() {
   return "Hello World!";
}
#End If

You need an instance of JavaObject to access the Java methods:
B4X:
Sub Process_Globals
   Private NativeMe As JavaObject
End Sub

Sub Globals

End Sub

Sub Activity_Create(FirstTime As Boolean)
   If FirstTime Then
     NativeMe.InitializeContext
   End If
   Dim s As String = NativeMe.RunMethod("FirstMethod", Null)
   Log(s) 'will print Hello World!
End Sub
The Java code can also include imports. The compiler will find these imports and add them at the top of the class.

The Java code will be added at the end of the class. Right before the closing bracket.

Methods added to Activity or Service modules will be executed with the component context ('this' will be the activity or service instance).

Hooks

Hooks are similar to the standard events but they run Java methods. Hooks are mainly used by SDKs that require you to add code in the various Activity events.
Note that there are no hooks in B4J implementation.

For example the following code will run in the standard Activity onCreate method (before the call to setContentView):
B4X:
#If JAVA
public void _onCreate() {
   requestWindowFeature(Window.FEATURE_INDETERMINATE_PROGRESS);
}
#End If

Activity hooks:
_onCreate()
_onResume()
_onPause()
_onDestroy()
_onStop()
_onStart()
_onPrepareOptionsMenu (android.view.Menu menu)
boolean _onCreateOptionsMenu (android.view.Menu menu) <--- If you return true from this method then the standard B4A code in onCreateOptionsMenu will not run.
boolean _onkeydown (int keyCode, android.view.KeyEvent event) <-- Return true from this method to return true from the native onKeyDown (and skip Activity_KeyPress event).
boolean _onkeyup (int keyCode, android.view.KeyEvent event) <-- same comment as above

Service hooks:
_onCreate()
_onStartCommand(Intent intent, int flags, int startId)
_onDestroy()

Tips

- The methods you add should be public methods.
- Methods in static code modules should be public static.
- See the attached project. It shows how to call Java methods in all types of modules.

Don't misuse this feature. There are no performance advantages for using it and it can make your project more difficult to debug and maintain.
 

Attachments

  • InlineJava.zip
    12.7 KB · Views: 3,166
  • B4J-InlineJava.zip
    1.2 KB · Views: 1,744
Last edited:

MarcoRome

Expert
Licensed User
Hi all i have this code:

B4X:
....
If FirstTime Then
NativeMe.InitializeContext
EndIf
Dim w AsBoolean
w = NativeMe.RunMethod("isInstalled", Null) ' <----- I thin kthat i have here ERROR
Log("Whatsapp " & w)
 
......
 
#If JAVA
import java.io.File;
import android.content.Context;
 
import java.util.ArrayList;
import android.content.ContentProviderOperation;
import android.content.ContentResolver;
import android.content.Intent;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.database.Cursor;
import android.net.Uri;
import android.provider.ContactsContract;
import android.util.Log;
 
private final static String WHATSSAP_APP_ID = "com.whatsapp";
private final static boolean DEBUG = false;
     
public final static int IMAGE_TYPE = 0;
public final static int AUDIO_TYPE = 1;
public final static int VIDEO_TYPE = 2;
     
    public final static boolean isInstalled(Context context){
         Boolean installed = false;
         try{
             PackageManager mPm = context.getPackageManager();
             PackageInfo info = mPm.getPackageInfo(WHATSSAP_APP_ID, 0);
             installed = info != null;
         }catch(Exception e){
             installed = false;
             if(DEBUG) Log.e("Exception", e.toString());
         }
         return installed;
     }
 
#End If

compile and when start app i have this error:

B4X:
** Activity (main) Create, isFirst = true **
 
java.lang.RuntimeException: Method: isInstalled not matched.
 
    at anywheresoftware.b4j.object.JavaObject.RunMethod(JavaObject.java:128)
    at b4a.example.main._activity_create(main.java:351)
    at java.lang.reflect.Method.invokeNative(Native Method)
    at java.lang.reflect.Method.invoke(Method.java:515)
    at anywheresoftware.b4a.BA.raiseEvent2(BA.java:187)
    at b4a.example.main.afterFirstLayout(main.java:112)
    at b4a.example.main.access$100(main.java:29)
    at b4a.example.main$WaitForLayout.run(main.java:90)
    at android.os.Handler.handleCallback(Handler.java:733)
    at android.os.Handler.dispatchMessage(Handler.java:95)
    at android.os.Looper.loop(Looper.java:136)
    at android.app.ActivityThread.main(ActivityThread.java:5017)
    at java.lang.reflect.Method.invokeNative(Native Method)
    at java.lang.reflect.Method.invoke(Method.java:515)
    at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:779)
    at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:595)
    at dalvik.system.NativeStart.main(Native Method)
java.lang.RuntimeException: Method: isInstalled not matched.

Any idea ?
Thanks
 

mlc

Active Member
Licensed User
@mlc it is a bug in JavaObject library. It will only happen in debug mode. It is fixed for the next update.

The workaround is to call JavaObject.InitializeContext once from Activity_Create:
B4X:
If FirstTime Then
Dim jo As JavaObject
jo.InitializeContext
...

Thanks Erel
It is true in release mode works well.


DEbug rapid:

Can't use FirstTime, is a service.
But i have copied the code to the main, and I call it from the service this way:

B4X:
dim l as long
l =  Main.nativeMe.RunMethod("FreeSpace", Array(File.DirRootExternal))

Works well but, Is correct ?

Thanks
 

Philip Chatzigeorgiadis

Active Member
Licensed User
I understand how I can have more than one parameters of the same type (from previous posts):

NativeMe.Runmethod("methodName",Array("String1","String2"))

and

#If java
public void methodName(String s1,String s2){
BA.Log(s1 + " " + s2);
}#End If


But how can I have parameters of different types, e.g. a string and an int?
More important: Can I have a list or a Map as a parameter?
 

Johan Schoeman

Expert
Licensed User
I understand how I can have more than one parameters of the same type (from previous posts):

NativeMe.Runmethod("methodName",Array("String1","String2"))

and

#If java
public void methodName(String s1,String s2){
BA.Log(s1 + " " + s2);
}#End If


But how can I have parameters of different types, e.g. a string and an int?
More important: Can I have a list or a Map as a parameter?
NativeMe.Runmethod("methodname", Array(stringvariable name, integer variable name))

And then

Public void methodName(String s1, int myint){
Etc....
 

Rusty

Well-Known Member
Licensed User
I am trying to set the Google Verify Apps function OFF.
I've included inline java code to do this, but am having difficulty compiling this code:
B4X:
Sub Process_Globals
    Private NativeMe As JavaObject
End Sub

Sub Globals
End Sub

Sub Activity_Create(FirstTime As Boolean)
    If FirstTime Then
           NativeMe.InitializeContext
    End If
    NativeMe.RunMethod("VerifyApp", Null)
End Sub

Sub Activity_Resume

End Sub

Sub Activity_Pause (UserClosed As Boolean)

End Sub

#If JAVA
public String FirstMethod() {
   return "Hello World!";
}
public void VerifyApp() {
    boolean success = true;
    boolean enabled = Settings.Secure.getInt(context.getContentResolver(), "package_verifier_enable", 1) == 1;
    If (enabled) {
        success = Settings.Secure.putString(context.getContentResolver(), "package_verifier_enable", "0");
    }
}
#End If
I took the Hello world java from Erel's post and it compiles and works fine. The VerifyApp results in:
B4A version: 5.20
Parsing code. (0.00s)
Compiling code. (0.03s)
Compiling layouts code. (0.00s)
Generating R file. (0.05s)
Compiling debugger engine code. (0.74s)
Compiling generated Java code. Error
B4A line: 38
End Sub
javac 1.8.0_51
src\b4a\example\main.java:387: error: ';' expected
If (enabled) {
^
1 error
For some reason, I'm not seeing the error in the Java code. Any suggestions would be appreciated.
Thanks,
Rusty
 

Rusty

Well-Known Member
Licensed User
Thanks Siam.
I corrected that and it still doesn't work.
B4X:
#If JAVA
public void VerifyApp() {
    Settings.Secure.putString(context.getContentResolver(), "package_verifier_enable", "0");
    }
#End If

it is complaining about:
javac 1.8.0_51
src\b4a\example\main.java:1222: error: cannot find symbol
Settings.Secure.putString(context.getContentResolver(), "package_verifier_enable", "0");
^
symbol: variable context
location: class main
I am trying to set the Google Verify Apps function OFF. Is there a better way?
thanks,
Rusty
 

Informatix

Expert
Licensed User
Thanks Siam.
I corrected that and it still doesn't work.
B4X:
#If JAVA
public void VerifyApp() {
    Settings.Secure.putString(context.getContentResolver(), "package_verifier_enable", "0");
    }
#End If

it is complaining about:
javac 1.8.0_51
src\b4a\example\main.java:1222: error: cannot find symbol
Settings.Secure.putString(context.getContentResolver(), "package_verifier_enable", "0");
^
symbol: variable context
location: class main
I am trying to set the Google Verify Apps function OFF. Is there a better way?
thanks,
Rusty
Replace Settings.Secure by android.provider.Settings.Secure and context by getApplicationContext(), then set the appropriate permissions in the manifest.
 

Rusty

Well-Known Member
Licensed User
Perfect! Thanks Informatix!
I changed it to:
B4X:
#If JAVA
public void VerifyApp() {
    boolean success = true;
    boolean enabled = android.provider.Settings.Secure.getInt(getApplicationContext().getContentResolver(), "package_verifier_enable", 1) == 1;
    if (enabled) {
        success = android.provider.Settings.Secure.putString(getApplicationContext().getContentResolver(), "package_verifier_enable", "0");    }
}
#End If

With manifest change of:
B4X:
<uses-permission android:name="android.permission.WRITE_SETTINGS" />
<uses-permission android:name="android.permission.WRITE_SECURE_SETTINGS" />
Compiles ...:)
Thanks,
Rusty
 

Rusty

Well-Known Member
Licensed User
I have another in-line Java question.
I would like to display the available widgets from which the user can select and drag to their home screen.

I've tried to include this:
B4X:
#If JAVA
public void widgetctl() {
    Intent pickIntent = new Intent(AppWidgetManager.ACTION_APPWIDGET_PICK);
    pickIntent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, AppWidgetID);
    startActivityForResult(pickIntent, KEY_CODE);
}
#End if
In my previous post, Informatix suggested "android.provider.Settings.Secure" changes.
I am not clear as to how to determine what changes are required to make Java actually work within the B4a environment.
Any suggestions are appreciated.
Thanks,
Rusty
 

Rusty

Well-Known Member
Licensed User
Thanks Erel!

SORRY, I HAVE MOVED THIS TO A NEW THREAD...
Inline Java intents

question on that post though, shouldn't the below sub be "Sub GetBA as JavaObject"?
B4X:
Sub GetBA As Object
   Dim jo As JavaObject
   Dim cls As String = Me
   cls = cls.SubString("class ".Length)
   jo.InitializeStatic(cls)
   Return jo.GetField("processBA")
End Sub
Also, where can I find a list of intents and when do we preface the intent with "android.intent.action.xxxxxxx"?

Regards,
Rusty
 
Last edited:

Lee Gillie CCP

Active Member
Licensed User
Probably doing some dumb nu-bee thing, here, but this keeps failing at runtime, and I can't see the problem.

I am calling from a Module named Globals

B4X:
Sub Process_Globals
    ...
    Private NativeMe As JavaObject
End Sub

B4X:
Public Sub Init
     ...
     NativeMe.InitializeContext
End Sub

#if JAVA
public static anywheresoftware.b4a.objects.collections.List GetStackTraceJava()
{
    anywheresoftware.b4a.objects.collections.List _lst = null;
    _lst = new anywheresoftware.b4a.objects.collections.List();
    _lst.Initialize();
    _lst.Add((Object)("Test 1"));
    _lst.Add((Object)("Test 2"));
    if (true) return _lst;
    return null;
}
#End If

Public Sub GetStackTrace() As List
    Return NativeMe.RunMethod("GetStackTraceJava",Null)
End Sub

and the failure I see is...

globals_getstacktrace (java line: 348)
java.lang.RuntimeException: Method: GetStackTraceJava not found in: odp.eljaydelivery.main
at anywheresoftware.b4j.object.JavaObject$MethodCache.getMethod(JavaObject.java:367)
at anywheresoftware.b4j.object.JavaObject.RunMethod(JavaObject.java:118)
at odp.eljaydelivery.globals._getstacktrace(globals.java:348)
at odp.eljaydelivery.globals._reporterror(globals.java:922)
at odp.eljaydelivery.main._btntest_click(main.java:513)
at java.lang.reflect.Method.invoke(Native Method)
at java.lang.reflect.Method.invoke(Method.java:372)
at anywheresoftware.b4a.BA.raiseEvent2(BA.java:187)
at anywheresoftware.b4a.BA.raiseEvent2(BA.java:175)
at anywheresoftware.b4a.BA.raiseEvent(BA.java:171)
at anywheresoftware.b4a.objects.ViewWrapper$1.onClick(ViewWrapper.java:78)
at android.view.View.performClick(View.java:4764)
at android.view.View$PerformClick.run(View.java:19844)
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:5376)
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:908)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:703)
java.lang.RuntimeException: Method: GetStackTraceJava not found in: odp.eljaydelivery.main

Any clues would be greatly appreciated.
 
Top