Android Tutorial Create a B4X library with Android Studio - Step by step

Hi,

I share with you my method to create a library for B4X, based on corwin42's post (Create a wrapper library with Android Studio).
Feel free to improve it and share your modifications.


🙏 My great thanks to corwin42 🙏


⚠️ I work on Linux, so you may need to change the file separators. ⚠️

1 - Creating a new project
Select "Phone and Tablet" and "No Activity"

Click 'Next'
1642536926043.png
Select
  1. package name :
    "myTestProject" (for this tutorial)
  2. save location
  3. langage : java
  4. minimum SDK API level your library :
    21 (for this tutorial)

Click 'Finish'

⚠️Wait for Gradle sync... (about 1 minute)​
1642536926064.png
2 - Delete the default "app" module
By default a module named "app" is created. We don't need this,
so we can delete it.
1642537049007.png
  1. Select File/Project Structure...
    <Ctrl+Alt+Maj+s>
  2. Select "app" in the modules list
  3. Click the "-" button at the top of the list.
  4. Confirm dialog
  5. Click 'Ok'
1642537049030.png
Delete directory app in project files.
1642537049058.png
3 - Add Core and B4AShared jar libraries
Select File->New->Directory
Name it 'libs'
1642537338094.png
Open 'libs' directory and add the following files, from your B4A installation folder
(usually in the folder C:\Program Files (x86)\Anywhere Software\Basic4android\Libraries\) :
  • B4AShared.jar
  • Core.jar
1642537338112.png
4 - Modify build.gradle for the project
Dependencies
  • Classpath : select lastest version 'of com.android.tools.build:gradle'
Allprojects / repositories
  • Remove jcenter()
Click 'Sync now'
1642537422956.png
5 - Create your library
  1. Select File->New->New module...
  2. Select "Android Library"
  3. Name your library (We use default name "mylibrary" for this tutorial)
  4. Click Finish
1642537477675.png
Change build method for your Library :
  1. Menu Build->Select Build Variant...
  2. Select Release
1642537477693.png
6 - Delete files from testing framework
⚠️The default project creates some code for the testing framework we don't need, so we delete these files/folders.⚠️
Select the "Project" view and open the src tree. There are three subfolders.
Main is our main source tree. androidTest and test folders can be deleted.
Right click them and select Delete.
1642537551076.png
7 - Add a dummy/myLib class
B4A always expects a .jar file as a library. With our AS project we can only create .aar files. So we have to include a dummy class in our release.

I declare Author, Version and DependsOn in this class, nothing else.
1642538639864.png
8 - Under module mylibrary, Modify Build.gradle file
Replace build.gradle for our library. You can use a full example at end of this document.

You must adapt the follow definitions :
  • def BADoclet_Path = '/home/olivier/Documents/01-projets/01-Java/BADoclet'
  • def B4A_AdditionalLibs = '/home/olivier/Documents/01-projets/02-Android/B4A_OtherLibs'
  • def Optional_StaticClass = 'none'
  • def Optional_B44IgnoreClasses = 'none'
1642538684584.png
Select the Spinner in the toolbar (it may show the "app" build configuration) and select "Edit Configuration..."
1642538817820.png

Remove app
1642538848313.png


That's all folks ;)

Build.gradle for project:
Build.gradle for project:
// Top-level build file where you can add configuration options common to all sub-projects/modules.
buildscript {
    repositories {
        google()
        mavenCentral()
    }
    dependencies {
        classpath "com.android.tools.build:gradle:4.2.2"
 
        // NOTE: Do not place your application dependencies here; they belong
        // in the individual module build.gradle files
    }
}
 
allprojects {
    repositories {
        google()
        mavenCentral()
    }
}
 
task clean(type: Delete) {
    delete rootProject.buildDir
}

