How to find resources in Kotlin?


#1

I thought I'd find the answer to this, and indeed my code compiles, but I eventually get a runtime error.

Here’s some simple code that tries to create a JavaFX UI from an FXML specification (a resource):

package ui

import kotlinfx.builders.*
import kotlinfx.properties.*
import javafx.application.Application
import javafx.stage.Stage
import javafx.fxml.FXMLLoader

fun main(args: Array<String>) {
  Application.launch(javaClass<LaRGUI>())
}

class LaRGUI : Application() {
  override fun start(stage: Stage?) {
  print(javaClass.getResource(“main.fxml”));
  Stage(stage, title = “Log and Report”) {
           scene = Scene(width = 900.0, height = 550.0) {
           root = FXMLLoader.load(javaClass.getResource(“main.fxml”));
           }
  }.show()
  }
}


Here is the error:


$ ~/gradle-2.3/bin/gradle ui
:compileKotlin
w: Annotations path entry points to a non-existent location:
:compileJava UP-TO-DATE
:processResources
:classes
:jar
:ui
nullException in Application start method
Exception in thread “main” java.lang.reflect.InvocationTargetException
  at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
  at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
  at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
  at java.lang.reflect.Method.invoke(Method.java:483)
  at sun.launcher.LauncherHelper$FXHelper.main(LauncherHelper.java:767)
Caused by: java.lang.RuntimeException: Exception in Application start method
  at com.sun.javafx.application.LauncherImpl.launchApplication1(LauncherImpl.java:875)
  at com.sun.javafx.application.LauncherImpl.lambda$launchApplication$147(LauncherImpl.java:157)
  at com.sun.javafx.application.LauncherImpl$$Lambda$48/99550389.run(Unknown Source)
  at java.lang.Thread.run(Thread.java:745)
Caused by: java.lang.NullPointerException: Location is required.
  at javafx.fxml.FXMLLoader.loadImpl(FXMLLoader.java:3201)
  at javafx.fxml.FXMLLoader.loadImpl(FXMLLoader.java:3169)
  at javafx.fxml.FXMLLoader.loadImpl(FXMLLoader.java:3142)
  at javafx.fxml.FXMLLoader.loadImpl(FXMLLoader.java:3118)
  at javafx.fxml.FXMLLoader.loadImpl(FXMLLoader.java:3098)
  at javafx.fxml.FXMLLoader.load(FXMLLoader.java:3091)
  at ui.LaRGUI$start$1$1.invoke(main.kt:24)
  at ui.LaRGUI$start$1$1.invoke(main.kt:19)
  at kotlinfx.builders.BuildersPackage$Scene$e907cc0b.Scene(Scene.kt:83)
  at kotlinfx.builders.BuildersPackage.Scene(Unknown Source)
  at ui.LaRGUI$start$1.invoke(main.kt:23)
  at ui.LaRGUI$start$1.invoke(main.kt:19)
  at kotlinfx.builders.BuildersPackage$Stage$651bb459.Stage(Stage.kt:48)
  at kotlinfx.builders.BuildersPackage.Stage(Unknown Source)
  at ui.LaRGUI.start(main.kt:22)
  at com.sun.javafx.application.LauncherImpl.lambda$launchApplication1$153(LauncherImpl.java:821)
  at com.sun.javafx.application.LauncherImpl$$Lambda$51/1084860161.run(Unknown Source)
  at com.sun.javafx.application.PlatformImpl.lambda$runAndWait$166(PlatformImpl.java:323)
  at com.sun.javafx.application.PlatformImpl$$Lambda$45/1469821799.run(Unknown Source)
  at com.sun.javafx.application.PlatformImpl.lambda$null$164(PlatformImpl.java:292)
  at com.sun.javafx.application.PlatformImpl$$Lambda$47/781040349.run(Unknown Source)
  at java.security.AccessController.doPrivileged(Native Method)
  at com.sun.javafx.application.PlatformImpl.lambda$runLater$165(PlatformImpl.java:291)
  at com.sun.javafx.application.PlatformImpl$$Lambda$46/626202354.run(Unknown Source)
  at com.sun.glass.ui.InvokeLaterDispatcher$Future.run(InvokeLaterDispatcher.java:95)
  at com.sun.glass.ui.win.WinApplication._runLoop(Native Method)
  at com.sun.glass.ui.win.WinApplication.lambda$null$141(WinApplication.java:102)
  at com.sun.glass.ui.win.WinApplication$$Lambda$37/128893786.run(Unknown Source)
  … 1 more
