Android Tutorial Accessing third party Jar with #Additionaljar and JavaObject - Picasso

The #AdditionalJar module attribute (introduced in B4A v3.80) allows us to reference external jars.
With the help of JavaObject it is now possible to integrate third party jars without a wrapper.

This solution is good for "simple" libraries. If the API is complicated with many interfaces then it will be easier to create a wrapper.

As an example we will use Picasso image downloader to download images: http://square.github.io/picasso/

upload_2014-5-12_13-9-8.png


The first step is to download the third party jar and put it in the additional libraries folder.
We then use #AdditionalJar to tell the compiler to add a reference to this jar:
B4X:
#AdditionalJar: picasso-2.2.0
Note that the jar extension is omitted. You can call #AdditionalJar multiple times if multiple jars are required.

The following two subs will usually be required. They allow you to get the "context" (it will be an android.app.Activity when called from an Activity module).

This code should be added to an activity or service directly.
B4X:
Sub GetContext As JavaObject
   Return GetBA.GetField("context")
End Sub

Sub GetBA As JavaObject
  Dim jo As JavaObject
  Dim cls As String = Me
  cls = cls.SubString("class ".Length)
  jo.InitializeStatic(cls)
  Return jo.GetFieldJO("processBA")
End Sub

SS-2014-05-12_13.15.19.png


As you can see in their examples we always start by calling Picasso static method 'with'. It is more clear in the JavaDocs page: http://square.github.io/picasso/javadoc/com/squareup/picasso/Picasso.html

This sub will call the static method:
B4X:
Sub GetPicasso As JavaObject
   Dim jo As JavaObject
   'com.squareup.picasso.Picasso.with(context)
   Return jo.InitializeStatic("com.squareup.picasso.Picasso").RunMethod("with", Array(GetContext))
End Sub

Now we will implement the above Java code:
B4X:
GetPicasso.RunMethodJO("load", Array(url)).RunMethodJO("into", Array(img1))

In the second example we call a more complex API:
B4X:
'second example: Picasso.with(context).load(url).resize(50, 50).centerCrop().into(ImageView)
GetPicasso.RunMethodJO("load", Array(url)).RunMethodJO("resize", Array(50, 50)) _
   .RunMethodJO("centerCrop", Null).RunMethodJO("into", Array(img2))

The third example is more interesting. We download an image with a callback event that is raised when download completes.

The first step it to create the interface. This is done with JavaObject.CreateEvent (or CreateEventFromUI).
In this case we are implementing com.squareup.picasso.Callback: http://square.github.io/picasso/javadoc/com/squareup/picasso/Callback.html
B4X:
Dim callback As Object = jo.CreateEvent("com.squareup.picasso.Callback", "Callback", Null)
The last parameter is the default return value. This value will be used if the event cannot be raised (activity is paused for example). In this case we return Null.
The event sub:
B4X:
Sub Callback_Event (MethodName As String, Args() As Object) As Object
   If MethodName = "onSuccess" Then
     ToastMessageShow("Success!!!", True)
   Else If MethodName = "onError" Then
     ToastMessageShow("Error downloading image.", True)
   End If
   Return Null
End Sub
MethodName - The interface method name (onSuccess or onError in this case).
Args - An array of parameters passed to this method. In this case there are no parameters.
All this information is from Picasso JavaDocs: http://square.github.io/picasso/javadoc/index.html?com/squareup/picasso/Callback.html

The last step is to call the method that expects the callback:
B4X:
GetPicasso.RunMethodJO("load", Array(url)).RunMethodJO("into", Array(img1, callback))

As this library requires the INTERNET permission we need to manually add it to the manifest editor:
B4X:
AddPermission(android.permission.INTERNET)

The complete code:
B4X:
#Region  Project Attributes
   #ApplicationLabel: B4A Example
   #VersionCode: 1
   #VersionName:
   'SupportedOrientations possible values: unspecified, landscape or portrait.
   #SupportedOrientations: unspecified
   #CanInstallToExternalStorage: False
#End Region

#Region  Activity Attributes
   #FullScreen: False
   #IncludeTitle: True
#End Region

#AdditionalJar: picasso-2.2.0

Sub Process_Globals

End Sub

Sub Globals
   Dim img1, img2 As ImageView
End Sub