Build.gradle for module :
Build.gradle for module:
apply plugin: 'com.android.library'
 
 
def BADoclet_Path = '/home/olivier/Documents/01-projets/01-Java/BADoclet'
def B4A_AdditionalLibs = '/home/olivier/Documents/01-projets/02-Android/B4A_OtherLibs'
def Optional_B4AStaticClass = 'none'
def Optional_B44IgnoreClasses = 'none'
 
def buildTempLibs = "$buildDir" + File.separator + "tmp" + File.separator + "libs"
def buildClasses = "$buildDir" + File.separator + "classes"
 
android {
    compileSdkVersion 30
    buildToolsVersion "30.0.2"
 
    buildFeatures {
        buildConfig = false
    }
 
    defaultConfig {
        minSdkVersion 14
        targetSdkVersion 30
        versionCode 1
        versionName "1.0"
 
    }
    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
        }
    }
 
    compileOptions {
        sourceCompatibility JavaVersion.VERSION_1_8
        targetCompatibility JavaVersion.VERSION_1_8
    }
 
    sourceSets {
        main {
            java {filter.excludes = ["dummy/*.java"]}
        }
    }
 
}
 
dependencies {
    implementation fileTree(include: ['*.jar'], dir: 'libs')
    compileOnly files('../libs/b4a/Core.jar')
    compileOnly files('../libs/b4a/B4AShared.jar')
   // Implementation sample :
    implementation 'com.github.rey5137:material:1.3.1'
    implementation 'androidx.annotation:annotation:1.2.0'
    implementation 'androidx.appcompat:appcompat:1.3.1'
    implementation 'androidx.cardview:cardview:1.0.0'
    implementation 'androidx.recyclerview:recyclerview:1.2.1'
}
 
configurations {
 
    println("Check directories")
    File BADocletPath = new File (BADoclet_Path)
    if (!BADocletPath.exists()){
    throw new FileNotFoundException('BADoclet path not found! Please, check BADoclet_Path.')
}
File B4xPath = new File (B4A_AdditionalLibs)
if (!B4xPath.exists()){
    throw new FileNotFoundException('B4X Additional Libraries path not found! Please, check B4A_AdditionalLibs.')
}
 
/* update Jan 26, 2024 : deleted because it causes an error
    println("Copy api (jar file) into libs directory")
    configurations.api.canBeResolved(true)
    copy {
        from configurations.api
        into 'libs'
    }
*/
 
    println("Prepare environment for Javadoc")
    configurations.implementation.canBeResolved(true)
    configurations.implementation
            .each { archive ->
                if (archive.name.endsWith('.aar')) {
                    copy {
                        from zipTree(archive)
                        include "**"  + File.separator + "classes.jar"
                        into buildTempLibs + File.separator + "${archive.name.replace('.aar', '')}"
                    }
                }
                else {
                    copy {
                        from archive
                        into buildTempLibs
                    }
                }
            }
    configurations.compileOnly.canBeResolved(true)
    configurations.compileOnly
            .each { archive ->
                if (archive.name.endsWith('.aar')) {
                    copy {
                        from zipTree(archive)
                        include "**" + File.separator + "classes.jar"
                        into buildTempLibs + File.separator + "${archive.name.replace('.aar', '')}"
                    }
                }
                else {
                    copy {
                        from archive
                        into buildTempLibs
                    }
                }
            }
 
}
 
tasks.register("b4xJavadoc", Javadoc) {
    File javadocFile = new File(B4A_AdditionalLibs + File.separator + thisObject.getName() + ".xml")
 
    classpath += project.files(android.getBootClasspath().join(File.pathSeparator))
    classpath += fileTree(dir: buildTempLibs)
 
    source = "src" + File.separator + "main" + File.separator + "java"
 
    options.docletpath = [file(BADoclet_Path)]
    options.doclet = "BADoclet"
    options.addStringOption("b4atarget", javadocFile.absoluteFile.toString())
    options.addStringOption("b4aignore", Optional_B44IgnoreClasses)
}
 