:ui FAILED

I’ve attached an image of my project structure. Also, I verfied main.fxml is getting put into the jar file:

$ unzip LaR.jar
Archive:  LaR.jar
  creating: META-INF/
  inflating: META-INF/MANIFEST.MF
  creating: ui/
  inflating: ui/LaRGUI$start$1$1.class
  inflating: ui/LaRGUI$start$1.class
  inflating: ui/LaRGUI.class
  inflating: ui/UiPackage$main$95ba707c.class
  inflating: ui/UiPackage.class
  inflating: main.fxml

Any ideas what I may be doing wrong? Thanks!



LaR_Resource.PNG (75.8 KB)

#2

See: http://stackoverflow.com/a/22011185


#3

I had actually looked at that page, but didn't see anything very helpful. getClass is apparently replaced by javaClass in Kotlin, which I was able to compile successfuly. I believe I've also put my fxml resource in the designated place.


#4

Have you tried "javaClass.getClassLoader().getResource("main.fxml")" or "javaClass.getResource("/main.fxml")"


#5

Thanks, it was a bit strange to me that getResource existed at two levels; I suppose the way I was using it was trying to get the resource of the javaClass class...

Also, I had to specify the class like this:

 
javaClass<LaRGUI>().getClassLoader().getResource("main.fxml")

I saw some code floading around like this:

Paths.get(javaClass<Unit>().getClassLoader()!!.getResource(".")!!.getPath()!!)!!.
getParent()!!.getParent()!!.getParent()!!.toFile().getAbsolutePath()

Any idea what the double “!” operator is?


#6

Its explained in the documentation: http://kotlinlang.org/docs/reference/null-safety.html#the--operator


#7

This trick does not seems to work now from inside lambdas. Any idea how to get the resources? I am using lambda as part of a dsl.

javaClass.classLoader.getResource("abcd.xyz") always returns null from lambda, but it is returning the resources from a normal top-level class.


#8

Do we have any better solutions to this?
I am currently using the below, which does not work sometimes,

private val soundUri = Uri.parse("android.resource://" + MainActivity::class.java.`package`!!.name + "/" + R.raw.audio_file)

#9

This is not a Kotlin problem, rather an android problem. You’re supposed to access resources (based on a context) using the Resources class. In this case https://developer.android.com/reference/android/content/res/Resources.html#openRawResourceFd(int) or https://developer.android.com/reference/android/content/res/Resources.html#openRawResource(int) might be the solution you’re looking for. Note that neither is accessible from outside the package. Btw. You get the package not from the MainActivity, but you can inject it into BuildConfig through a gradle configuration


#10

The two methods you have linked in the response, return InputStream, I would require a Uri to the resource file.

Thank you for your reply.


#11

You can do that. There is some further info here. But what is called package has been renamed to applicationId. It is the name of your app as far as android is concerned (the name in the manifest). It is actually available from the Context

This answer for images has a good solution


#12

Actually, my solution for Uri works. I just wanted to have a cleaner way of writing the Uri to a resource file.

Only MainActivity::class.java.package!!.name portion of the string is a bit weird. I wanted this to be a bit cleaner.

Edit: and above mentioned version of string does not work when I enable the debug version (using applicationIdSuffix “.debug”) of the app to be deployed on the device. Some light on the debug thing here.

Thank you for the links. :slight_smile: