Overview
In Android development it is a standard practice to use inheritance to create custom activities, applications and other components. Currently NativeScript runtime for Android ({N}) has limited support for this scenario. This proposal aims to provide a solution for creating custom activities, applications and other components.
Note: for the sake of this proposal I use the common scenario to extend an activity class.
Current State
{N} provides built-in NativeScriptApplication and NativeScriptActivity classes. While these classes helped the adoption of {N} they also provide limitations. Currently it is not possible to inherit from a third party activity or other types which are used in AndroidManifest.xml file.
Technical Details
Here is how a Java class is extended in {N}
// app/myactivity.js
var MyActivity = android.app.Activity.extend({
onCreate: function(bundle) {
// implementation
}
});
However MyActivity class is generated at runtime by {N} bridge. This mean you cannot use MyActivity in AndroidManifest.xml the way we currently use NativeScriptActivity for example.
<!-- AndroidManifest.xml -->
<activity
android:name="com.tns.NativeScriptActivity"
android:label="TestApp">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
Proposal
The current extend implementation can accept two parameters as well. We can rewrite the previous example as follows
// app/myactivity.js
var MyActivity = android.app.Activity.extend("MyActivity", {
onCreate: function(bundle) {
// implementation
}
});
We can leverage the latter syntax of extend function and pass the fully qualified type name of the new class as follows
// app/myactivity.js
var MyActivity = android.app.Activity.extend("com.example.MyActivity", {
onCreate: function(bundle) {
// implementation
}
});
During build time we can parse the JavaScript and generate Java file com/example/MyActivity.java. This proposal will not make the existing code ambiguous because currently there is a validation rule that says the class name cannot contain . symbol. The generated MyActivity.java file will look as follows
// com/example/MyActivity.java
package com.example;
@com.tns.JavaScriptImplementation(javaScriptFile = "app/myactivity.js")
public class MyActivity extends android.app.Activity {
public MyActivity() {
com.tns.Platform.initInstance(this);
}
public void onCreate(android.os.Bundle param_0) {
Object[] args = new Object[1];
args[0] = param_0;
com.tns.Platform.callJSMethod(this, "onCreate", void.class, args);
}
}
This is how the currently runtime generated types look like. We can provide a tool that generates equivalent types at build time. The only difference is the use of com.tns.JavaScriptImplementation annotation. The need a way to find the JavaScript implementation for MyActivity when an instance is created. Using annotation is just one possible approach. I find it clear and, more importantly, I find it aligned with the following proposition how to extend Java class in TypeScript.
// myactivity.ts
@JavaProxy("com.tns.MyActivity")
class MyActivity extends android.app.Activity {
onCreate(bundle: android.os.Bundle) {
// implementation
}
}
Using ES6/TypeScript decorators is a simple way to indicate that we have to generate Java class at build time. The main benefit of using decorators is that the change is less obtrusive than other alternatives.
Impact
This proposal will require {N} modules to change the way an application is initialized and activities are created. I expect that this would be small-to-medium change. The immediate benefits are better encapsulation and control. Also, it may require {N} modules to come with prebuilt Activity and Application classes. Another approach would be adding these classes to the project template.
Summary
This proposal has some other benefits as well. Removing NativeScriptActivity and NativeScriptApplication from {N} will allows us to support lower Android API levels. Currently we have to compile against API level 17 and there is no technical justification for that. Also it will allow embedding {N} in other applications much easier. One primary scenario is NativeScript companion app.
Finally, you can find a prototype here
Overview
In Android development it is a standard practice to use inheritance to create custom activities, applications and other components. Currently NativeScript runtime for Android ({N}) has limited support for this scenario. This proposal aims to provide a solution for creating custom activities, applications and other components.
Current State
{N} provides built-in
NativeScriptApplicationandNativeScriptActivityclasses. While these classes helped the adoption of {N} they also provide limitations. Currently it is not possible to inherit from a third party activity or other types which are used inAndroidManifest.xmlfile.Technical Details
Here is how a Java class is extended in {N}
However
MyActivityclass is generated at runtime by {N} bridge. This mean you cannot useMyActivityinAndroidManifest.xmlthe way we currently useNativeScriptActivityfor example.Proposal
The current
extendimplementation can accept two parameters as well. We can rewrite the previous example as followsWe can leverage the latter syntax of
extendfunction and pass the fully qualified type name of the new class as followsDuring build time we can parse the JavaScript and generate Java file
com/example/MyActivity.java. This proposal will not make the existing code ambiguous because currently there is a validation rule that says the class name cannot contain.symbol. The generatedMyActivity.javafile will look as followsThis is how the currently runtime generated types look like. We can provide a tool that generates equivalent types at build time. The only difference is the use of
com.tns.JavaScriptImplementationannotation. The need a way to find the JavaScript implementation forMyActivitywhen an instance is created. Using annotation is just one possible approach. I find it clear and, more importantly, I find it aligned with the following proposition how to extend Java class in TypeScript.Using ES6/TypeScript decorators is a simple way to indicate that we have to generate Java class at build time. The main benefit of using decorators is that the change is less obtrusive than other alternatives.
Impact
This proposal will require {N} modules to change the way an application is initialized and activities are created. I expect that this would be small-to-medium change. The immediate benefits are better encapsulation and control. Also, it may require {N} modules to come with prebuilt
ActivityandApplicationclasses. Another approach would be adding these classes to the project template.Summary
This proposal has some other benefits as well. Removing
NativeScriptActivityandNativeScriptApplicationfrom {N} will allows us to support lower Android API levels. Currently we have to compile against API level 17 and there is no technical justification for that. Also it will allow embedding {N} in other applications much easier. One primary scenario is NativeScript companion app.Finally, you can find a prototype here