This document provides information on Android Hyperloop requirements, classes, instantiation, methods and fields, casting, interfaces, creating your own classes, and using 3rd party libraries.
Requirements
Software
See Hyperloop Requirements for software requirements for using Hyperloop with Android.
Configure tiapp.xml
Make sure to add the Hyperloop module to the <modules>
section of the tiapp.xml
:
< modules > < module >hyperloop</ module > </ modules > |
Classes
Classes in Hyperloop map to the underlying classes defined in Java. For
example, if you have a class such as android.view.View
defined, you would reference it using a standard require such as:
var View = require( 'android.view.View' ); |
This will return the View
class object (it’s not an instance of a View
, but the View
class itself).
Once you have the Class reference returned from require
, you can call normal JavaScript property and functions against it. Remember,
at this point calling functions or properties against the class object
above will be accessing Class level (static) Java methods (not instance
level).
For example, you could get the generated view id of the View
using the example:
var generatedId = View.generateViewId(); |
This is because generateViewId
is defined as a static method.
Instantiation
Please refer to our hyperloop-examples app in those code-level examples. For example, to use a View
, the activity needs to be set:
var activity = new Activity(Ti.Android.currentActivity); |
See animateview.js (Github) for an working sample.
Methods and fields
Methods in Java are mapped to JavaScript functions. Fields in Java are mapped to JavaScript property accessors. Static methods or fields (such as constants) will be attached to the class type. For example:
1
2
3
4
5
6
|
public class Example { public int field; public static final String staticString = "" ; public void method( int argument); public static boolean staticMethod(); } |
Would map to the following in JavaScript:
example.field = 123; example.staticString; example.method(567); var result = example.staticMethod(); |
Method resolution
If a class has overloads for a method (multiple forms of the method with different signatures, but the same name), Hyperloop will attempt to match the correct method to invoke on the Java side by matching the passed in arguments to the closest match. Typically, this involves matching the name, number of arguments and the ability to convert the passed in arguments (in-order) to the method’s parameter types. Hyperloop is slightly more liberal in accepting numeric primitives than typical method resolution due to the conversion of JavaScript Numbers.
Casting
Sometimes, interfaces define generic return types such as Object
and you will need to cast them to a different type to then reference methods
and properties of the class. You can pass along the object you want to
wrap to the constructor of the type you want to wrap it in. For example,
suppose the result of the function returned an Object
but you know the implementation is actually a View
. You could use the following:
var view = new View(object); // call View instance methods on view variable |
Be careful with casting: If you cast an object which is actually something different, you will experience an error and likely a crash.
You can also cast a Titanium UI Component into its equivalent. For example:
var tiView = Ti.UI.createView({ backgroundColor : 'red' }); var nativeView = new View(tiView); console.log( 'X (relative to parent): ' , nativeView.getLeft()); |
Interfaces
Interfaces may be implemented using a Javascript syntax similar to an anonymous
Java class. Call the constructor of the interface type with a JavaScript object
that contains properties that match the interface method names, and corresponding
values as function that implement them. For example, to create an instance
that implements android.view.View.OnTouchListener
:
1
2
3
4
5
6
7
|
var OnTouchListener = require( 'android.view.View.OnTouchListener' ); var listener = new OnTouchListener({ onTouch: function (v, event) { // Do some work here return true ; } }); |
Creating your own classes
Hyperloop provides you the ability to dynamically create your own Java classes at runtime. Once created, these classes can be used as normal in either Hyperloop or passed to native calls. Hyperloop generates the custom subclass using the "extend" function of the type we want to extend, which takes a single JavaScript Object as an argument containing the overriding method implementations (same as we did for interface implementations). The returned value is a new class type that subclasses the extended type. We can then use the constructor to generate instances of that subclass.
Let's create a simple custom subclass of android.view.View
, and instantiate an instance of it:
1
2
3
4
5
6
7
8
9
10
11
|
var Activity = require( 'android.app.Activity' ), activity = new Activity(Ti.Android.currentActivity), View = require( 'android.view.View' ); var MyView = android.view.View.extend({ onDraw: function (canvas) { this . super .onDraw(canvas); // implementation here } }); var customView = new MyView(activity); // Add your custom view to the content... |
android.view.View
which is equivalent to the following code (though please note that
we do not generate Java source, but instead generate Dalvik bytecode that gets loaded
into the runtime as a class):
1
2
3
4
5
|
class MyView extends android.view.View { protected void onDraw(Canvas canvas) { // implementation here } } |
Accessing native XML Layouts
In native Android development, you have many situations where you are inflating native XML layouts in your source-code. In Hyperloop, you can do the same with just a few lines of code:
var Activity = require( 'android.app.Activity' ); Context = require( 'android.content.Context' ), Inflater = require( 'android.view.LayoutInflater' ), activity = new Activity(Ti.Android.currentActivity); // Load the layout inflater var inflater = Inflater.cast(activity.getApplicationContext().getSystemService(Context.LAYOUT_INFLATER_SERVICE)); // Load the "main_content.xml" from [app]/platform/android/res/layout/ // This can also be used for other kind of resources, like images, colors
and values var resId = activity.getResources().getIdentifier( 'main_content' , 'layout' , activity.getPackageName()); var view = inflater.inflate(resId, null ); // Add "view" your Titanium view here ... |
Learn more about a native XML layouts in our Native Layout Example as part of the Hyperloop Examples app.
Using Third-party libraries
Manual dependencies
You can use Third-party libraries in Hyperloop such as JARs and AARs.
Place the JAR and AAR files into the platform/android
(Classic) or app/platform/android
(Alloy) folder of your app. Hyperloop will pick up the JAR files and will
generate necessary bindings. For JARs, they will be included in your app.
For AAR files, they will extract resources, extract and use the class.jar,
*.so file, and so on.
Gradle Dependencies
Instead of placing your Android archives into your project manually, you can also use Gradle to pull down any library and its dependencies. Here is a quick example of using the Mapbox Android SDK via Gradle:
apply plugin: 'java' repositories { google() mavenCentral() } dependencies { implementation 'com.mapbox.mapboxsdk:mapbox-android-sdk:5.5.0' } task getDeps(type: Copy) { from sourceSets.main.runtimeClasspath exclude 'support-*' // The support libraries are packaged with Titanium already into 'platform/android/' // Use "app/platform/android" for Alloy } |
In the above Gradle file, the Mapbox Android SDK is downloaded to platform/android
(Classic) and can be changed to app/platform/android
if used in Alloy. You can see a full example of using Gradle, CocoaPods
(iOS) and modern ES6+ in the Ti.Mapbox module.
Third-party Examples
Make sure to check out more Android third-party libraries (like Localytics, SSDP, Shimmer) in our Hyperloop Sample App.