diff options
| author | landerlyoung <landerlyoung@gmail.com> | 2024-08-29 13:36:20 +0800 |
|---|---|---|
| committer | LanderlYoung <LanderlYoung@users.noreply.github.com> | 2024-08-29 14:33:42 +0800 |
| commit | 68ad768bb8e63f99d3efe8e3fee83584f624a7d7 (patch) | |
| tree | 7f83f8bafa2bf3c8062c69fa3864e1bd5b8035d2 | |
| parent | 17beebe2cca23863740ca310136026496774e52f (diff) | |
1. fix deprecated kotlin codeupstream/master
2. update demo code
| -rw-r--r-- | .github/workflows/ci.yml (renamed from .github/workflows/android.yml) | 2 | ||||
| -rw-r--r-- | README.md | 6 | ||||
| -rw-r--r-- | compiler/src/main/java/io/github/landerlyoung/jenny/HandyHelper.kt | 2 | ||||
| -rw-r--r-- | compiler/src/main/java/io/github/landerlyoung/jenny/NativeProxyGenerator.kt | 8 | ||||
| -rwxr-xr-x | run_sample_java_test | 2 | ||||
| -rw-r--r-- | sample-android/src/main/cpp/ComputeIntensiveClass.cpp | 78 | ||||
| -rw-r--r-- | sample-android/src/main/cpp/NativeDrawable.cpp | 35 | ||||
| -rw-r--r-- | sample-android/src/main/java/io/github/landerlyoung/jennysampleapp/MainActivity.java | 34 | ||||
| -rw-r--r-- | sample-android/src/main/res/layout/content_main.xml | 11 |
9 files changed, 117 insertions, 61 deletions
diff --git a/.github/workflows/android.yml b/.github/workflows/ci.yml index 59e3747..4296870 100644 --- a/.github/workflows/android.yml +++ b/.github/workflows/ci.yml @@ -1,4 +1,4 @@ -name: Android CI +name: CI on: pull_request: @@ -2,12 +2,12 @@ [![CI][CI_B]][CI] [![Publish][PUB_B]][PUB] [![MavenCentral][MV_B]][MV] ![GitHub code size in bytes][CS_B] ![GitHub][LC_B] -[CI_B]: https://github.com/LanderlYoung/Jenny/workflows/Android%20CI/badge.svg -[CI]: https://github.com/LanderlYoung/Jenny/actions?workflow=Android+CI +[CI_B]: https://github.com/LanderlYoung/Jenny/workflows/CI/badge.svg +[CI]: https://github.com/LanderlYoung/Jenny/actions?workflow=CI [PUB_B]: https://github.com/LanderlYoung/Jenny/workflows/Publish/badge.svg [PUB]: https://github.com/LanderlYoung/Jenny/actions?workflow=Publish [MV_B]: https://img.shields.io/maven-central/v/io.github.landerlyoung/jenny-annotation -[MV]: https://search.maven.org/artifact/io.github.landerlyoung/jenny-compiler +[MV]: https://central.sonatype.com/artifact/io.github.landerlyoung/jenny-compiler [CS_B]: https://img.shields.io/github/languages/code-size/LanderlYoung/Jenny [LC_B]: https://img.shields.io/github/license/LanderlYoung/Jenny diff --git a/compiler/src/main/java/io/github/landerlyoung/jenny/HandyHelper.kt b/compiler/src/main/java/io/github/landerlyoung/jenny/HandyHelper.kt index 29d0f11..d2400a6 100644 --- a/compiler/src/main/java/io/github/landerlyoung/jenny/HandyHelper.kt +++ b/compiler/src/main/java/io/github/landerlyoung/jenny/HandyHelper.kt @@ -1 +1 @@ -/**
* Copyright 2016 landerlyoung@gmail.com
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package io.github.landerlyoung.jenny
import java.util.ArrayDeque
import java.util.Locale
import javax.lang.model.element.Element
import javax.lang.model.element.ElementKind
import javax.lang.model.element.ExecutableElement
import javax.lang.model.element.Modifier
import javax.lang.model.element.TypeElement
import javax.lang.model.element.VariableElement
import javax.lang.model.type.ArrayType
import javax.lang.model.type.DeclaredType
import javax.lang.model.type.IntersectionType
import javax.lang.model.type.NoType
import javax.lang.model.type.PrimitiveType
import javax.lang.model.type.TypeKind
import javax.lang.model.type.TypeMirror
import javax.lang.model.type.TypeVariable
import javax.lang.model.type.WildcardType
/**
* Author: landerlyoung@gmail.com
* Date: 2014-12-17
* Time: 20:19
* Life with passion. Code with creativity!
*/
class HandyHelper(private val mEnv: Environment) {
fun getBinaryMethodSignature(method: ExecutableElement): String {
return Signature(method).toString()
}
fun getBinaryTypeSignature(type: TypeMirror): String {
return Signature(type).toString()
}
/**
*
* example
* Signature: (ILjava/lang/Runnable;LN/M_M;)V
* JNIEXPORT void JNICALL Java_N_n__ILjava_lang_Runnable_2LN_M_1M_2
*
* __ILjava_lang_Runnable_2LN_M_1M_2
*/
fun getMethodOverloadPostfix(method: ExecutableElement): String {
val signature = getBinaryMethodSignature(method)
val paramSig = signature.subSequence(signature.indexOf('(') + 1, signature.indexOf(")")).toString()
return "__" + paramSig.replace("_", "_1")
.replace("/", "_")
.replace(";", "_2")
.stripNonASCII()
}
/**
* @return like com.example_package.SomeClass$InnerClass
*/
fun getClassName(clazz: Element): String {
val className = ArrayDeque<String>()
val sb = StringBuilder()
var e: Element? = clazz
while (e != null && (e.kind.isClass || e.kind.isInterface)) {
className.add(e.simpleName.toString())
e = e.enclosingElement
}
val pkg = mEnv.elementUtils.getPackageOf(clazz)
if (pkg != null) {
val pkgName = pkg.qualifiedName.toString()
if (pkgName.isNotEmpty()) {
sb.append(pkgName)
sb.append('.')
}
}
while (!className.isEmpty()) {
sb.append(className.removeLast())
sb.append('$')
}
sb.deleteCharAt(sb.length - 1)
return sb.toString()
}
/**
* @return like com/example_package/SomeClass$InnerClass
*/
fun getSlashClassName(className: String): String {
return className.replace('.', '/')
}
/**
* @param className
*
* @return like com_example_1package_SomeClass_InnerClass
*/
fun toJNIClassName(className: String): String {
return className.replace("_", "_1")
.replace(".", "_")
.stripNonASCII()
}
fun getModifiers(m: Element): String =
m.modifiers.asSequence()
.filter { modifier ->
modifier == Modifier.PUBLIC
|| modifier == Modifier.PROTECTED
|| modifier == Modifier.PRIVATE
|| modifier == Modifier.FINAL
|| modifier == Modifier.STATIC
|| modifier == Modifier.ABSTRACT
|| modifier == Modifier.SYNCHRONIZED
}
.sorted()
.joinToString(" ") { it.toString().toLowerCase(Locale.US) }
fun getJavaMethodParam(m: ExecutableElement) =
m.parameters.joinToString(", ") {
it.asType().toString() + " " + it.simpleName
}
fun getReturnStatement(e: ExecutableElement): String = buildString {
val returnType = e.returnType
if (returnType is NoType) {
return@buildString
}
append("return ")
if (returnType is PrimitiveType) {
if (returnType.kind == TypeKind.BOOLEAN) {
append("JNI_FALSE")
} else {
append("0")
}
} else if (returnType.toString() == String::class.java.name) {
append("env->NewStringUTF(\"Hello From Jenny\")")
} else {
append("nullptr")
}
append(";")
}
fun getConstexprStatement(it: VariableElement): String {
val constValue = it.constantValue!!
val t = it.asType()
val nativeType = if (t == null) null else {
val jniType = toJNIType(t)
if (jniType == "jstring") {
"auto"
} else jniType
}
val value = if (constValue is Boolean) {
if (constValue) "JNI_TRUE" else "JNI_FALSE"
} else if (constValue is Number) {
constValue.toString()
} else if (constValue is Char) {
"'${constValue}'"
} else if (constValue is String) {
"u8\"$constValue\""
} else {
throw IllegalArgumentException("unknown type:$constValue " + constValue.javaClass)
}
return "static constexpr $nativeType ${it.simpleName} = ${value};"
}
fun getNativeMethodParam(m: ExecutableElement): String {
val sb = StringBuilder()
sb.append("JNIEnv* env")
if (m.modifiers.contains(Modifier.STATIC)) {
sb.append(", jclass clazz")
} else {
sb.append(", jobject thiz")
}
m.parameters.forEach { ve ->
sb.append(", ")
sb.append(toJNIType(ve.asType()))
sb.append(' ')
sb.append(ve.simpleName.toString())
}
return sb.toString()
}
fun isNestedClass(clazz: Element): Boolean {
val enclosingElement: Element? = clazz.enclosingElement
return (enclosingElement != null
&& enclosingElement.kind == ElementKind.CLASS
&& !clazz.modifiers.contains(Modifier.STATIC))
}
fun instanceOf(clazzName: String, typeMirror: TypeMirror): Boolean {
var t = typeMirror
while (clazzName != getNonGenericName(t)) {
val base = mEnv.typeUtils.asElement(t)
if (base is TypeElement) {
val superClazz = base.superclass
if (superClazz is NoType) return false
t = superClazz
} else {
return false
}
}
return true
}
fun getNonGenericName(t: TypeMirror): String = when (t) {
is DeclaredType -> getClassName(t.asElement())
is TypeVariable -> {
// function param
val upper = t.upperBound
getNonGenericName(
if (upper is IntersectionType) {
upper.bounds[0]
} else {
upper
}
)
}
is WildcardType -> {
// function param
t.extendsBound?.let {
getNonGenericName(it)
} ?: java.lang.Object::class.java.name
}
is ArrayType -> {
getNonGenericName(t.componentType) + "[]"
}
is PrimitiveType -> t.toString()
is NoType -> "void"
else -> throw IllegalArgumentException("TypeMirror kind: ${t.kind} is not supported ${t.javaClass}")
}
fun toJNIType(t: TypeMirror?): String {
if (t == null) return ""
// check if t is a subclass of java.lang.Throwable
if (instanceOf(Throwable::class.java.name, t)) {
return "jthrowable"
}
return when (t) {
is PrimitiveType -> "j${t.kind.name.toLowerCase(Locale.US)}"
is NoType -> "void"
is ArrayType -> {
if (t.componentType is PrimitiveType) {
"j${t.componentType.kind.name.toLowerCase(Locale.US)}Array"
} else {
"jobjectArray"
}
}
else -> {
if (t is DeclaredType) {
when (getClassName(t.asElement())) {
"java.lang.String" -> "jstring"
"java.lang.Class" -> "jclass"
else -> "jobject"
}
} else {
"jobject"
}
}
}
}
private inner class Signature(
private val mMethod: ExecutableElement?,
private val mType: TypeMirror?) {
constructor(method: ExecutableElement) : this(method, null)
constructor(type: TypeMirror) : this(null, type)
private fun StringBuilder.getSignatureClassName(_type: TypeMirror) {
var type = _type
while (type is ArrayType) {
append('[')
type = type.componentType
}
when (val name = getNonGenericName(type)) {
"char" -> append('C')
"byte" -> append('B')
"short" -> append('S')
"int" -> append('I')
"long" -> append('J')
"float" -> append('F')
"double" -> append('D')
"boolean" -> append('Z')
"void" -> append('V')
else -> append('L').append(name.replace('.', '/')).append(';')
}
}
override fun toString(): String = if (mMethod != null) {
buildString {
append('(')
if (mMethod.simpleName.contentEquals("<init>")) {
val clazz = mMethod.enclosingElement
if (isNestedClass(clazz)) {
// generate this$0 param for nested class
val enclosingClazz = clazz.enclosingElement
getSignatureClassName(enclosingClazz.asType())
}
}
for (param in mMethod.parameters) {
getSignatureClassName(param.asType())
}
append(')')
getSignatureClassName(mMethod.returnType)
return toString()
}
} else {
buildString { getSignatureClassName(mType!!) }
}
}
}
fun String.stripNonASCII(): String = this.replace("[^a-zA-Z0-9_]".toRegex()) {
String.format(Locale.US, "_%05x", it.value.codePointAt(0))
}
\ No newline at end of file +/**
* Copyright 2016 landerlyoung@gmail.com
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package io.github.landerlyoung.jenny
import java.util.ArrayDeque
import java.util.Locale
import javax.lang.model.element.Element
import javax.lang.model.element.ElementKind
import javax.lang.model.element.ExecutableElement
import javax.lang.model.element.Modifier
import javax.lang.model.element.TypeElement
import javax.lang.model.element.VariableElement
import javax.lang.model.type.ArrayType
import javax.lang.model.type.DeclaredType
import javax.lang.model.type.IntersectionType
import javax.lang.model.type.NoType
import javax.lang.model.type.PrimitiveType
import javax.lang.model.type.TypeKind
import javax.lang.model.type.TypeMirror
import javax.lang.model.type.TypeVariable
import javax.lang.model.type.WildcardType
/**
* Author: landerlyoung@gmail.com
* Date: 2014-12-17
* Time: 20:19
* Life with passion. Code with creativity!
*/
class HandyHelper(private val mEnv: Environment) {
fun getBinaryMethodSignature(method: ExecutableElement): String {
return Signature(method).toString()
}
fun getBinaryTypeSignature(type: TypeMirror): String {
return Signature(type).toString()
}
/**
*
* example
* Signature: (ILjava/lang/Runnable;LN/M_M;)V
* JNIEXPORT void JNICALL Java_N_n__ILjava_lang_Runnable_2LN_M_1M_2
*
* __ILjava_lang_Runnable_2LN_M_1M_2
*/
fun getMethodOverloadPostfix(method: ExecutableElement): String {
val signature = getBinaryMethodSignature(method)
val paramSig = signature.subSequence(signature.indexOf('(') + 1, signature.indexOf(")")).toString()
return "__" + paramSig.replace("_", "_1")
.replace("/", "_")
.replace(";", "_2")
.stripNonASCII()
}
/**
* @return like com.example_package.SomeClass$InnerClass
*/
fun getClassName(clazz: Element): String {
val className = ArrayDeque<String>()
val sb = StringBuilder()
var e: Element? = clazz
while (e != null && (e.kind.isClass || e.kind.isInterface)) {
className.add(e.simpleName.toString())
e = e.enclosingElement
}
val pkg = mEnv.elementUtils.getPackageOf(clazz)
if (pkg != null) {
val pkgName = pkg.qualifiedName.toString()
if (pkgName.isNotEmpty()) {
sb.append(pkgName)
sb.append('.')
}
}
while (!className.isEmpty()) {
sb.append(className.removeLast())
sb.append('$')
}
sb.deleteCharAt(sb.length - 1)
return sb.toString()
}
/**
* @return like com/example_package/SomeClass$InnerClass
*/
fun getSlashClassName(className: String): String {
return className.replace('.', '/')
}
/**
* @param className
*
* @return like com_example_1package_SomeClass_InnerClass
*/
fun toJNIClassName(className: String): String {
return className.replace("_", "_1")
.replace(".", "_")
.stripNonASCII()
}
fun getModifiers(m: Element): String =
m.modifiers.asSequence()
.filter { modifier ->
modifier == Modifier.PUBLIC
|| modifier == Modifier.PROTECTED
|| modifier == Modifier.PRIVATE
|| modifier == Modifier.FINAL
|| modifier == Modifier.STATIC
|| modifier == Modifier.ABSTRACT
|| modifier == Modifier.SYNCHRONIZED
}
.sorted()
.joinToString(" ") { it.toString().lowercase(Locale.US) }
fun getJavaMethodParam(m: ExecutableElement) =
m.parameters.joinToString(", ") {
it.asType().toString() + " " + it.simpleName
}
fun getReturnStatement(e: ExecutableElement): String = buildString {
val returnType = e.returnType
if (returnType is NoType) {
return@buildString
}
append("return ")
if (returnType is PrimitiveType) {
if (returnType.kind == TypeKind.BOOLEAN) {
append("JNI_FALSE")
} else {
append("0")
}
} else if (returnType.toString() == String::class.java.name) {
append("env->NewStringUTF(\"Hello From Jenny\")")
} else {
append("nullptr")
}
append(";")
}
fun getConstexprStatement(it: VariableElement): String {
val constValue = it.constantValue!!
val t = it.asType()
val nativeType = if (t == null) null else {
val jniType = toJNIType(t)
if (jniType == "jstring") {
"auto"
} else jniType
}
val value = if (constValue is Boolean) {
if (constValue) "JNI_TRUE" else "JNI_FALSE"
} else if (constValue is Number) {
constValue.toString()
} else if (constValue is Char) {
"'${constValue}'"
} else if (constValue is String) {
"u8\"$constValue\""
} else {
throw IllegalArgumentException("unknown type:$constValue " + constValue.javaClass)
}
return "static constexpr $nativeType ${it.simpleName} = ${value};"
}
fun getNativeMethodParam(m: ExecutableElement): String {
val sb = StringBuilder()
sb.append("JNIEnv* env")
if (m.modifiers.contains(Modifier.STATIC)) {
sb.append(", jclass clazz")
} else {
sb.append(", jobject thiz")
}
m.parameters.forEach { ve ->
sb.append(", ")
sb.append(toJNIType(ve.asType()))
sb.append(' ')
sb.append(ve.simpleName.toString())
}
return sb.toString()
}
fun isNestedClass(clazz: Element): Boolean {
val enclosingElement: Element? = clazz.enclosingElement
return (enclosingElement != null
&& enclosingElement.kind == ElementKind.CLASS
&& !clazz.modifiers.contains(Modifier.STATIC))
}
fun instanceOf(clazzName: String, typeMirror: TypeMirror): Boolean {
var t = typeMirror
while (clazzName != getNonGenericName(t)) {
val base = mEnv.typeUtils.asElement(t)
if (base is TypeElement) {
val superClazz = base.superclass
if (superClazz is NoType) return false
t = superClazz
} else {
return false
}
}
return true
}
fun getNonGenericName(t: TypeMirror): String = when (t) {
is DeclaredType -> getClassName(t.asElement())
is TypeVariable -> {
// function param
val upper = t.upperBound
getNonGenericName(
if (upper is IntersectionType) {
upper.bounds[0]
} else {
upper
}
)
}
is WildcardType -> {
// function param
t.extendsBound?.let {
getNonGenericName(it)
} ?: java.lang.Object::class.java.name
}
is ArrayType -> {
getNonGenericName(t.componentType) + "[]"
}
is PrimitiveType -> t.toString()
is NoType -> "void"
else -> throw IllegalArgumentException("TypeMirror kind: ${t.kind} is not supported ${t.javaClass}")
}
fun toJNIType(t: TypeMirror?): String {
if (t == null) return ""
// check if t is a subclass of java.lang.Throwable
if (instanceOf(Throwable::class.java.name, t)) {
return "jthrowable"
}
return when (t) {
is PrimitiveType -> "j${t.kind.name.lowercase(Locale.US)}"
is NoType -> "void"
is ArrayType -> {
if (t.componentType is PrimitiveType) {
"j${t.componentType.kind.name.lowercase(Locale.US)}Array"
} else {
"jobjectArray"
}
}
else -> {
if (t is DeclaredType) {
when (getClassName(t.asElement())) {
"java.lang.String" -> "jstring"
"java.lang.Class" -> "jclass"
else -> "jobject"
}
} else {
"jobject"
}
}
}
}
private inner class Signature(
private val mMethod: ExecutableElement?,
private val mType: TypeMirror?) {
constructor(method: ExecutableElement) : this(method, null)
constructor(type: TypeMirror) : this(null, type)
private fun StringBuilder.getSignatureClassName(_type: TypeMirror) {
var type = _type
while (type is ArrayType) {
append('[')
type = type.componentType
}
when (val name = getNonGenericName(type)) {
"char" -> append('C')
"byte" -> append('B')
"short" -> append('S')
"int" -> append('I')
"long" -> append('J')
"float" -> append('F')
"double" -> append('D')
"boolean" -> append('Z')
"void" -> append('V')
else -> append('L').append(name.replace('.', '/')).append(';')
}
}
override fun toString(): String = if (mMethod != null) {
buildString {
append('(')
if (mMethod.simpleName.contentEquals("<init>")) {
val clazz = mMethod.enclosingElement
if (isNestedClass(clazz)) {
// generate this$0 param for nested class
val enclosingClazz = clazz.enclosingElement
getSignatureClassName(enclosingClazz.asType())
}
}
for (param in mMethod.parameters) {
getSignatureClassName(param.asType())
}
append(')')
getSignatureClassName(mMethod.returnType)
return toString()
}
} else {
buildString { getSignatureClassName(mType!!) }
}
}
}
fun String.stripNonASCII(): String = this.replace("[^a-zA-Z0-9_]".toRegex()) {
String.format(Locale.US, "_%05x", it.value.codePointAt(0))
}
\ No newline at end of file diff --git a/compiler/src/main/java/io/github/landerlyoung/jenny/NativeProxyGenerator.kt b/compiler/src/main/java/io/github/landerlyoung/jenny/NativeProxyGenerator.kt index 6e3ae4f..5908229 100644 --- a/compiler/src/main/java/io/github/landerlyoung/jenny/NativeProxyGenerator.kt +++ b/compiler/src/main/java/io/github/landerlyoung/jenny/NativeProxyGenerator.kt @@ -400,7 +400,7 @@ class NativeProxyGenerator(env: Environment, clazz: TypeElement, nativeProxy: Na private fun StringBuilder.buildFieldDefines(useJniHelper: Boolean) { mFields.forEachIndexed { index, f -> val isStatic = f.modifiers.contains(Modifier.STATIC) - val camelCaseName = f.simpleName.toString().capitalize(Locale.ROOT) + val camelCaseName = f.simpleName.toString().capitalize() val getterSetters = hasGetterSetter(f) val fieldId = getFieldName(f, index) val typeForJniCall = getTypeForJniCall(f.asType()) @@ -850,10 +850,14 @@ class NativeProxyGenerator(env: Environment, clazz: TypeElement, nativeProxy: Na val result: String val k = type.kind result = if (k.isPrimitive || k == TypeKind.VOID) { - k.name.toLowerCase(Locale.US) + k.name.lowercase(Locale.US) } else { "object" } return result.capitalize() } } + +// replace deprecated kotlin-stdlib one +private fun String.capitalize():String = + replaceFirstChar { if (it.isLowerCase()) it.titlecase(Locale.US) else it.toString() }
\ No newline at end of file diff --git a/run_sample_java_test b/run_sample_java_test index 1c11dc6..d9a6b88 100755 --- a/run_sample_java_test +++ b/run_sample_java_test @@ -1,5 +1,5 @@ #! /usr/bin/env bash -set -e +set -e -x echo ">>> build :sample-java" ./gradlew :sample-java:build diff --git a/sample-android/src/main/cpp/ComputeIntensiveClass.cpp b/sample-android/src/main/cpp/ComputeIntensiveClass.cpp index fc35b83..c880600 100644 --- a/sample-android/src/main/cpp/ComputeIntensiveClass.cpp +++ b/sample-android/src/main/cpp/ComputeIntensiveClass.cpp @@ -11,7 +11,7 @@ #include "gen/jenny_fusion_proxies.h" /* - * Class: io_github_landerlyoung_jennysampleapp_ComputeIntensiveClass + * Class: io.github.landerlyoung.jennysampleapp.ComputeIntensiveClass * Method: public int addInNative(int a, int b) * Signature: (II)I */ @@ -21,7 +21,7 @@ jint ComputeIntensiveClass::addInNative(JNIEnv* env, jobject thiz, jint a, jint } /* - * Class: io_github_landerlyoung_jennysampleapp_ComputeIntensiveClass + * Class: io.github.landerlyoung.jennysampleapp.ComputeIntensiveClass * Method: public static void computeSomething(byte[] sth) * Signature: ([B)V */ @@ -30,7 +30,7 @@ void ComputeIntensiveClass::computeSomething(JNIEnv *env, jclass clazz, jbyteArr } /* - * Class: io_github_landerlyoung_jennysampleapp_ComputeIntensiveClass + * Class: io.github.landerlyoung.jennysampleapp.ComputeIntensiveClass * Method: public static java.lang.String greet() * Signature: ()Ljava/lang/String; */ @@ -39,7 +39,7 @@ jstring ComputeIntensiveClass::greet(JNIEnv *env, jclass clazz) { } /* - * Class: io_github_landerlyoung_jennysampleapp_ComputeIntensiveClass + * Class: io.github.landerlyoung.jennysampleapp.ComputeIntensiveClass * Method: public final void testParamParse(int a, java.lang.String b, long[] c, float[][] d, java.lang.Exception e, java.lang.Class<java.lang.String> f, java.util.HashMap<?,?> g) * Signature: (ILjava/lang/String;[J[[FLjava/lang/Exception;Ljava/lang/Class;Ljava/util/HashMap;)V */ @@ -48,7 +48,7 @@ void ComputeIntensiveClass::testParamParse(JNIEnv *env, jobject thiz, jint a, js } /* - * Class: io_github_landerlyoung_jennysampleapp_ComputeIntensiveClass + * Class: io.github.landerlyoung.jennysampleapp.ComputeIntensiveClass * Method: public static long returnsLong() * Signature: ()J */ @@ -57,7 +57,7 @@ jlong ComputeIntensiveClass::returnsLong(JNIEnv *env, jclass clazz) { } /* - * Class: io_github_landerlyoung_jennysampleapp_ComputeIntensiveClass + * Class: io.github.landerlyoung.jennysampleapp.ComputeIntensiveClass * Method: public static boolean returnsBool() * Signature: ()Z */ @@ -66,7 +66,7 @@ jboolean ComputeIntensiveClass::returnsBool(JNIEnv *env, jclass clazz) { } /* - * Class: io_github_landerlyoung_jennysampleapp_ComputeIntensiveClass + * Class: io.github.landerlyoung.jennysampleapp.ComputeIntensiveClass * Method: public static java.lang.Object returnsObject() * Signature: ()Ljava/lang/Object; */ @@ -93,7 +93,7 @@ void ComputeIntensiveClass::testOverload__I(JNIEnv *env, jclass clazz, jint i) { } /* - * Class: io_github_landerlyoung_jennysampleapp_ComputeIntensiveClass + * Class: io.github.landerlyoung.jennysampleapp.ComputeIntensiveClass * Method: public static java.lang.String httpGet(java.lang.String url) * Signature: (Ljava/lang/String;)Ljava/lang/String; */ @@ -139,38 +139,62 @@ void ComputeIntensiveClass::runJniHelperTest(JNIEnv* env, jclass clazz) { } /* - * Class: io_github_landerlyoung_jennysampleapp_ComputeIntensiveClass + * Class: io.github.landerlyoung.jennysampleapp.ComputeIntensiveClass * Method: public int computeThenCallback(io.github.landerlyoung.jennysampleapp.Callback listener) * Signature: (Lio/github/landerlyoung/jennysampleapp/Callback;)I */ jint ComputeIntensiveClass::computeThenCallback(JNIEnv* env, jobject thiz, jobject listener) { - CallbackProxy::onJobStart(env, listener); + static int count = 0; + bool useJniHelper = count++ % 2 == 0; + + if (useJniHelper) { + CallbackProxy proxy{listener, false}; + auto name = proxy.getName(); + + auto newInstance = CallbackProxy::newInstance(); + proxy.setLock(newInstance.getThis(false)); + proxy.onJobProgress(20); + + auto nestedClass = NestedClassProxy::newInstance(proxy.getThis(false)); + proxy.setLock(nestedClass.getThis(false)); + proxy.onJobProgress(50); - auto name = CallbackProxy::getName(env, listener); + CallbackProxy::setAStaticField(nullptr); + proxy.setCount(100); + proxy.setLock(proxy.getThis(false)); + proxy.onJobProgress(100); - auto newInstance = CallbackProxy::newInstance(env); - CallbackProxy::setLock(env, listener, newInstance); - CallbackProxy::onJobProgress(env, listener, 20); + auto str = jenny::toJavaString("Yes, callback from jni w/ jnihelper"); + proxy.onJobDone(JNI_TRUE, str); + } else { + CallbackProxy::onJobStart(env, listener); - auto nestedClass = NestedClassProxy::newInstance(env, listener); - CallbackProxy::setLock(env, newInstance, nestedClass); - CallbackProxy::onJobProgress(env, listener, 50); + auto name = CallbackProxy::getName(env, listener); - CallbackProxy::setAStaticField(env, nullptr); + auto newInstance = CallbackProxy::newInstance(env); + CallbackProxy::setLock(env, listener, newInstance); + CallbackProxy::onJobProgress(env, listener, 20); - CallbackProxy::setCount(env, listener, 100); - CallbackProxy::setLock(env, listener, listener); - CallbackProxy::onJobProgress(env, listener, 100); + auto nestedClass = NestedClassProxy::newInstance(env, listener); + CallbackProxy::setLock(env, newInstance, nestedClass); + CallbackProxy::onJobProgress(env, listener, 50); - jstring str = env->NewStringUTF("Yes, callback from jni"); - CallbackProxy::onJobDone(env, listener, JNI_TRUE, str); + CallbackProxy::setAStaticField(env, nullptr); - env->DeleteLocalRef(name); - env->DeleteLocalRef(str); + CallbackProxy::setCount(env, listener, 100); + CallbackProxy::setLock(env, listener, listener); + CallbackProxy::onJobProgress(env, listener, 100); - env->DeleteLocalRef(newInstance); - env->DeleteLocalRef(nestedClass); + jstring str = env->NewStringUTF("Yes, callback from jni w/o jnihelper"); + CallbackProxy::onJobDone(env, listener, JNI_TRUE, str); + + env->DeleteLocalRef(name); + env->DeleteLocalRef(str); + + env->DeleteLocalRef(newInstance); + env->DeleteLocalRef(nestedClass); + } return 0; } diff --git a/sample-android/src/main/cpp/NativeDrawable.cpp b/sample-android/src/main/cpp/NativeDrawable.cpp index 41d1b70..8a7a95f 100644 --- a/sample-android/src/main/cpp/NativeDrawable.cpp +++ b/sample-android/src/main/cpp/NativeDrawable.cpp @@ -33,26 +33,39 @@ public: /* - * Class: io_github_landerlyoung_jennysampleapp_NativeDrawable + * Class: io.github.landerlyoung.jennysampleapp.NativeDrawable * Method: private final long nativeInit() * Signature: ()J */ jlong NativeDrawable::nativeInit(JNIEnv *env, jobject thiz) { using jenny::GraphicsProxy; using android::StyleProxy; - GenericProxy::initClazz(env); - auto fillType = android::StyleProxy::getFILL(env); - auto paint = GraphicsProxy::newPaint(env); - GraphicsProxy::paintSetStyle(env, paint, fillType); + static int count = 0; + bool useJniHelper = count++ % 2 == 0; - auto paintGlobal = env->NewGlobalRef(paint); + if (useJniHelper) { + auto fillType = StyleProxy::getFILL(); + auto paint = GraphicsProxy::newPaint(); + GraphicsProxy::paintSetStyle(paint, fillType); - return reinterpret_cast<jlong>(new State(paintGlobal)); + auto paintGlobal = paint.toGlobal(); + return reinterpret_cast<jlong>(new State(paintGlobal.release())); + } else { + auto fillType = StyleProxy::getFILL(env); + auto paint = GraphicsProxy::newPaint(env); + GraphicsProxy::paintSetStyle(env, paint, fillType); + + auto paintGlobal = env->NewGlobalRef(paint); + + env->DeleteLocalRef(fillType); + env->DeleteLocalRef(paint); + return reinterpret_cast<jlong>(new State(paintGlobal)); + } } /* - * Class: io_github_landerlyoung_jennysampleapp_NativeDrawable + * Class: io.github.landerlyoung.jennysampleapp.NativeDrawable * Method: public final void onClick() * Signature: ()V */ @@ -62,7 +75,7 @@ void NativeDrawable::onClick(JNIEnv *env, jobject thiz) { } /* - * Class: io_github_landerlyoung_jennysampleapp_NativeDrawable + * Class: io.github.landerlyoung.jennysampleapp.NativeDrawable * Method: public void draw(android.graphics.Canvas canvas) * Signature: (Landroid/graphics/Canvas;)V */ @@ -82,10 +95,12 @@ void NativeDrawable::draw(JNIEnv *env, jobject thiz, jobject _canvas) { RectProxy::exactCenterY(env, bounds)) * 0.7f, state->paint ); + + env->DeleteLocalRef(bounds); } /* - * Class: io_github_landerlyoung_jennysampleapp_NativeDrawable + * Class: io.github.landerlyoung.jennysampleapp.NativeDrawable * Method: public final void release() * Signature: ()V */ diff --git a/sample-android/src/main/java/io/github/landerlyoung/jennysampleapp/MainActivity.java b/sample-android/src/main/java/io/github/landerlyoung/jennysampleapp/MainActivity.java index 15d8b91..ebcbbba 100644 --- a/sample-android/src/main/java/io/github/landerlyoung/jennysampleapp/MainActivity.java +++ b/sample-android/src/main/java/io/github/landerlyoung/jennysampleapp/MainActivity.java @@ -18,15 +18,18 @@ package io.github.landerlyoung.jennysampleapp; import android.annotation.SuppressLint; import android.os.Bundle; import android.view.View; +import android.widget.ImageView; import android.widget.TextView; import android.widget.Toast; -import androidx.appcompat.app.AppCompatActivity; -import androidx.appcompat.widget.Toolbar; + import com.google.android.material.floatingactionbutton.FloatingActionButton; -import com.google.android.material.snackbar.Snackbar; + import java.util.concurrent.Executor; import java.util.concurrent.Executors; +import androidx.appcompat.app.AppCompatActivity; +import androidx.appcompat.widget.Toolbar; + public class MainActivity extends AppCompatActivity { @@ -43,8 +46,8 @@ public class MainActivity extends AppCompatActivity { setContentView(R.layout.activity_main); mNativeDrawable = new NativeDrawable(); - View bg = findViewById(R.id.text); - bg.setBackground(mNativeDrawable); + ImageView bg = findViewById(R.id.image); + bg.setImageDrawable(mNativeDrawable); bg.setOnClickListener(v -> { bg.invalidate(); mNativeDrawable.onClick(); @@ -57,6 +60,7 @@ public class MainActivity extends AppCompatActivity { final ComputeIntensiveClass nativeClass = new ComputeIntensiveClass(); mTextView = findViewById(R.id.text); + mTextView.setOnClickListener(v -> mTextView.setText(null)); mTextView.setText("1 + 2 = " + nativeClass.addInNative(1, 2) + "\n"); mTextView.append(ComputeIntensiveClass.greet()); ComputeIntensiveClass.testOverload(); @@ -67,8 +71,10 @@ public class MainActivity extends AppCompatActivity { fab.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { - Snackbar.make(view, "Hello", Snackbar.LENGTH_SHORT) - .show(); + testComputeIntensiveClass(view); + } + + private void testComputeIntensiveClass(View view) { ComputeIntensiveClass.NestedNativeClass nestedNativeClass = new ComputeIntensiveClass.NestedNativeClass(); long handle = nestedNativeClass.nativeInit(); nestedNativeClass.testOverload(); @@ -79,12 +85,12 @@ public class MainActivity extends AppCompatActivity { @Override public void onJobDone(boolean success, String result) { toast("success=" + success + " result=" + result - + "\ncount=" + count + " obj==this = " + (lock == this)); + + "\ncount=" + count + " obj==this = " + (lock == this) + "\n"); } @Override public void onJobProgress(long progress) { - toast("onJobProgress = " + progress + " lock = " + System.identityHashCode(lock)); + toast("onJobProgress = " + progress + " lock = " + lock); } @Override @@ -104,8 +110,9 @@ public class MainActivity extends AppCompatActivity { } private void testNativeHttpGet() { - final String json = ComputeIntensiveClass - .httpGet("https://jsonplaceholder.typicode.com/todos/1"); + String url = "https://jsonplaceholder.typicode.com/todos/1"; + runOnUiThread(() -> toast("http get: " + url + "\n")); + final String json = ComputeIntensiveClass.httpGet(url); runOnUiThread(() -> toast("http got\n" + json)); } @@ -121,9 +128,4 @@ public class MainActivity extends AppCompatActivity { mTextView.append(msg); } - public void test(Callback callback) { - int a = Callback.COMPILE_CONSTANT_INT; - int b = callback.count; - int c = a + b; - } } diff --git a/sample-android/src/main/res/layout/content_main.xml b/sample-android/src/main/res/layout/content_main.xml index 416e148..f714105 100644 --- a/sample-android/src/main/res/layout/content_main.xml +++ b/sample-android/src/main/res/layout/content_main.xml @@ -15,6 +15,7 @@ android:layout_height="match_parent" android:text="Hello World!" android:padding="10dp" + android:fontFamily="monospace" app:layout_constraintBottom_toBottomOf="@+id/content_main" app:layout_constraintLeft_toLeftOf="@+id/content_main" app:layout_constraintRight_toRightOf="@+id/content_main" @@ -22,4 +23,14 @@ android:id="@+id/text" /> + <ImageView + android:id="@+id/image" + android:layout_width="256dp" + android:layout_height="256dp" + android:padding="16dp" + android:alpha="0.5" + app:layout_constraintRight_toRightOf="parent" + app:layout_constraintTop_toTopOf="parent" + /> + </androidx.constraintlayout.widget.ConstraintLayout> |
