FlexibleHybridApp is a library that provides various convenience functions to develop HybridApp, such as implementing interface between WebPage and Native with promises.
minSdkVersion 19
Minimum ChromeVersion 55
- Enable jitpack
Add the following to the project build.gradle
allprojects {
repositories {
...
maven { url 'https://jitpack.io' }
}
}
Then add the following to the build.gradle of the module:
dependencies {
implementation 'com.github.Kyun-J:FlexHybridApp-Android:0.3.9.6'
}
Basically, it compensates for the shortcomings of Android's JavascriptInterface and has additional restrictions.
- When a native function is called from the Web, the return of the native function is passed to Promise on the Web.
- When calling the Web function from Native, the return value can be passed Async from Web to Native.
- In addition to annotations, you can add an interface by invoking a function that receives the Lambda (interface of Java) of Kotlin as a factor.
- In addition to the basic data type, JS array can be delivered as (JSONARray, Array, List) of Kotlin(JAVA) and JS Object as (JSONObject, Map) of Kotlin(JAVA).
- When calling Native on the web, Native code block operates in Custom Coroutine and operates as Multi Thread unlike JavaBridge Thread of JavascriptInterface, so it is processed in parallel when multiple interfaces are called at the same time.
- By specifying BaseUrl in FlexWebView, you can prevent native and interface on other sites and pages.
- You cannot add an interface after the page is first loaded into FlexWebView and appears on the screen.
- It is possible to transfer general data types and strings, such as Android JavascriptInterface.
- It is possible to transfer JS Array to Kotlin(JAVA) (JSONArray, Array, List) and JS Object to Kotlin(JAVA) (JSONObject, Map).
- When transferring data of type Array and Object, the data contained in it must be one of the following data types.
JS | Kotlin(Java) |
---|---|
Number | Int, Long, Float, Double |
String | String, Char |
Boolean | Boolean |
Array [] | JSONArray, Array<Any>, Iterable<Any> |
Object {} | JSONObject, Map<String,Any> |
undefined (Single Argument Only), null | Null |
Error | FlexReject |
The WebToNative interface has the following features.
- Two types of normal interface, which passes values by function return, and action interface, which passes values by method call
- Add interface in the form of lambda and annotation function
- Native code blocks operate in a separate Background Scope
- The added interface can be called in the form of $flex.function on the web.
- $flex Object can be used after window.onFlexLoad is called
Normal Interface is basically used as follows.
// in Kotlin
flexWebView.setInterface("Normal") // "Normal" becomes the function name in Web JavaScript.
{ arguments ->
// arguments is Arguemnts Data from web. Type is JSONArray
// ["data", 2, false]
return "HiFlexWeb" // "HiFlexWeb" is passed to web in Promise pattern.
}
// in web javascript
...
const res = await $flex.Normal("data1",2,false);
// res is "HiFlexWeb"
Specify the function name on the web as the first argument of setInterface
, and the following lambda becomes a block of code where the function operates.
The arguments passed to lambda are JSONArray objects, which contain the values passed when calling the function on the web.
When passing a value from lambda to web (when returning), only Transferable Data Type is available.
The Action Interface is almost the same as the Normal Interface, but it returns the value return to the Web at the time of calling the promiseReturn
method of the action object.
// in Kotlin
var mAction: FlexAction? = null
...
flexWebView.setAction("Action")
{ action, arguments ->
// arguments is JSONObject, ["Who Are You?"]
// action is FlexAction Object
mAction = action
}
...
// Returns to the Web when calling promiseReturn.
mAction.promiseReturn(arrayOf("FlexAction!!!",100));
mAction = null
// in web javascript
....
const res = await $flex.Action("Who Are You?"); // Pending until promiseReturn is called...
// res is ["FlexAction!!!", 100]
The parameter of promiseReturn
is only available for Transferable Data Type.
If the promiseReturn
method is not called, the function in the web will be in a pending state, so be careful to call promiseReturn
when using the Action Interface.
In addition, FlexAction objects that have already called promiseReturn
do not happen even if duplicatepromiseReturn
is called.
Similar to Android's @JavascriptInterface
, Interface or Action can be registered through Annotation.
@FlexFunInterface
must comply with the following:
- Only one JSONArray parameter can be used. (Exception occurs when adding another parameter)
- Return can only use Transferable Data Type. (Exception occurs when returning another value)
- The class containing @FlexFunInterface must be passed as an argument to FlexWebView.addFlexInterface to add an interface.
class MyInterface {
@FlexFunInterface
fun funInterface(arguments: JSONArray): JSONObject {
// .... work something
return JSONObject()
}
}
...
// in activity
mFlexWebView.addFlexInterface(MyInterface())
...
const res = await $flex.funInterface();
// res is {}
@FlexActionInterface
must comply with the following:
- Parameters must be declared in the order of FlexAction, JSONArray, and other parameters cannot be used. (Exception occurs when violation occurs)
- Return can be declared, but not used.
- When sending the return value to the web, you should use the
promiseReturn
of the passed FlexAction parameter. - The parameters of
promiseReturn
are only available for Transferable Data Type. - If the
promiseReturn
method cannot be called, the function on the web will be in a pending state, so be careful to callpromiseReturn
when using the Action Interface. promiseReturn
works only once, and nothing happens when a duplicate call is made.- The class containing @FlexActionInterface must be passed as an argument to FlexWebView.addFlexInterface to add the interface.
class MyInterface {
@FlexActionInterface
fun actionInterface(action: FlexAction, arguments: JSONArray) {
// .... work something
action.promiseReturn(JSONArray())
}
}
...
// in activity
mFlexWebView.addFlexInterface(MyInterface())
...
const res = await $flex.actionInterface();
// res is []
It is a separate class that separates only the interface addition function of FlexWebView.
Instead of adding an interface directly to FlexWebView, add it to FlexInterfaces and pass it to FlexWebView.addFlexInterface
to add the interface to FlexWebView.
public class FlexInterfaceExample extends FlexInterfaces {
FlexInterfaceExample() {
this.setInterface("test1", new Function1<JSONArray, Object>() {
@Override
public Object invoke(JSONArray arguments) {
return null;
}
}).setAction("test2", new Function2<FlexAction, JSONArray, Unit>() {
@Override
public Unit invoke(final FlexAction flexAction, JSONArray arguments) {
...
return null;
}
}).setInterface("test3", new Function1<JSONArray, Object>() {
@Override
public Object invoke(JSONArray arguments) {
...
return null;
}
});
}
@FlexFuncInterface
public void test4(JSONArray arguments) {
...
}
@FlexActionInterface
public void test5(FlexAction action, JSONArray arguments) {
action.promiseReturn(null);
}
}
// in activity...
...
// add interface test1, test2, test3, test4, test5
mFlexWebView.addFlexInterface(FlexInterfaceExample())
let other = FlexInterfaces()
other.setInterface("test6")
{ arguments ->
return null
}
other.setAction("test7")
{ action, arguments ->
action.promiseReturn(null)
}
// add interface test6, test7
mFlexWebView.addFlexInterface(other)
If you return the FlexReject
object, you can send an error to the web.
// in kotlin
mFlexWebView.setInterface("errorTest")
{ arguments ->
return FlexReject("errorTest")
}
// in js
...
try {
const result = await $flex.errorTest();
} catch(e) {
// e is Error("errorTest")
}
In FlexAction
, you can easily pass an error by calling the reject
function instead of promiseReturn
.
// in kotlin
flexComponent.setAction("errorAction")
{ action, arguments ->
action.reject("errorAction") // = action.promiseReturn(FlexReject("errorAction"))
}
// in js
...
try {
const result = await $flex.errorAction();
} catch(e) {
// e is Error("errorAction")
}
The NativeToWeb interface has the following features.
- If you add a function in the web's $flex.web Object, you can call the function through the
evalFlexFunc
method in Native(FlexWebView). - After calling
window.onFlexLoad
(after creating $flex), you can add a function to $flex.web. - The $flex.web function can pass values to Native through regular return and promise return.
window.onFlexLoad = () => {
$flex.web.webFunc = (data) => {
// data is ["data1","data2"]
return data[0]; // "data1"
}
$flex.web.promiseReturn = () => {
return Promise.resolve("this is promise")
}
}
...
// call function, send data, get response
mFlexWebView.evalFlexFunc("webFunc",arrayOf("data1","data2")) // same as $flex.web.webFunc(["data1","data2"])
{ res ->
// res is "data1"
}
mFlexWebView.evalFlexFunc("promiseReturn") // same as $flex.web.promiseReturn()
{ res ->
// res is "this is promise"
}
// just call function
mFlexWebView.evalFlexFunc("promiseReturn")
// call function and send data
mFlexWebView.evalFlexFunc("webFunc",arrayOf("data1","data2"))
Describes native classes of libraries including FlexWebView.
FlexWebView has the following features.
- It was created by inheriting Android WebView and is very similar to WebView.
- WebViewClient, WebChromeClient should use FlexWebViewClient, FlexWebChromeClient class. (If not used, exception occurs)
- You can use the existing Android JavascriptInterface. (In this case, you cannot use the Promise pattern interface using $flex.)
- BaseUrl must be specified, and $flex Object in WebPage can be used only in the URL containing the corresponding BaseUrl.
- Default of WebViewSettings is different from existing WebView.
FlexWebView defaults to the settings below.
This setting is applied when FlexWebView is declared and can be changed at any time.
settings.javaScriptEnabled = true
settings.displayZoomControls = false
settings.builtInZoomControls = false
settings.setSupportZoom(false)
settings.textZoom = 100
settings.domStorageEnabled = true
settings.loadWithOverviewMode = true
settings.loadsImagesAutomatically = true
settings.useWideViewPort = true
settings.cacheMode = WebSettings.LOAD_DEFAULT
settings.layoutAlgorithm = WebSettings.LayoutAlgorithm.SINGLE_COLUMN
settings.enableSmoothTransition()
settings.javaScriptCanOpenWindowsAutomatically = true
if(Build.VERSION.SDK_INT > Build.VERSION_CODES.LOLLIPOP) {
settings.mixedContentMode = WebSettings.MIXED_CONTENT_ALWAYS_ALLOW
}
if(Build.VERSION.SDK_INT > Build.VERSION_CODES.O) {
setRendererPriorityPolicy(RENDERER_PRIORITY_IMPORTANT, true)
}
$Flex Object can be used only in the page containing the configured BaseUrl.
Once set, the BaseUrl cannot be modified again.
fun setBaseUrl(url: String)
fun getBaseUrl(): String?
Set the time to wait for return after FlexInterface is executed.
After that time, the Promise created by the interface is forcibly rejected.
fun setInterfaceTimeout(timeout: Int)
Set the number of threads of the ThreadPoolExecutor where FlexInterface is executed.
The default is the number of cpu cores(=Runtime.getRuntime().availableProcessors()).
fun setInterfaceThreadCount(count: Int)
FlexWebView must use FlexWebViewClient and FlexWebChromeClient.
In the setter, WebChromeClient and WebViewClient are received as arguments, but an exception occurs if the object cannot be Cased with FlexWebViewClient or FlexWebChromeClient.
FlexWebChromeClient includes code that enables WebView full screen, so you can switch to full screen by calling requestFullscreen of js.
fun getWebChromeClient(): FlexWebChromeClient
fun setWebChromeClient(client: WebChromeClient)
fun getWebViewClient(): FlexWebViewClient
fun setWebViewClient(client: WebViewClient)
Add an interface to the FlexWebView. For details, refer to WebToNavite Interface.
fun setInterface(name: String, lambda: (JSONArray?) -> Any?): FlexWebView
fun setAction(name: String, action: (action: FlexAction?, arguments: JSONArray?) -> Unit): FlexWebView
fun addFlexInterface(flexInterfaces: Any)
Implement the interface by calling the function added to $ flex. For details, refer to NativeToWeb Interface.
fun evalFlexFunc(funcName: String)
fun evalFlexFunc(funcName: String, response: (Any?) -> Unit)
fun evalFlexFunc(funcName: String, sendData: Any?)
fun evalFlexFunc(funcName: String, sendData: Any?, response: (Any?) -> Unit)
Created when WebToNative interface added with setAction, @FlexActionInterface is called.
The reject function automatically creates and passes a FlexReject object (same as promiseReturn(FlexReject)).
fun promiseReturn(response: Any?)
If any of the above functions is called, the next time any function is called, the value is not passed to the Web.
If you directly create and use FlexAction Class, there is no effect. Only FlexAction created and delivered on the interface is effective.
FlexInterfaces class is a class that separates only setInterface and setAction functions from FlexWebView.
Refer to Interface example for usage examples.
fun setInterface(name: String, lambda: (JSONArray?) -> Any?): FlexInterfaces
fun setAction(name: String, action: (action: FlexAction?, arguments: JSONArray?) -> Unit): FlexInterfaces
$flex Object is an object composed of interfaces between FlexWebView and Promise.
The components of $ flex Object are as follows.
window.onFlexLoad // $flex is called upon completion of loading.
$flex // Object that contains functions that can call Native area as WebToNative
$flex.version // get Library version
$flex.web // Object used to add and use functions to be used for NativeToWeb
$flex.device // Current Device Info
$flex.isAndroid // true
$flex.isiOS // false
For detailed usage, refer to Flex Interface Implementation.