Skip to content

allows static method calls on JNIObject #14

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 5 commits into from
Aug 22, 2019
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
67 changes: 53 additions & 14 deletions Sources/JNI/JNIObjects.swift
Original file line number Diff line number Diff line change
@@ -1,23 +1,42 @@
import CJNI
import Dispatch

/// Designed to simplify calling a constructor and methods on a JavaClass
/// Subclass this and add the methods appropriate to the object you are constructing.
open class JNIObject {
public let javaClass: JavaClass
open class var className: String {
return "java.lang.object"
}

private static var classInstances = [String: JavaClass]()

public static var javaClass: JavaClass {
return DispatchQueue.main.sync {
if let classInstance = classInstances[className] {
return classInstance
}

let javaClassLocalRef = try! jni.FindClass(name: className.replacingFullstopsWithSlashes())
try! checkAndThrowOnJNIError()
let classInstance = jni.NewGlobalRef(javaClassLocalRef)!
classInstances[className] = classInstance

return classInstance
}
}

public let instance: JavaObject

required public init(_ instance: JavaObject) throws {
guard let globalInstanceRef = jni.NewGlobalRef(instance) else {
throw Error.couldntCreateGlobalRef
}

let javaClassLocalRef = try jni.GetObjectClass(obj: instance)
let javaClass = jni.NewGlobalRef(javaClassLocalRef)
try checkAndThrowOnJNIError()
self.javaClass = javaClass!
self.instance = globalInstanceRef
}

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

looks good overall, but we should deprecate the current convenience init and force use of the new API where users have to override className (it's weird to have a convenience init with className here)

Copy link
Member

@ephemer ephemer Aug 21, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think the best way to do that would be to add

@available(*, deprecated, message: "Override Self.className instead and use the initializers that don't take className as an argument")
convenience public init(_ className: String, arguments: [JavaParameterConvertible] = []) throws {
  // deprecated init
}

// note the variadic arguments syntax instead of the array
convenience public init(...arguments: [JavaParameterConvertible]) throws {
  // use `Self.javaClass` here
}

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i like the idea and i commited the changes. One thing, Self does not seem to be available here and i used type(of: self).javaClass to workaround it. Not really understanding why

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@available(*, deprecated, message: "Override Self.className instead and use the initializers that don't take className as an argument")
convenience public init(_ className: String, arguments: [JavaParameterConvertible] = []) throws {
let className = className.replacingFullstopsWithSlashes()
let javaClassLocalRef = try jni.FindClass(name: className)
Expand All @@ -29,22 +48,39 @@ open class JNIObject {
try self.init(instanceLocalRef)
}

convenience public init(arguments: JavaParameterConvertible...) throws {
guard let instanceLocalRef = try jni.callConstructor(on: type(of: self).javaClass, arguments: arguments) else {
throw Error.couldntCallConstructor
}

try self.init(instanceLocalRef)
}

deinit {
jni.DeleteGlobalRef(instance)
jni.DeleteGlobalRef(javaClass)
}

public func call(methodName: String, arguments: [JavaParameterConvertible] = []) throws {
try jni.call(methodName, on: self.instance, arguments: arguments)
}

public func call<T: JavaInitializableFromMethod & JavaParameterConvertible>(methodName: String, arguments: [JavaParameterConvertible] = []) throws -> T {
public func call<T: JavaInitializableFromMethod & JavaParameterConvertible>(
methodName: String,
arguments: [JavaParameterConvertible] = []
) throws -> T {
return try jni.call(methodName, on: self.instance, arguments: arguments)
}

public func callStatic(methodName: String, arguments: [JavaParameterConvertible] = []) throws {
public static func callStatic(methodName: String, arguments: [JavaParameterConvertible] = []) throws {
try jni.callStatic(methodName, on: self.javaClass, arguments: arguments)
}

public static func callStatic<T: JavaInitializableFromMethod & JavaParameterConvertible>(
methodName: String,
arguments: [JavaParameterConvertible] = []
) throws -> T {
return try jni.callStatic(methodName, on: self.javaClass, arguments: arguments)
}
}

extension JNIObject {
Expand All @@ -55,7 +91,10 @@ extension JNIObject {
}

extension JNI {
func callConstructor(on targetClass: JavaClass, arguments: [JavaParameterConvertible] = []) throws -> JavaObject? {
func callConstructor(
on targetClass: JavaClass,
arguments: [JavaParameterConvertible] = []
) throws -> JavaObject? {
let methodID = _env.pointee.pointee.GetMethodID(_env, targetClass, "<init>", arguments.methodSignature(returnType: nil))
try checkAndThrowOnJNIError()

Expand All @@ -64,19 +103,19 @@ extension JNI {
}

public extension JNI {
public func NewObject(targetClass: JavaClass, _ methodID: JavaMethodID, _ args: [JavaParameter]) throws -> JavaObject? {
let env = self._env
func NewObject(targetClass: JavaClass, _ methodID: JavaMethodID, _ args: [JavaParameter]) throws -> JavaObject? {
let env = self._env
var mutableArgs = args
let newObject = env.pointee.pointee.NewObject(env, targetClass, methodID, &mutableArgs)
try checkAndThrowOnJNIError()

return newObject
}
}

public func GetObjectClass(obj: JavaObject) throws -> JavaClass {
let env = self._env
func GetObjectClass(obj: JavaObject) throws -> JavaClass {
let env = self._env
let result = env.pointee.pointee.GetObjectClass(env, obj)
try checkAndThrowOnJNIError()
return result!
}
}
}