New video tutorial is available here: http://www.b4x.com/forum/libraries-...1-video-tutorial-creating-simple-library.html
Basic4android has a very good support for external libraries. In fact almost all of the internal keywords are loaded from an external library (Core library).
In order to develop a library you need to have Eclipse installed.
The Android plug-in is not required as the libraries are regular Java libraries.
Knowledge required:
- Java
- Android main concepts: http://www.b4x.com/forum/basic4andr...87-android-process-activities-life-cycle.html
The IDE is a .Net process. It cannot load or inspect jar files. Therefore together with each jar file there is an XML file that describes the file to the IDE. This file also holds the documentation for the online help.
The XML file is created by using a custom Javadoc tool.
Creating a new project:
1. Create a new Java project (not Android project).
2. Add a reference to the following jars: android.jar, B4AShared.jar and optionally to Core.jar.
This is done by right clicking on the project name - Build Path - Add External Jars.
android.jar is part of the Android SDK.
By default you should choose the android.jar that is located under <android-folder>\platforms\android-4.
That way your library will work with Android v1.6 and above (which is the same as the other libraries). If you don't see android-4 then you need to download this SDK with the SDK manager.
B4AShared.jar and Core.jar are located in Basic4android libraries folder.
An updated B4AShared.jar is included in the attached file. This is a minor update which is only relevant if you are using Basic4android v1.00.
Writing code:
All public classes and members are exposed to B4A.
Members can be either static or non-static. In both ways the user accesses the members with the 'Dimmed' object.
Only the no arguments constructor will be called.
The constructor is called when the object is 'Dimmed'.
Consider the following code:
In this code, b is declared to allow the user to access a different button.
So it would have been a waste if the Button constructor already called the android button constructor (the B4A button is wrapper above the standard button).
This is why many types have an Initialize method. In that cases the Initialize actually calls the standard button constructor.
Actually there is a special class which you can inherit from, for simple wrappers. I will later cover it.
Properties - Methods that start with 'get' and don't have any parameters will be converted to a property. The same is true for methods that start with 'set' and accept one parameter. The parameter types should match if both get and set exist.
=> will appear as Width property which is readable and writable.
BA - BA is a special object that you can use to raise events and to get access to the user activity, application context and other resources.
The compiler is responsible for passing this object. The IDE doesn't expose it.
For example, this is GPS.Start signature:
Here BA is used to raise the GPS events.
And now for something a bit more complicated. There are actually two BA objects for each activity. One is the "process BA" and the other is the "activity BA".
The process BA doesn't hold an activity context (or an activity object). The activity BA does hold a reference to the activity.
If your object is an "Activity object" then you will get the activity BA. Otherwise you will get the process BA.
Activity objects cannot be declared in Sub Process_Globals (as this would cause a memory leak).
You should prefer to create process objects. Only create Activity objects if it is really needed (mainly for UI elements). Note that the ApplicationContext is accessible from the process BA.
Annotations
So assuming that you are still following, the question that should be asked is how do you mark your object as an Activity object? This is done with annotations.
B4AShared.BA has several important annotations.
@ActivityObject - Marking your class with this annotation will make it an activity object.
Additional important annotations:
@ShortName - In order for a class to be accessible it must has a short name annotation. This annotation which receives one string parameter defines the name that the user will see. The user is not familiar with the package or the actual class name. This means that you should not use common names as the short names, otherwise there will be conflicts in the future. You can use a prefix like: eu_FTP.
Here is the declaration of the GPS class:
Again the name of this class could have been different.
@Permissions - An array of strings with the permissions that will be added to the manifest file when the user declares an object of this type.
@Events - An array of strings with the events that are raised by this type.
@Version - Sets the library version. Should only be declared once per library (a library can have many classes).
@Hide - Hides public classes or members.
@author - Receives one string parameter with the author name. This will affect the online documentation (this is not available in B4AShared that comes with v1.00, it is available in the attached file).
NEW - @DependsOn - An array of strings with additional jar files that will be referenced when this library is referenced. Removes the need to add "dummy" xml files.
Documentation
All the documentation is done inside the java classes.
You should use regular javadoc style comments to document methods, fields and classes. Do not use any special tags like @param or @return.
You can use code tags:
The code will be syntax highlighted automatically.
You can also use link tags:
The format is: Link title|URL.
If you have several objects and want to create a library scope documentation you should add a method with the following signature:
Raising events
There are three methods available for raising events:
- raiseEventFromUI: this is the default way to raise an event from a UI object. It sends a message to an internal queue with the event information.
There is no return value as the event is not raised immediately. The reason behind this method is to avoid conflicts with the way Android handles UI events. See this link for more information.
- raiseEvent: directly calls the given sub.
- raiseEventFromDifferentThread - this method should be used when the event is raised from a different thread. The event message will be delegated to the main thread message queue. You can pass null and 0 in the container and TaskId parameters. They are not needed.
Common parameters:
sender - Is the object that the user will get with the Sender keyword.
event - This is the event sub name. This string must be lower cased and it should contain the prefix name and the event.
params - list of objects to pass as the event parameters.
You should make sure that the string is lower cased or it will not find the sub.
For example, this code checks if the user has a sub with the correct signature and then adds the listener:
New tool to simplify the building process: http://www.b4x.com/forum/showthread.php?p=173756
Generating the XML file
This requires some configuration on the first time.
We are using a custom "doclet" to generate the XML file.
Choose Project - Generate JavaDoc.
Choose custom doclet and set the name to BADoclet and path to the doclet folder in the downloaded file.
Make sure that the correct project is marked.
Press Next.
Add a parameter named -b4atarget and write the path of the target file.
Press Finish and the XML should be generated.
You should create the XML in your 'additional libraries' folder.
The jar file should also be copied to this location.
There are several ways to build the jar file.
Personally I'm using a batch file that creates the jar file and copies it to the libraries folder. I'm using a nice utility named AutoHotKey to assign a hot key for this batch file. So I first run the Javadoc tool from eclipse, then press on the hotkey which creates the jar file and copies it to the libraries folder and then right click on the libraries tab in the IDE - Refresh to reload the libraries. From time to time you will get strange MethodNotFoundErrors when you run your program. This happens when you forget to recreate the jar files or you forget to refresh the libraries (the IDE uses a modified Dexer that caches the libraries in order make compilation quicker).
AbsObjectWrapper
If your class is a simple wrapper above another android class then you can inherit from AbsObjectWrapper. The compiler treats such classes differently and automatically converts between the wrapper and the underlying object as required.
It is better not to use AbsObjectWrapper than to use it wrong.
For example, this is the RectWrapper class:
The user can pass objects from this class whenever a standard Rect is expected. There are additional advantages for AbsObjectWrapper.
Basic4android has internal support for running tasks in the background. This will be covered in the future.
Some examples:
PhoneAccelerometer code:
PhoneSMS:
Email:
The Email example is interesting as we are using the regular B4A lists to hold the recipients. The user will write code similar to:
This is useful as we can reuse the functionality of other objects this way and don't need to duplicate existing functionality.
Questions related to libraries development should be asked here: https://www.b4x.com/android/forum/threads/java-creating-libraries-for-basic4android.6810/
See this thread for the default license: http://www.b4x.com/forum/libraries-...important-notice-about-libraries-license.html
Basic4android has a very good support for external libraries. In fact almost all of the internal keywords are loaded from an external library (Core library).
In order to develop a library you need to have Eclipse installed.
The Android plug-in is not required as the libraries are regular Java libraries.
Knowledge required:
- Java
- Android main concepts: http://www.b4x.com/forum/basic4andr...87-android-process-activities-life-cycle.html
The IDE is a .Net process. It cannot load or inspect jar files. Therefore together with each jar file there is an XML file that describes the file to the IDE. This file also holds the documentation for the online help.
The XML file is created by using a custom Javadoc tool.
Creating a new project:
1. Create a new Java project (not Android project).
2. Add a reference to the following jars: android.jar, B4AShared.jar and optionally to Core.jar.
This is done by right clicking on the project name - Build Path - Add External Jars.
android.jar is part of the Android SDK.
By default you should choose the android.jar that is located under <android-folder>\platforms\android-4.
That way your library will work with Android v1.6 and above (which is the same as the other libraries). If you don't see android-4 then you need to download this SDK with the SDK manager.
B4AShared.jar and Core.jar are located in Basic4android libraries folder.
An updated B4AShared.jar is included in the attached file. This is a minor update which is only relevant if you are using Basic4android v1.00.
Writing code:
All public classes and members are exposed to B4A.
Members can be either static or non-static. In both ways the user accesses the members with the 'Dimmed' object.
Only the no arguments constructor will be called.
The constructor is called when the object is 'Dimmed'.
Consider the following code:
B4X:
Dim b As Button
b = SomeList.Get(5)
'now user uses b to access the button.
So it would have been a waste if the Button constructor already called the android button constructor (the B4A button is wrapper above the standard button).
This is why many types have an Initialize method. In that cases the Initialize actually calls the standard button constructor.
Actually there is a special class which you can inherit from, for simple wrappers. I will later cover it.
Properties - Methods that start with 'get' and don't have any parameters will be converted to a property. The same is true for methods that start with 'set' and accept one parameter. The parameter types should match if both get and set exist.
B4X:
public int getWidth() {
}
public void setWidth(int value) {
}
BA - BA is a special object that you can use to raise events and to get access to the user activity, application context and other resources.
The compiler is responsible for passing this object. The IDE doesn't expose it.
For example, this is GPS.Start signature:
B4X:
public void Start(BA ba, long MinimumTime, float MinimumDistance) {
And now for something a bit more complicated. There are actually two BA objects for each activity. One is the "process BA" and the other is the "activity BA".
The process BA doesn't hold an activity context (or an activity object). The activity BA does hold a reference to the activity.
If your object is an "Activity object" then you will get the activity BA. Otherwise you will get the process BA.
Activity objects cannot be declared in Sub Process_Globals (as this would cause a memory leak).
You should prefer to create process objects. Only create Activity objects if it is really needed (mainly for UI elements). Note that the ApplicationContext is accessible from the process BA.
Annotations
So assuming that you are still following, the question that should be asked is how do you mark your object as an Activity object? This is done with annotations.
B4AShared.BA has several important annotations.
@ActivityObject - Marking your class with this annotation will make it an activity object.
Additional important annotations:
@ShortName - In order for a class to be accessible it must has a short name annotation. This annotation which receives one string parameter defines the name that the user will see. The user is not familiar with the package or the actual class name. This means that you should not use common names as the short names, otherwise there will be conflicts in the future. You can use a prefix like: eu_FTP.
Here is the declaration of the GPS class:
B4X:
/**
* The main object that raises the GPS events.
*/
@ShortName("GPS")
@Permissions(values={"android.permission.ACCESS_FINE_LOCATION"})
@Events(values={"LocationChanged (Location1 As Location)",
"UserEnabled (Enabled As Boolean)",
"GpsStatus (Satellites As List)"})
@Version(1f)
public class GPS {
@Permissions - An array of strings with the permissions that will be added to the manifest file when the user declares an object of this type.
@Events - An array of strings with the events that are raised by this type.
@Version - Sets the library version. Should only be declared once per library (a library can have many classes).
@Hide - Hides public classes or members.
@author - Receives one string parameter with the author name. This will affect the online documentation (this is not available in B4AShared that comes with v1.00, it is available in the attached file).
NEW - @DependsOn - An array of strings with additional jar files that will be referenced when this library is referenced. Removes the need to add "dummy" xml files.
Documentation
All the documentation is done inside the java classes.
You should use regular javadoc style comments to document methods, fields and classes. Do not use any special tags like @param or @return.
You can use code tags:
B4X:
/**
* Reads the file and returns its content as a string.
*Example:<code>
*Dim text As String
*text = File.ReadString(File.DirRootExternal, "text files/1.txt")</code>
*/
You can also use link tags:
B4X:
/**
* Creates a new file and writes the given map. Each key value pair is written as a single line.
*All values are converted to strings.
*See this link for more information about the actual format: <link>Properties format|http://en.wikipedia.org/wiki/.properties</link>.
*You can use File.ReadMap to read this file.
*/
public static void WriteMap(String Dir, String FileName, Map Map) throws IOException {
If you have several objects and want to create a library scope documentation you should add a method with the following signature:
B4X:
/**
* The HTTP library allows you to communicate with web services and to download resources from the web.
*As network communication can be slow and fragile this library handles the requests and responses in the background and raises events when a task is ready.
*There are two HTTP examples which demonstrates this library: <link>Currency Converter|http://www.b4x.com/forum/basic4android-getting-started-tutorials/6506-currency-converter-http-web-services-more.html</link> and a more complex example: <link>Flickr Viewer|http://www.b4x.com/forum/basic4android-getting-started-tutorials/6646-flickr-viewer.html</link>.
*/
public static void LIBRARY_DOC() {
}
There are three methods available for raising events:
- raiseEventFromUI: this is the default way to raise an event from a UI object. It sends a message to an internal queue with the event information.
There is no return value as the event is not raised immediately. The reason behind this method is to avoid conflicts with the way Android handles UI events. See this link for more information.
- raiseEvent: directly calls the given sub.
- raiseEventFromDifferentThread - this method should be used when the event is raised from a different thread. The event message will be delegated to the main thread message queue. You can pass null and 0 in the container and TaskId parameters. They are not needed.
Common parameters:
sender - Is the object that the user will get with the Sender keyword.
event - This is the event sub name. This string must be lower cased and it should contain the prefix name and the event.
params - list of objects to pass as the event parameters.
You should make sure that the string is lower cased or it will not find the sub.
For example, this code checks if the user has a sub with the correct signature and then adds the listener:
B4X:
if (ba.subExists(eventName + "_click")) {
getObject().setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
ba.raiseEvent(getObject(), eventName + "_click");
}
});
}
New tool to simplify the building process: http://www.b4x.com/forum/showthread.php?p=173756
Generating the XML file
This requires some configuration on the first time.
We are using a custom "doclet" to generate the XML file.
Choose Project - Generate JavaDoc.
Choose custom doclet and set the name to BADoclet and path to the doclet folder in the downloaded file.
Make sure that the correct project is marked.
Press Next.
Add a parameter named -b4atarget and write the path of the target file.
Press Finish and the XML should be generated.
You should create the XML in your 'additional libraries' folder.
The jar file should also be copied to this location.
There are several ways to build the jar file.
Personally I'm using a batch file that creates the jar file and copies it to the libraries folder. I'm using a nice utility named AutoHotKey to assign a hot key for this batch file. So I first run the Javadoc tool from eclipse, then press on the hotkey which creates the jar file and copies it to the libraries folder and then right click on the libraries tab in the IDE - Refresh to reload the libraries. From time to time you will get strange MethodNotFoundErrors when you run your program. This happens when you forget to recreate the jar files or you forget to refresh the libraries (the IDE uses a modified Dexer that caches the libraries in order make compilation quicker).
AbsObjectWrapper
If your class is a simple wrapper above another android class then you can inherit from AbsObjectWrapper. The compiler treats such classes differently and automatically converts between the wrapper and the underlying object as required.
It is better not to use AbsObjectWrapper than to use it wrong.
For example, this is the RectWrapper class:
B4X:
@ShortName("Rect")
public static class RectWrapper extends AbsObjectWrapper<Rect> {
public void Initialize(int Left, int Top, int Right, int Bottom) {
Rect r = new Rect(Left, Top, Right, Bottom);
setObject(r);
}
public int getLeft() {return getObject().left;} public void setLeft(int Left) {getObject().left = Left;}
public int getTop() {return getObject().top;} public void setTop(int Top) {getObject().top = Top;}
public int getRight() {return getObject().right;} public void setRight(int Right) {getObject().right = Right;}
public int getBottom() {return getObject().bottom;} public void setBottom(int Bottom) {getObject().bottom = Bottom;}
/**
* Returns the horizontal center.
*/
public int getCenterX() {return getObject().centerX();};
/**
* Returns the vertical center.
*/
public int getCenterY() {return getObject().centerY();};
}
Basic4android has internal support for running tasks in the background. This will be covered in the future.
Some examples:
PhoneAccelerometer code:
B4X:
/**
* This object gives access to the internal accelerometers sensors.
*See the <link>Orientation and accelerometers example|http://www.b4x.com/forum/basic4android-getting-started-tutorials/6647-orientation-accelerometer.html</link>.
*This object should be declared as a process global object.
*/
@ShortName("PhoneAccelerometer")
@Events(values={"AccelerometerChanged (X As Float, Y As Float, Z As Float)"})
public static class PhoneAccelerometer {
private SensorEventListener listener;
/**
* Starts listening for AccelerometerChanged events.
*/
public void StartListening(final BA ba, String EventName) {
SensorManager sm = (SensorManager) ba.context.getSystemService(Context.SENSOR_SERVICE);
final String s = EventName.toLowerCase(BA.cul) + "_accelerometerchanged";
listener = new SensorEventListener() {
@Override
public void onAccuracyChanged(Sensor sensor, int accuracy) {
}
@Override
public void onSensorChanged(SensorEvent event) {
ba.raiseEvent(this, s, event.values[0], event.values[1], event.values[2]);
}
};
sm.registerListener(listener, sm.getDefaultSensor(Sensor.TYPE_ACCELEROMETER), SensorManager.SENSOR_DELAY_NORMAL);
}
/**
* Stops listening for events.
*/
public void StopListening(BA ba) {
if (listener != null) {
SensorManager sm = (SensorManager) ba.context.getSystemService(Context.SENSOR_SERVICE);
sm.unregisterListener(listener);
}
}
}
B4X:
@ShortName("PhoneSms")
@Permissions(values={"android.permission.SEND_SMS"})
public static class PhoneSms {
/**
* Sends an Sms message. Note that this method actually sends the message (unlike most other methods that
*create an intent object).
*/
public static void Send(String PhoneNumber, String Text) {
SmsManager sm = SmsManager.getDefault();
sm.sendTextMessage(PhoneNumber, null, Text, null, null);
}
}
B4X:
/**
* Using an Email object you can create an intent that holds a complete email message.
*You can then launch the email application by calling StartActivity. Note that the email will not be sent automatically. The user will need to press on the send button.
*Example:<code>
*Dim Message As Email
*Message.To.Add("SomeEmail@example.com")
*Message.Attachments.Add(File.Combine(File.DirRootExternal, "SomeFile.txt"))
*StartActivity(Message.GetIntent)</code>
*/
@ShortName("Email")
public static class Email {
public String Subject = "";
public String Body = "";
public anywheresoftware.b4a.objects.collections.List To = new anywheresoftware.b4a.objects.collections.List();
public anywheresoftware.b4a.objects.collections.List CC = new anywheresoftware.b4a.objects.collections.List();
public anywheresoftware.b4a.objects.collections.List BCC = new anywheresoftware.b4a.objects.collections.List();
public anywheresoftware.b4a.objects.collections.List Attachments = new anywheresoftware.b4a.objects.collections.List();
public Email() {
To.Initialize();
CC.Initialize();
BCC.Initialize();
Attachments.Initialize();
}
/**
* Returns the Intent that should be sent with StartActivity.
*/
public Intent GetIntent() {
Intent emailIntent = new Intent(android.content.Intent.ACTION_SEND_MULTIPLE);
emailIntent.setType("plain/text");
emailIntent.putExtra(android.content.Intent.EXTRA_EMAIL,
To.getObject().toArray(new String[0]));
emailIntent.putExtra(android.content.Intent.EXTRA_CC,
CC.getObject().toArray(new String[0]));
emailIntent.putExtra(android.content.Intent.EXTRA_BCC,
BCC.getObject().toArray(new String[0]));
emailIntent.putExtra(Intent.EXTRA_SUBJECT, Subject);
emailIntent.putExtra(Intent.EXTRA_TEXT, Body);
ArrayList<Uri> uris = new ArrayList<Uri>();
for (Object file : Attachments.getObject())
{
File fileIn = new File((String)file);
Uri u = Uri.fromFile(fileIn);
uris.add(u);
}
emailIntent.putParcelableArrayListExtra(Intent.EXTRA_STREAM, uris);
return emailIntent;
}
}
B4X:
Email1.To.Add(...)
Questions related to libraries development should be asked here: https://www.b4x.com/android/forum/threads/java-creating-libraries-for-basic4android.6810/
See this thread for the default license: http://www.b4x.com/forum/libraries-...important-notice-about-libraries-license.html
Attachments
Last edited: