Click here to Skip to main content
14,176,439 members
Click here to Skip to main content
Add your own
alternative version

Stats

10.1K views
97 downloads
4 bookmarked
Posted 3 Jun 2017
Licenced CPOL

Xamarin.Forms Android Using J2V8 to Execute JavaScript without WebView

, 7 Jun 2017
Rate this:
Please Sign up or sign in to vote.
This article illustrate how to execute JavaScript on Android using Xamarin.Forms.
  • Download J2V8BL.zip - 4.2 MB  [This contains the Android Bindings Library you can directly use it for Xamarin.Android. So you don't have to follow this article to build another one.]

Introduction

Recently, I need to execute JavaScript code in an Xamarin.Forms Project. Due to the WebView is taking more resources from the mobile device. I was seeking an approach to achieve my requirement. And I found the J2V8 was good to do so. When I build my code and use the J2V8, I faced some problems. But finally, I solved these problems and you may get the errors when you are trying to use it.

Go

In the Xamrin.Forms solution, build an Android Binding Library(BL). In the BL project, add the "j2v8_android-3.0.5.aar" file to the "Jars" folder. Set the Build Action for j2v8_android-3.0.5.aar" to LibraryProjectZip. Now build the BL project, you may get 3 errors and others warnings. Obviously, we don't have to take care of the warnings at this time.

1>..\obj\Debug\generated\src\Com.Eclipsesource.V8.Utils.V8Map.cs(10,99,10,121): error CS0738: 'V8Map' does not implement interface member 'IMap.EntrySet()'. 'V8Map.EntrySet()' cannot implement 'IMap.EntrySet()' because it does not have the matching return type of 'ICollection'.

1>..\obj\Debug\generated\src\Com.Eclipsesource.V8.Utils.V8Map.cs(10,99,10,121): error CS0738: 'V8Map' does not implement interface member 'IMap.KeySet()'. 'V8Map.KeySet()' cannot implement 'IMap.KeySet()' because it does not have the matching return type of 'ICollection'.

1>..\obj\Debug\generated\src\Com.Eclipsesource.V8.Utils.V8Map.cs(10,99,10,121): error CS0535: 'V8Map' does not implement interface member 'IMap.Put(Object, Object)'

These errors are Problem: Class does not implement interface method. To fix these problems, I followed the documentation from here. But it may take sometime to figure it out since there is no demo project.

Let's fix the errors.

1).

In the output window from Visual Studio, find the first error and double click it. Now it should open the generated cs file "..\obj\Debug\generated\src\Com.Eclipsesource.V8.Utils.V8Map.cs". Find the "EntrySet()" and the "KeySet ()" method.

static IntPtr id_entrySet;
// Metadata.xml XPath method reference: path="/api/package[@name='com.eclipsesource.v8.utils']/class[@name='V8Map']/method[@name='entrySet' and count(parameter)=0]"
[Register ("entrySet", "()Ljava/util/Set;", "GetEntrySetHandler")]
public virtual unsafe global::System.Collections.Generic.ICollection<global::Java.Util.IMapEntry> EntrySet ()
{
    if (id_entrySet == IntPtr.Zero)
        id_entrySet = JNIEnv.GetMethodID (class_ref, "entrySet", "()Ljava/util/Set;");
    try {

        if (((object) this).GetType () == ThresholdType)
             return global::Android.Runtime.JavaSet<global::Java.Util.IMapEntry>.FromJniHandle (JNIEnv.CallObjectMethod (((global::Java.Lang.Object) this).Handle, id_entrySet), JniHandleOwnership.TransferLocalRef);
        else
             return global::Android.Runtime.JavaSet<global::Java.Util.IMapEntry>.FromJniHandle (JNIEnv.CallNonvirtualObjectMethod (((global::Java.Lang.Object) this).Handle, ThresholdClass, JNIEnv.GetMethodID (ThresholdClass, "entrySet", "()Ljava/util/Set;")), JniHandleOwnership.TransferLocalRef);
    } finally {
    }
}

//...............................................................................//