Sub Activity_Create(FirstTime As Boolean)
   img1.Initialize("")
   Activity.AddView(img1, 0, 0, 100%x, 50%y)
   img2.Initialize("")
   Activity.AddView(img2, 0, 50%y, 100%x, 50%y)
   Dim url As String = "http://i.imgur.com/DvpvklR.png"
   'first example: Picasso.with(context).load(url).into(imageView);
   GetPicasso.RunMethodJO("load", Array(url)).RunMethodJO("into", Array(img1))
  
   'second example: Picasso.with(context).load(url).resize(50, 50).centerCrop().into(ImageView)
   GetPicasso.RunMethodJO("load", Array(url)).RunMethodJO("resize", Array(50, 50)) _
     .RunMethodJO("centerCrop", Null).RunMethodJO("into", Array(img2))
  
   'third example: download image with callback
   Dim jo As JavaObject = GetPicasso
   Dim callback As Object = jo.CreateEvent("com.squareup.picasso.Callback", "Callback", Null)
   GetPicasso.RunMethodJO("load", Array(url)).RunMethodJO("into", Array(img1, callback))
End Sub

Sub Callback_Event (MethodName As String, Args() As Object) As Object
   If MethodName = "onSuccess" Then
     ToastMessageShow("Success!!!", True)
   Else If MethodName = "onError" Then
     ToastMessageShow("Error downloading image.", True)
   End If
   Return Null
End Sub

Sub GetPicasso As JavaObject
   Dim jo As JavaObject
   'com.squareup.picasso.Picasso.with(context)
   Return jo.InitializeStatic("com.squareup.picasso.Picasso").RunMethod("with", Array(GetContext))
End Sub

Sub GetContext As JavaObject
   Return GetBA.GetField("context")
End Sub

Sub GetBA As JavaObject
  Dim jo As JavaObject
  Dim cls As String = Me
  cls = cls.SubString("class ".Length)
  jo.InitializeStatic(cls)
  Return jo.GetFieldJO("processBA")
End Sub

Sub Activity_Resume

End Sub

Sub Activity_Pause (UserClosed As Boolean)

End Sub
 

somed3v3loper

Well-Known Member
Licensed User
Longtime User
As I still want a twitter solution I am trying to use Twitter4J but do not know if I am going the right way or not (which is more likely)

B4X:
Sub GetContext As JavaObject
      Return GetBA.GetField("context")
End Sub

Sub GetBA As JavaObject
      Dim jo As JavaObject
     Dim cls As String = Me
     cls = cls.SubString("class ".Length)
     jo.InitializeStatic(cls)
     Return jo.GetFieldJO("processBA")
End Sub

Sub Twitter As JavaObject
      Dim jo As JavaObject
     Return jo.InitializeStatic("twitter4j.auth")
End Sub
Sub setOAuthConsumer(conskey As String,conscret As String)
       Twitter.RunMethodJO("setOAuthConsumer",Array())
End Sub

For the last SUB I do not know how to pass the two parameters .

Appreciate your help .
 

Jaames

Active Member
Licensed User
Longtime User
For 0% Java guys as I'm, can you please explain how to call simple method as is "multiply" from the FirstLib. (from the tutorial "How to make java Library for Basic4Android")

For example instead to check FirstLib in the library tab, we
reference it with #AdditionalJar: FirstLib.jar

(just for learning purpose)

Thanks
 

Erel

B4X founder
Staff member
Licensed User
Longtime User
@somed3v3loper please start a new thread for this question and also post the link to the Java API you are trying to call.