tasks.whenTaskAdded {
    theTask ->
        if (theTask.name.contains("assembleRelease")) {
            theTask.dependsOn(b4xJavadoc)
            doLast{
                println("CreateDummyJar")
                ant.mkdir(dir: buildClasses)
                ant.javac(
                        release: "8",
                        srcdir: "src" + File.separator + "main" + File.separator + "java" + File.separator + "dummy",
                        destdir: buildClasses,
                        debug: "false",
                        includeantruntime: "false",
                        classpath: ".." + File.separator + "libs" + File.separator + "b4a" + File.separator + "B4AShared.jar"
                )
                ant.jar(
                        basedir: buildClasses,
                        jarfile: B4A_AdditionalLibs + File.separator + thisObject.getName() + '.jar'
                )
 
                println("Copy aar files to B4X Additional Libraries")
                copy {
                    from "$buildDir" + File.separator + "outputs" + File.separator + "aar" + File.separator + thisObject.getName() + "-release.aar"
                    into B4A_AdditionalLibs
                    rename(thisObject.getName() + '-release.aar', thisObject.getName() + '.aar')
                }
 
                println("Update Javadoc")
                ant.replaceregexp(
                        file: B4A_AdditionalLibs + File.separator + thisObject.getName() + '.xml',
                        match: '\\<class\\>(\\n *\\<name\\>' + Optional_B4AStaticClass + '\\<\\/name\\>)',
                        replace: '\\<class b4a_type=\'StaticCode\'\\>\\1',
                        flags: 'g',
                        byline: 'false'
                )
            }
        }
}

To build your library : Menu Build->Make Module...
 

Attachments

  • 1642537591575.png
    1642537591575.png
    9.2 KB · Views: 437
  • 1642537591596.png
    1642537591596.png
    47 KB · Views: 376
Last edited:

watesoft

Active Member
Licensed User
Longtime User
Hi,

I share with you my method to create a library for B4X, based on corwin42's post (Create a wrapper library with Android Studio).
Feel free to improve it and share your modifications.


🙏 My great thanks to corwin42 🙏


⚠️ I work on Linux, so you may need to change the file separators. ⚠️

1 - Creating a new project
Select "Phone and Tablet" and "No Activity"

Click 'Next'
View attachment 124389
Select
  1. package name :
    "myTestProject" (for this tutorial)
  2. save location
  3. langage : java
  4. minimum SDK API level your library :
    21 (for this tutorial)

Click 'Finish'

⚠️Wait for Gradle sync... (about 1 minute)​
View attachment 124390
2 - Delete the default "app" module
By default a module named "app" is created. We don't need this,
so we can delete it.
View attachment 124391
  1. Select File/Project Structure...
    <Ctrl+Alt+Maj+s>
  2. Select "app" in the modules list
  3. Click the "-" button at the top of the list.
  4. Confirm dialog
  5. Click 'Ok'
View attachment 124392
Delete directory app in project files.View attachment 124393
3 - Add Core and B4AShared jar libraries
Select File->New->Directory
Name it 'libs'
View attachment 124395
Open 'libs' directory and add the following files, from your B4A installation folder
(usually in the folder C:\Program Files (x86)\Anywhere Software\Basic4android\Libraries\) :
  • B4AShared.jar
  • Core.jar
View attachment 124394
4 - Modify build.gradle for the project
Dependencies
  • Classpath : select lastest version 'of com.android.tools.build:gradle'
Allprojects / repositories
  • Remove jcenter()
Click 'Sync now'
View attachment 124396
5 - Create your library
  1. Select File->New->New module...
  2. Select "Android Library"
  3. Name your library (We use default name "mylibrary" for this tutorial)
  4. Click Finish
View attachment 124397
Change build method for your Library :
  1. Menu Build->Select Build Variant...
  2. Select Release