static IntPtr id_keySet;
// Metadata.xml XPath method reference: path="/api/package[@name='com.eclipsesource.v8.utils']/class[@name='V8Map']/method[@name='keySet' and count(parameter)=0]"
[Register ("keySet", "()Ljava/util/Set;", "GetKeySetHandler")]
public virtual unsafe global::System.Collections.ICollection<global::Java.Util.IMapEntry> KeySet ()
{
    if (id_keySet == IntPtr.Zero)
        id_keySet = JNIEnv.GetMethodID (class_ref, "keySet", "()Ljava/util/Set;");
    try {

        if (((object) this).GetType () == ThresholdType)
            return global::Android.Runtime.JavaSet<global::Com.Eclipsesource.V8.V8Value>.FromJniHandle (JNIEnv.CallObjectMethod (((global::Java.Lang.Object) this).Handle, id_keySet), JniHandleOwnership.TransferLocalRef);
        else
            return global::Android.Runtime.JavaSet<global::Com.Eclipsesource.V8.V8Value>.FromJniHandle (JNIEnv.CallNonvirtualObjectMethod (((global::Java.Lang.Object) this).Handle, ThresholdClass, JNIEnv.GetMethodID (ThresholdClass, "keySet", "()Ljava/util/Set;")), JniHandleOwnership.TransferLocalRef);
        } finally {
    }
}

2). 

In the BL project, expand the "Transforms" folder and open the Metadata.xml file. Add attr nodes inside the xml file. The XPaths are from the above methods.

<metadata>
  <attr

    path="/api/package[@name='com.eclipsesource.v8.utils']/class[@name='V8Map']/method[@name='entrySet' and count(parameter)=0]"

    name="managedReturn">
    System.Collections.ICollection
  </attr>

  <attr

    path="/api/package[@name='com.eclipsesource.v8.utils']/class[@name='V8Map']/method[@name='keySet' and count(parameter)=0]"

    name="managedReturn">
    System.Collections.ICollection
  </attr>
</metadata>

Now build the BL project. You will get the last error.

3).

In the generated Com.Eclipsesource.V8.Utils.V8Map.cs file, Add the V8Map partial class inside the Com.Eclipsesource.V8.Utils namespace. And implemente the IMap.Put interface.

public partial class V8Map[...]


public partial class V8Map : global::Java.Lang.Object, global::Com.Eclipsesource.V8.IReleasable, global::Java.Util.IMap
{
    //Metadata.xml XPath method reference: path="/api/package[@name='com.eclipsesource.v8.utils']/class[@name='V8Map']/method[@name='put' and count(parameter)=2 and parameter[1][@type='com.eclipsesource.v8.V8Value'] and parameter[2][@type='V']]"
    [Register("put", "(Lcom/eclipsesource/v8/V8Value;Ljava/lang/Object;)Ljava/lang/Object;", "GetPut_Lcom_eclipsesource_v8_V8Value_Ljava_lang_Object_Handler")]
    Java.Lang.Object global::Java.Util.IMap.Put(Java.Lang.Object key, Java.Lang.Object value)
    {
        return Put((global::Com.Eclipsesource.V8.V8Value)key, value);
    }
}

Now build the BL project. In the output window from Visual Studio, you will get 4 errors.

1>..\obj\Debug\generated\src\Com.Eclipsesource.V8.Utils.V8Map.cs(228,13,228,206): error CS0266: Cannot implicitly convert type 'System.Collections.Generic.ICollection<Java.Util.IMapEntry>' to 'System.Collections.ICollection'. An explicit conversion exists (are you missing a cast?)

1>..\obj\Debug\generated\src\Com.Eclipsesource.V8.Utils.V8Map.cs(230,13,230,289): error CS0266: Cannot implicitly convert type 'System.Collections.Generic.ICollection<Java.Util.IMapEntry>' to 'System.Collections.ICollection'. An explicit conversion exists (are you missing a cast?)

1>..\obj\Debug\generated\src\Com.Eclipsesource.V8.Utils.V8Map.cs(300,13,300,213): error CS0266: Cannot implicitly convert type 'System.Collections.Generic.ICollection<Com.Eclipsesource.V8.V8Value>' to 'System.Collections.ICollection'. An explicit conversion exists (are you missing a cast?)

1>..\obj\Debug\generated\src\Com.Eclipsesource.V8.Utils.V8Map.cs(302,13,302,296): error CS0266: Cannot implicitly convert type 'System.Collections.Generic.ICollection<Com.Eclipsesource.V8.V8Value>' to 'System.Collections.ICollection'. An explicit conversion exists (are you missing a cast?)

Now we need to add cast into the code. Double click the error and open the generated code. Add "as global::System.Collections.ICollection" to do the cast inside the "EntrySet()" and the "KeySet ()" method.

