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

Discussion in 'Tutorials & Examples' started by Erel, May 12, 2014.

  1. Erel

    Erel Administrator Staff Member Licensed User

    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:
    Code:
    #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.
    Code:
    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
    [​IMG]

    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:
    Code:
    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:
    Code:
    GetPicasso.RunMethodJO("load"Array(url)).RunMethodJO("into"Array(img1))
    In the second example we call a more complex API:
    Code:
    'second example: Picasso.with(context).load(url).resize(50, 50).centerCrop().into(ImageView)
    GetPicasso.RunMethodJO("load"Array(url)).RunMethodJO("resize"Array(5050)) _
       .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
    Code:
    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:
    Code:
    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:
    Code:
    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:
    Code:
    AddPermission(android.permission.INTERNET)
    The complete code:
    Code:
    #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, 00100%x50%y)
       img2.Initialize(
    "")
       
    Activity.AddView(img2, 050%y100%x50%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(5050)) _
         .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
     
    fredo, rscheel, lemonisdead and 13 others like this.
  2. somed3v3loper

    somed3v3loper Well-Known Member Licensed 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)

    Code:
    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 .
     
  3. Jaames

    Jaames Active Member Licensed 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
     
  4. Erel

    Erel Administrator Staff Member Licensed 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:
    Code:
    Dim fb As JavaObject
    fb.InitializeNewInstance(
    "package.name.FirstLib"null)
    log(fb.RunMethod("Multiply"Array(1020))
     
    ArminKH, eSolution and DonManfred like this.
  5. Jaames

    Jaames Active Member Licensed User

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

    Thanks
     
    Bobi likes this.
  6. Jose Cuevas

    Jose Cuevas Member Licensed User

  7. Erel

    Erel Administrator Staff Member Licensed User

    It will be difficult to create a complete wrapper with JavaObject for this library.

    It will be much easier to use the existing library and extend it with JavaObject (start a new thread if you need more information).
     
  8. Andris

    Andris Member Licensed 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?
     
  9. Erel

    Erel Administrator Staff Member Licensed 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.
     
  10. Andris

    Andris Member Licensed User

    Thanks for the suggestion Erel. I'll try this and share results.
     
    demasi likes this.
  11. Andris

    Andris Member Licensed User

    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?
     
  12. Erel

    Erel Administrator Staff Member Licensed User

    This discussion should have been done in a new thread...

    You can edit jSQL library XML file and change the class shortname element.
     
  13. Andris

    Andris Member Licensed User

    Thanks Erel!
     
  14. ppgirl

    ppgirl Member Licensed 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: Jul 19, 2014
  15. stevel05

    stevel05 Expert Licensed User

    Do you have the documentation? What types should the constructors arguments be?
     
  16. ppgirl

    ppgirl Member Licensed 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);
    }
    });
     
  17. stevel05

    stevel05 Expert Licensed User

    You are passing Constants.APPId and Constants.InterteristalPosId as strings, is this correct? Or should they be ints or other type?
     
  18. ppgirl

    ppgirl Member Licensed 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";
     
  19. stevel05

    stevel05 Expert Licensed User

    Is the code in a Module or a class?
     
  20. stevel05

    stevel05 Expert Licensed User

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

    Code:
    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
     
Loading...