View attachment 124398
6 - Delete files from testing framework
⚠️The default project creates some code for the testing framework we don't need, so we delete these files/folders.⚠️
Select the "Project" view and open the src tree. There are three subfolders.
Main is our main source tree. androidTest and test folders can be deleted.
Right click them and select Delete.
View attachment 124401
7 - Add a dummy/myLib class
B4A always expects a .jar file as a library. With our AS project we can only create .aar files. So we have to include a dummy class in our release.

I declare Author, Version and DependsOn in this class, nothing else.
View attachment 124412
8 - Under module mylibrary, Modify Build.gradle file
Replace build.gradle for our library. You can use a full example at end of this document.

You must adapt the follow definitions :
  • def BADoclet_Path = '/home/olivier/Documents/01-projets/01-Java/BADoclet'
  • def B4A_AdditionalLibs = '/home/olivier/Documents/01-projets/02-Android/B4A_OtherLibs'
  • def Optional_StaticClass = 'none'
  • def Optional_B44IgnoreClasses = 'none'
View attachment 124413
Select the Spinner in the toolbar (it may show the "app" build configuration) and select "Edit Configuration..."View attachment 124414
Remove appView attachment 124415

That's all folks ;)

Build.gradle for project:
Build.gradle for project:
// Top-level build file where you can add configuration options common to all sub-projects/modules.
buildscript {
    repositories {
        google()
        mavenCentral()
    }
    dependencies {
        classpath "com.android.tools.build:gradle:4.2.2"
 
        // NOTE: Do not place your application dependencies here; they belong
        // in the individual module build.gradle files
    }
}
 
allprojects {
    repositories {
        google()
        mavenCentral()
    }
}
 
task clean(type: Delete) {
    delete rootProject.buildDir
}

Build.gradle for module :
Build.gradle for module:
apply plugin: 'com.android.library'
 
 
def BADoclet_Path = '/home/olivier/Documents/01-projets/01-Java/BADoclet'
def B4A_AdditionalLibs = '/home/olivier/Documents/01-projets/02-Android/B4A_OtherLibs'
def Optional_B4AStaticClass = 'none'
def Optional_B44IgnoreClasses = 'none'
 
def buildTempLibs = "$buildDir" + File.separator + "tmp" + File.separator + "libs"
def buildClasses = "$buildDir" + File.separator + "classes"
 
android {
    compileSdkVersion 30
    buildToolsVersion "30.0.2"
 
    buildFeatures {
        buildConfig = false
    }
 
    defaultConfig {
        minSdkVersion 14
        targetSdkVersion 30
        versionCode 1
        versionName "1.0"
 
    }
    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
        }
    }
 
    compileOptions {
        sourceCompatibility JavaVersion.VERSION_1_8
        targetCompatibility JavaVersion.VERSION_1_8
    }
 
    sourceSets {
        main {
            java {filter.excludes = ["dummy/*.java"]}
        }
    }
 
}
 
dependencies {
    implementation fileTree(include: ['*.jar'], dir: 'libs')
    compileOnly files('../libs/b4a/Core.jar')
    compileOnly files('../libs/b4a/B4AShared.jar')
   // Implementation sample :
    implementation 'com.github.rey5137:material:1.3.1'
    implementation 'androidx.annotation:annotation:1.2.0'
    implementation 'androidx.appcompat:appcompat:1.3.1'
    implementation 'androidx.cardview:cardview:1.0.0'
    implementation 'androidx.recyclerview:recyclerview:1.2.1'
}
 
