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
 

ppgirl

Member
Licensed User
In activity module.

this is B4A's java code

//BA.debugLineNum = 120;BA.debugLine="wdj.InitializeNewInstance(\"com.qq.e.ads.InterstitialAd\",Array (GetContext ,\"1101721111\",\"9079537215654558411\"))";
mostCurrent._wdj.InitializeNewInstance("com.qq.e.ads.InterstitialAd",new Object[]{(Object)(_getcontext().getObject()),(Object)("1101721111"),(Object)("9079537215654558411")});
 

stevel05

Expert
Licensed User
Can you zip and post your project (and the library jar) and a link to the documentation / demo code.
 

ppgirl

Member
Licensed User
Hi Stevel,

There is the SDK and B4A demo code.

SDK is a whole Java demo code sample.zip and jar.

B4A demo code sdkdemo.zip is made by me , you can check the problem.

please copy those jar to library directory.

Thanks!
 

Attachments

  • sdkdemo.zip
    384.6 KB · Views: 415
  • android-query-full.0.26.7.jar
    204.4 KB · Views: 340
  • android-support-v4.jar
    472.9 KB · Views: 330
  • gdt_mob_release_v4.2.447.jar
    146.1 KB · Views: 280
  • sample.zip
    62.5 KB · Views: 387

stevel05

Expert
Licensed User
OK, the constructor doesn't want the context as an argument, it wants the activity, which you can get using this code (Requires the reflection library):

B4X:
Sub GetActivity As Object
    Dim R  As Reflector
    Return R.GetActivity
End Sub

Then:

B4X:
Sub Activity_Create(FirstTime As Boolean)
    'Do not forget to load the layout file created with the visual designer. For example:
    Activity.LoadLayout("main")

    wdj.InitializeNewInstance("com.qq.e.ads.InterstitialAd",Array (GetActivity ,"1101721115","9079537215654558411"))
    wdjop1=wdj.CreateEvent("com.qq.e.ads.InterstitialAdListener", "callbackwdj", Null)     
    wdj.RunMethod("setAdListener",Array(wdjop1))
    wdj.RunMethod("loadAd", Null)   
End Sub

Without the JavaDoc, it's not going to be possible to help much further I'm afraid. I tried translating their website into English, but couldn't find anything useful.

I hope this get's you up and running.
 

ppgirl

Member
Licensed User
Great! It is OK , the SDK is a AD SDK.

I forget it can get activity by Reflector.

Thanks!!!
 

ivan.tellez

Active Member
Licensed User
Hi

Im triing to use use an external jar to render SVG files https://code.google.com/p/androidsvg/

B4X:
Sub GetSVG(SVGString As String) As JavaObject
  Dim jo As JavaObject
  Return jo.InitializeStatic("com.caverock.androidsvg.SVG").RunMethod("getFromString", Array(SVGString))
End Sub




Sub Button1_Click
   Dim Canvas1 As Canvas
   Dim MyBMP As Bitmap
   MyBMP.InitializeMutable(512,512)
   Canvas1.Initialize2(MyBMP)
   
   Dim SVGString As String = File.ReadString(File.DirAssets, "android.svg")
   
   'Log(GetSVG(SVGString).RunMethod("getDocumentWidth", Null))     'This works
   
   'GetSVG(SVGString).RunMethod("renderToCanvas", Array(Canvas1))   'This is not working
   'GetSVG(SVGString).RunMethod("renderToPicture", Null)        'Returns an Android.Graphics.Picture, How to convert to Android.Graphics.Bitmap?
End Sub

I can load the SVG file, but cant use the render methods, any idea in how to get this working?

Thanks
 
Last edited:

stevel05

Expert
Licensed User
The B4a Canvas is a wrapper for the android.graphics.Canvas object. You need to get the android object from the wrapper:

B4X:
Dim CnvJO As JavaObject = Canvas1
GetSVG(SVGString).RunMethod("renderToCanvas", Array(CnvJO.GetField("canvas")))

I hadn't come across the android.graphics.Picture class, but looking at the documentation, to make it usable in B4a you'll need to render it to a canvas. So it seems a bit redundant unless you want the functionality of the Picture class itself. A quick test didn't work properly so that'll be an investigation you'll need to carry out if you want to use it.
 
Last edited:

ivan.tellez

Active Member
Licensed User
Hi, can you help me with another question please, how can you use a Type that its not accesible in B4A

For example, if there is a function in the java code like

public RectF getDocumentViewBox()

How can use the RectF object in B4A? If I use a standard Rect, it raises an error

B4X:
Dim tRect as Rect

Thanks
 

ivan.tellez

Active Member
Licensed User
Wow, it works the other way too.

B4X:
    Dim rect1 As JavaObject
    rect1.InitializeNewInstance("android.graphics.RectF", Null)
    rect1 = SVG.RunMethod("getDocumentViewBox", Null)


Thanks :D
 
Last edited:

Steini1980

Member
Licensed User
B4X:
GetPicasso.RunMethodJO("load", Array(url)).RunMethodJO("into", Array(img1))

Works fine, but how can I save the Image in ImageView "img1" ???
Img1.Bitmap seems empty, but the Image will be shown on Imageview.

Could someone help me saving the Image loaded via Picasso into File?!
 

Steini1980

Member
Licensed User
At the moment I have tried only without callback event. But I have used Sleep Function to wait 10 Seconds, i saw the picture in ImageView befor timer was zero. But the img1.Bitmap was empty too. Did you think I should try with Callback? Maybe the Bitmap will stored as Background Image or if possible only drawed at the control? What can I try else? Is it possible to catch the data directly from content:// url, without storing in ImageView (I also tried via httpUtils2 but it doesn't it seems only working with http:// urls?
 

Steini1980

Member
Licensed User
If I try Callback with Log("IMG1 BG: "& img1.Background &" BMP: " & img1.Bitmap,True) then

Background has a Value and Bitmap is Null in Callback.
Because Background has also a Value when I start Activity, I don't know if the value has changed after Callback.

How can I catch the Background Value as File?
 
Last edited:

a2stepper

Member
Licensed User
i have copy and pasted your example from above and get this error:

Parsing code. Error
Error parsing program.
Error description: Attribute not supported: additionaljar
Occurred on line: 16
#AdditionalJar: picasso-2.2.3

using B4A version 3.20.
is there anything else i need to do to get working.
thanks.
paul
 

a2stepper

Member
Licensed User
when i copy and pasted the code from the above, and ran it i got this message

#AdditionalJar: picasso-2.2.3 is unknown??

im using B4A ver 3.20

is there something else to get working?
thanks.
paul
 

tunderin

Member
Licensed User
A no java knowledge question - can I use #AdditionalJar with a .so library?
 
Top