@Jaames I don't remember the package name of this example. However it should be something like:
B4X:
Dim fb As JavaObject
fb.InitializeNewInstance("package.name.FirstLib", null)
log(fb.RunMethod("Multiply", Array(10, 20))
 

Jaames

Active Member
Licensed User
Longtime User
@Jaames I don't remember the package name of this example. However it should be something like:
B4X:
Dim fb As JavaObject
fb.InitializeNewInstance("package.name.FirstLib", null)
log(fb.RunMethod("Multiply", Array(10, 20))

Ah, it's that simple :eek: great!

Thanks
 

Andris

Active Member
Licensed User
Longtime User
Erel, with B4J, I made a successful connection to a Google Cloud SQL database, using

#AdditionalJar: mysql-connector-java-5.1.30-bin

Does this mean that I can now theoretically make a connection via a B4A app, using the same #AdditionalJar ? Or is my reasoning false?
 

Andris

Active Member
Licensed User
Longtime User
The B4A SQL library will only work with the built-in SQLite engine. Assuming that the MySQL driver is supported by Android then you can try to use B4J jSQL library on Android with the MySQL driver.

Thanks for the suggestion Erel. I'll try this and share results.
 

Andris

Active Member
Licensed User
Longtime User
The B4A SQL library will only work with the built-in SQLite engine. Assuming that the MySQL driver is supported by Android then you can try to use B4J jSQL library on Android with the MySQL driver.

Erel, let's say I wanted to use both B4A SQL and B4J jSQL libraries in my B4A app. The SQL object is referred to as "SQL" in both libraries. Is it possible to specify which one to use in the code? In other words, if I'm working with SQLite, I would want the B4A version of the SQL ... but if working with the MySQL driver, I'd want the B4J version. Is there any way to distinguish between the two?
 

ppgirl

Member
Licensed User
Longtime User
I have a simple problem , I don't if it is a bug.

I have a 3-rd party library need merge into my project :

import com.qq.e.ads.InterstitialAd;

final InterstitialAd iad = new InterstitialAd(this, "1101721111","9079537215654558411");

I change it to follow B4A code ,

Dim wdj As JavaObject

wdj.InitializeNewInstance("com.qq.e.ads.InterstitialAd",Array (GetContext,"1101721111","9079537215654558411"))


when running , it will cause a error:

java.lang.RuntimeException: Constructor not found.

at anywheresoftware.b4j.object.JavaObject.InitializeNewInstance(JavaObject.java:72)
...
at android.os.Handler.dispatchMessage(Handler.java:100)
at android.os.Looper.loop(Looper.java:194)
at android.app.ActivityThread.main(ActivityThread.java:5400)
at java.lang.reflect.Method.invokeNative(Native Method)
at java.lang.reflect.Method.invoke(Method.java:525)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:833)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:600)
at dalvik.system.NativeStart.main(Native Method)
java.lang.RuntimeException: Constructor not found.

Other , if modify to follow lines , it will have not error , but call will have no function .

Dim wdj As JavaObject
wdj.InitializeNewInstance("com.qq.e.ads.InterstitialAd",Array (null,"1101721111","9079537215654558411"))

It looks like call type mis-match in constructor . Please give me some advise ,thanks!
 
Last edited:

stevel05

Expert
Licensed User
Longtime User
Do you have the documentation? What types should the constructors arguments be?
 

ppgirl

Member
Licensed User
Longtime User
No more document ,but it show a java demo code.

private void bindInterstitialAdButton() {
iad = new InterstitialAd(this, Constants.APPId, Constants.InterteristalPosId);
iad.setAdListener(new InterstitialAdListener() {
@Override
public void onBack() {
// iad.loadAd();
}
@Override
public void onFail() {
}
@Override
public void onAdReceive() {
Log.i("admsg:", "receive ad pic");
iad.show(MainDemoActivity.this);
}
});
 

stevel05

Expert
Licensed User
Longtime User
You are passing Constants.APPId and Constants.InterteristalPosId as strings, is this correct? Or should they be ints or other type?
 

ppgirl

Member
Licensed User
Longtime User
I have try "Constants.APPId" and "Constants.InterteristalPosId" with int or string , it is still problem. thanks

Demo code :
.......
public static final String APPId = "1101152570";
public static final String InterteristalPosId = "8935422030341770529";
 

stevel05

Expert
Licensed User
Longtime User
Is the code in a Module or a class?
 

stevel05

Expert
Licensed User
Longtime User
I have to go out now, but if the code is in a class, try this for the GetContext:

B4X:
Sub GetContext As JavaObject
  Return GetBA.GetField("context")
End Sub

Sub GetBA As JavaObject
    Dim jo As JavaObject
    Dim cls As String = Me
    If Not(cls.StartsWith("class")) Then
        cls = GetType(Me)
        cls = cls.SubString2(0,cls.LastIndexOf(".")+1) & "main"
    Else
        cls = Me
        cls = cls.SubString("class ".Length)
    End If
    jo.InitializeStatic(cls)
    Return jo.GetFieldJO("processBA")
End Sub
 
Top