configurations {
 
    println("Check directories")
    File BADocletPath = new File (BADoclet_Path)
    if (!BADocletPath.exists()){
    throw new FileNotFoundException('BADoclet path not found! Please, check BADoclet_Path.')
}
File B4xPath = new File (B4A_AdditionalLibs)
if (!B4xPath.exists()){
    throw new FileNotFoundException('B4X Additional Libraries path not found! Please, check B4A_AdditionalLibs.')
}
 
    println("Copy api (jar file) into libs directory")
    configurations.api.canBeResolved(true)
    copy {
        from configurations.api
        into 'libs'
    }
 
    println("Prepare environment for Javadoc")
    configurations.implementation.canBeResolved(true)
    configurations.implementation
            .each { archive ->
                if (archive.name.endsWith('.aar')) {
                    copy {
                        from zipTree(archive)
                        include "**"  + File.separator + "classes.jar"
                        into buildTempLibs + File.separator + "${archive.name.replace('.aar', '')}"
                    }
                }
                else {
                    copy {
                        from archive
                        into buildTempLibs
                    }
                }
            }
    configurations.compileOnly.canBeResolved(true)
    configurations.compileOnly
            .each { archive ->
                if (archive.name.endsWith('.aar')) {
                    copy {
                        from zipTree(archive)
                        include "**" + File.separator + "classes.jar"
                        into buildTempLibs + File.separator + "${archive.name.replace('.aar', '')}"
                    }
                }
                else {
                    copy {
                        from archive
                        into buildTempLibs
                    }
                }
            }
 
}
 
tasks.register("b4xJavadoc", Javadoc) {
    File javadocFile = new File(B4A_AdditionalLibs + File.separator + thisObject.getName() + ".xml")
 
    classpath += project.files(android.getBootClasspath().join(File.pathSeparator))
    classpath += fileTree(dir: buildTempLibs)
 
    source = "src" + File.separator + "main" + File.separator + "java"
 
    options.docletpath = [file(BADoclet_Path)]
    options.doclet = "BADoclet"
    options.addStringOption("b4atarget", javadocFile.absoluteFile.toString())
    options.addStringOption("b4aignore", Optional_B44IgnoreClasses)
}
 
tasks.whenTaskAdded {
    theTask ->
        if (theTask.name.contains("assembleRelease")) {
            theTask.dependsOn(b4xJavadoc)
            doLast{
                println("CreateDummyJar")
                ant.mkdir(dir: buildClasses)
                ant.javac(
                        release: "8",
                        srcdir: "src" + File.separator + "main" + File.separator + "java" + File.separator + "dummy",
                        destdir: buildClasses,
                        debug: "false",
                        includeantruntime: "false",
                        classpath: ".." + File.separator + "libs" + File.separator + "b4a" + File.separator + "B4AShared.jar"
                )
                ant.jar(
                        basedir: buildClasses,
                        jarfile: B4A_AdditionalLibs + File.separator + thisObject.getName() + '.jar'
                )
 
                println("Copy aar files to B4X Additional Libraries")
                copy {
                    from "$buildDir" + File.separator + "outputs" + File.separator + "aar" + File.separator + thisObject.getName() + "-release.aar"
                    into B4A_AdditionalLibs
                    rename(thisObject.getName() + '-release.aar', thisObject.getName() + '.aar')
                }
 
                println("Update Javadoc")
                ant.replaceregexp(
                        file: B4A_AdditionalLibs + File.separator + thisObject.getName() + '.xml',
                        match: '\\<class\\>(\\n *\\<name\\>' + Optional_B4AStaticClass + '\\<\\/name\\>)',
                        replace: '\\<class b4a_type=\'StaticCode\'\\>\\1',
                        flags: 'g',
                        byline: 'false'
                )
            }
        }
}

To build your library : Menu Build->Make Module...
Hello,Many thanks to you for providing such detailed steps!
This is the first time I try to wrap a library for B4A. I followed the steps you provided, but some problems are not very clear and I cannot compile it successfully.The platform I am using is "Win10" & "Android Studio Bumblebee 2021.1.1 Patch 2".
Q1:In step 7,How to add dummy directory?
Q2:In step 8,How to specify the path of BADoclet_Path and B4A_AdditionalLibs? don't understand what it means, I copy it from your Build.gradle, an error showing:"BADoclet path not found! Please, check BADoclet_Path".
 
Last edited:

watesoft

Active Member
Licensed User
Longtime User
Last edited:

watesoft