static IntPtr id_entrySet;
// Metadata.xml XPath method reference: path="/api/package[@name='com.eclipsesource.v8.utils']/class[@name='V8Map']/method[@name='entrySet' and count(parameter)=0]"
[Register ("entrySet", "()Ljava/util/Set;", "GetEntrySetHandler")]
public virtual unsafe global::System.Collections.ICollection EntrySet ()
{
    if (id_entrySet == IntPtr.Zero)
        id_entrySet = JNIEnv.GetMethodID (class_ref, "entrySet", "()Ljava/util/Set;");
    try {

    if (((object) this).GetType () == ThresholdType)
        return global::Android.Runtime.JavaSet<global::Java.Util.IMapEntry>.FromJniHandle (JNIEnv.CallObjectMethod (((global::Java.Lang.Object) this).Handle, id_entrySet), JniHandleOwnership.TransferLocalRef) as global::System.Collections.ICollection;
    else
        return global::Android.Runtime.JavaSet<global::Java.Util.IMapEntry>.FromJniHandle (JNIEnv.CallNonvirtualObjectMethod (((global::Java.Lang.Object) this).Handle, ThresholdClass, JNIEnv.GetMethodID (ThresholdClass, "entrySet", "()Ljava/util/Set;")), JniHandleOwnership.TransferLocalRef) as global::System.Collections.ICollection;
    } finally {
    }
}


//........................................................................//

static IntPtr id_keySet;
// Metadata.xml XPath method reference: path="/api/package[@name='com.eclipsesource.v8.utils']/class[@name='V8Map']/method[@name='keySet' and count(parameter)=0]"
[Register ("keySet", "()Ljava/util/Set;", "GetKeySetHandler")]
public virtual unsafe global::System.Collections.ICollection<global::Java.Util.IMapEntry> KeySet ()
{
    if (id_keySet == IntPtr.Zero)
        id_keySet = JNIEnv.GetMethodID (class_ref, "keySet", "()Ljava/util/Set;");
    try {

        if (((object) this).GetType () == ThresholdType)
            return global::Android.Runtime.JavaSet<global::Com.Eclipsesource.V8.V8Value>.FromJniHandle (JNIEnv.CallObjectMethod (((global::Java.Lang.Object) this).Handle, id_keySet), JniHandleOwnership.TransferLocalRef) as global::System.Collections.ICollection;
        else
            return global::Android.Runtime.JavaSet<global::Com.Eclipsesource.V8.V8Value>.FromJniHandle (JNIEnv.CallNonvirtualObjectMethod (((global::Java.Lang.Object) this).Handle, ThresholdClass, JNIEnv.GetMethodID (ThresholdClass, "keySet", "()Ljava/util/Set;")), JniHandleOwnership.TransferLocalRef) as global::System.Collections.ICollection;
        } finally {
    }
}

Now build the BL project. It should build successfully. Keep in mind that backup the generated cs file because if you clean the project, it will regenerate the cs files.

Add the reference BL project from Android project. Add the following code in the MainActivity.cs to execute the JS as a demo.

       
       ......

       LoadApplication(new App());

       JS.ExecuteJS();   
    }
}
    
    //..................
    public class JS
    {
        public static void ExecuteJS()
        {
            Com.Eclipsesource.V8.V8 runtime = Com.Eclipsesource.V8.V8.CreateV8Runtime();
            int result = runtime.ExecuteIntegerScript(""
                  + "var hello = 'hello, ';\n"
                  + "var world = 'world!';\n"
                  + "hello.concat(world).length;\n");

            runtime.Release();
        }
    }

For more infomation about how to execute JS, please see this. You can add IScriptEngine into the PCL, and implement it from the Android project as a Dependency. Then you can use it from the PCL.

Points of Interest

For UWP project, you may use the Jurassic ScriptEngine. Actually I used it in my Xamarin.Forms project at first, it is working good on UWP. But it is working on  Android only the Linker is None, this leads that the apk package is very large(double). I tried different ways to make it work, but failed. If you can make it work successfully, please let me know.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

Share

About the Author

Hack~ok
United States United States
No Biography provided

You may also be interested in...

Pro
Pro

Comments and Discussions

 
QuestionCan't you provide this solution thru Nuget Pin
Ko-hei Ohtsuka6-Jun-17 20:21
memberKo-hei Ohtsuka6-Jun-17 20:21 
AnswerRe: Can't you provide this solution thru Nuget Pin
Hack~ok7-Jun-17 11:02
memberHack~ok7-Jun-17 11:02 
PraiseInteresting Pin
no_in3-Jun-17 7:31
professionalno_in3-Jun-17 7:31 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

Permalink | Advertise | Privacy | Cookies | Terms of Use | Mobile
Web02 | 2.8.190525.1 | Last Updated 8 Jun 2017
Article Copyright 2017 by Hack~ok
Everything else Copyright © CodeProject, 1999-2019
Layout: fixed | fluid