I am working on an Android app which makes use of Clojure for carrying out some of the computation in the backend. However, I am unable to figure out a way to invoke my Clojure methods in Android.
My Clojure program looks like the following:
foobar.MAST (MAST.clj)
:
(ns foobar.MAST
(:gen-class
:name foobar.MAST
:methods [#^{:static true} [evaluate [String] String]]))
(defn -evaluate [input]
...)
(defn -main [& args]
...)
project.clj
:
(defproject foobar "0.1.0-SNAPSHOT"
:description "foobar"
:url "http://example.com/FIXME"
:license {:name "EPL-2.0 OR GPL-2.0-or-later WITH Classpath-exception-2.0"
:url "https://www.eclipse.org/legal/epl-2.0/"}
:dependencies [[org.clojure/clojure "1.10.1"]]
:repl-options {:init-ns foobar.core}
:target-pat "target/%s"
:profiles {:uberjar {:aot :all}}
:java-source-paths ["foobar"]
:main foobar.MAST)
I tried building the project with lein uberjar
, then adding the standalone jar in my Android Studio project structure. This allowed me to invoke MAST.evaluate(String)
when running a simple kotlin script, but when trying to run it in an Android app, it results in the following runtime error message.
E/AndroidRuntime: FATAL EXCEPTION: main
Process: com.example.cass, PID: 2404
java.lang.ExceptionInInitializerError
at clojure.lang.Reflector.invokeInstanceMethod(Reflector.java:97)
at clojure.core$fn__8493.invokeStatic(core.clj:7060)
at clojure.core$fn__8493.invoke(core.clj:7055)
at clojure.core__init.load(Unknown Source:7055)
at clojure.core__init.<clinit>(Unknown Source:94)
at java.lang.Class.classForName(Native Method)
at java.lang.Class.forName(Class.java:454)
at clojure.lang.RT.classForName(RT.java:2211)
at clojure.lang.RT.classForName(RT.java:2220)
at clojure.lang.RT.loadClassForName(RT.java:2239)
at clojure.lang.RT.load(RT.java:449)
at clojure.lang.RT.load(RT.java:424)
at clojure.lang.RT.<clinit>(RT.java:338)
at clojure.lang.Namespace.<init>(Namespace.java:34)
at clojure.lang.Namespace.findOrCreate(Namespace.java:176)
at clojure.lang.Var.internPrivate(Var.java:156)
at foobar.MAST.<clinit>(Unknown Source:4)
at foobar.MAST.evaluate(Unknown Source:0)
at com.example.cass.model.comp.TexLisp.evaluate(Texlisp.kt:22)
at com.example.cass.ui.solution.solution_graph.SolutionStepActivity$bindNonMutatingOperations$1.onClick(SolutionStepActivity.kt:55)
at android.view.View.performClick(View.java:7870)
at android.view.View.performClickInternal(View.java:7839)
at android.view.View.access$3600(View.java:886)
at android.view.View$PerformClick.run(View.java:29363)
at android.os.Handler.handleCallback(Handler.java:883)
at android.os.Handler.dispatchMessage(Handler.java:100)
at android.os.Looper.loop(Looper.java:237)
at android.app.ActivityThread.main(ActivityThread.java:7948)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:493)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1075)
Caused by: java.lang.NoSuchMethodException: canAccess [class java.lang.Object]
at java.lang.invoke.MethodHandles$Lookup.findVirtual(MethodHandles.java:923)
at clojure.lang.Reflector.<clinit>(Reflector.java:38)
at clojure.lang.Reflector.invokeInstanceMethod(Reflector.java:97)
at clojure.core$fn__8493.invokeStatic(core.clj:7060)
at clojure.core$fn__8493.invoke(core.clj:7055)
at clojure.core__init.load(Unknown Source:7055)
at clojure.core__init.<clinit>(Unknown Source:94)
at java.lang.Class.classForName(Native Method)
at java.lang.Class.forName(Class.java:454)
at clojure.lang.RT.classForName(RT.java:2211)
at clojure.lang.RT.classForName(RT.java:2220)
at clojure.lang.RT.loadClassForName(RT.java:2239)
at clojure.lang.RT.load(RT.java:449)
at clojure.lang.RT.load(RT.java:424)
at clojure.lang.RT.<clinit>(RT.java:338)
at clojure.lang.Namespace.<init>(Namespace.java:34)
at clojure.lang.Namespace.findOrCreate(Namespace.java:176)
at clojure.lang.Var.internPrivate(Var.java:156)
at foobar.MAST.<clinit>(Unknown Source:4)
at foobar.MAST.evaluate(Unknown Source:0)
at com.example.cass.model.comp.TexLisp.evaluate(Texlisp.kt:22)
at com.example.cass.ui.solution.solution_graph.SolutionStepActivity$bindNonMutatingOperations$1.onClick(SolutionStepActivity.kt:55)
at android.view.View.performClick(View.java:7870)
at android.view.View.performClickInternal(View.java:7839)
at android.view.View.access$3600(View.java:886)
at android.view.View$PerformClick.run(View.java:29363)
at android.os.Handler.handleCallback(Handler.java:883)
at android.os.Handler.dispatchMessage(Handler.java:100)
at android.os.Looper.loop(Looper.java:237)
at android.app.ActivityThread.main(ActivityThread.java:7948)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:493)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1075)
I have considered dexing my uberjar to allow it to work with Android's dalvik JVM in case that's the issue but I am unsure of how to proceed after obtaining a jar containing classes.dex of my Clojure program after running:
dx --dex --min-sdk-version="26" --output=".\MMAST.jar" "C:\path\to\foobar-0.1.0-SNAPSHOT-standalone.jar"
Is there a way to fix this or some other way of integrating Clojure into my Android project?