Active Member
Licensed User
Longtime User
Right-click -> New -> Package

BADoclet: Download and extract zip from https://www.b4x.com/android/forum/t...mpiler-build-libraries-without-eclipse.29918/
B4A_AdditionalLibs: Folder from B4A IDE: Tools -> Configure Paths
Hi Spavlyuk.
I try to wrap a ChineseTTS library based on https://github.com/jinguangyang/ChineseTTS, tensorflow-lite was introduced due to artificial intelligence. I download AS sourcecode and build APK, unzip it and find two so files in armeabi-v7a folder,they are the dependency files of tensorflow-lite.There are also two trained sound model files(*.tflite), and a jar file(pinyin4j-2.5.1.jar) for formatting text.
I create libs folder, put *.so files and jar files into it,I can't confirm if this is correct as there doesn't seem to be such a complex example with AS in the forum,hope to have an example or detailed steps for reference.
When I run make moudle, 1 error shows:“Cannot change resolution strategy of dependency configuration ':chinesetts:api' after it has been resolved”,I'm new to AS and don't know how to fix this, so the compilation fails.I want your help,thank you first.
Sorry,the *.so files and *.tflite files are too huge,I can't upload them.

1.png
 

Attachments

  • gradle file.txt
    6.5 KB · Views: 169

Spavlyuk

Active Member
Licensed User
Unfortunately I'm not familiar with wrapping .so libraries, you can find a few threads about it on this forum.
However, the particular error seems related to Gradle from what I can find on Google. I'd suggest looking at some of the StackOverflow answers, or maybe someone else can help you out.
 

watesoft

Active Member
Licensed User
Longtime User
Unfortunately I'm not familiar with wrapping .so libraries, you can find a few threads about it on this forum.
However, the particular error seems related to Gradle from what I can find on Google. I'd suggest looking at some of the StackOverflow answers, or maybe someone else can help you out.
Thank you for your advice anyway, I searched the forums but got very little AS related, sincerely hope someone can provide something related. I tried to use SLC but also encountered some problems.
 

watesoft

Active Member
Licensed User
Longtime User
It is easier not to use Android Studio when wrapping a library.
I am alwyays using Eclipse and SLC to compile the library.
Hi DonManfred,thanks for your advice.
I've used SLC, but SLC doesn't seem to support lambda expressions, I installed the latest JAVA, but it can't solve the problem, I don't know much about java and it's hard to solve this question.
 
Last edited:

Livio F

Member
Licensed User
Longtime User
I get the error below when I sync. could someone help.

Build file 'C:\_Kotlin\myTestProject\mylibrary\build.gradle' line: 76

A problem occurred evaluating project ':mylibrary'.
> Could not find method from() for arguments [configuration ':mylibrary:api'] on configuration ':mylibrary:copy' of type org.gradle.api.internal.artifacts.configurations.DefaultConfiguration.
Build.gradle for module ::
    println("Copy api (jar file) into libs directory")
    configurations.api.canBeResolved(true)
    copy {
        from configurations.api
        into 'libs'
    }
 

Attachments

  • myTestProject.zip
    341.5 KB · Views: 40

zolive33

Active Member
Licensed User
Longtime User
I get the error below when I sync. could someone help.

Build file 'C:\_Kotlin\myTestProject\mylibrary\build.gradle' line: 76

A problem occurred evaluating project ':mylibrary'.
> Could not find method from() for arguments [configuration ':mylibrary:api'] on configuration ':mylibrary:copy' of type org.gradle.api.internal.artifacts.configurations.DefaultConfiguration.
Build.gradle for module ::
    println("Copy api (jar file) into libs directory")
    configurations.api.canBeResolved(true)
    copy {
        from configurations.api
        into 'libs'
    }
Hi,

simply remove these lines :

B4X:
println("Copy api (jar file) into libs directory")
    configurations.api.canBeResolved(true)
    copy {
        from configurations.api
        into 'libs'
    }
 
Top