-
Notifications
You must be signed in to change notification settings - Fork 3k
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
[pigeon] Kotlin implementation for ProxyApis #6371
Changes from 41 commits
7d66bf7
d64d2a2
b64d723
89d3c92
1f7d0ad
fa3c943
2665c22
cadf14c
2bf6738
6437fc9
aea71cd
0c09e91
f114c8c
d57ac0d
ff1e210
7e345ac
db2a198
a7c94e5
7af4343
a7b20a0
f2a1d5b
f76265e
b8e70f6
4f2327d
5ade010
3b3a91b
ec37d62
186fe47
894e642
118606a
4e821cb
4205418
258653b
5070f6e
06d163b
79e994b
1b9dfc1
f91a51f
84e0a0c
bdf1aae
2ff9075
b0569f5
4f47756
03d1100
c49aff2
c7e5ad0
d1883be
3025438
9af2d08
78128b8
84879d2
90f38b6
505b500
172ef36
c294d49
eaf1302
03f8a9e
6a57229
38ab4a1
78ade11
fcb3aa5
61579bc
c981d97
60be3da
0b7de44
8974f28
4ae228d
e5ad0ce
77ac326
33e3742
927aeea
ce65169
1dd1eda
bd08aa0
b2ae858
b2b82f6
4627f4a
abe2c0d
92a7370
6d5b581
770b24c
9e59393
bc60c15
cb10073
3b3284a
cae65e8
82f0cee
233acf5
e810a8f
b95997e
24daaa1
f0aa519
fed5b58
ccb1783
0a2a643
942918b
89abc69
cc55be5
41fca50
78e1e48
e844875
af2965f
25d4515
c5a083e
676eb05
f7b662a
d79c8f0
f4ac560
f70c2f1
c1f23ae
b958879
ecf079c
98d088f
923e21e
f9d5b78
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,256 @@ | ||
// Copyright 2013 The Flutter Authors. All rights reserved. | ||
// Use of this source code is governed by a BSD-style license that can be | ||
// found in the LICENSE file. | ||
|
||
import '../generator_tools.dart'; | ||
|
||
/// The Kotlin `InstanceManager`. | ||
const String instanceManagerTemplate = ''' | ||
/** | ||
* Maintains instances used to communicate with the corresponding objects in Dart. | ||
* | ||
* <p>Objects stored in this container are represented by an object in Dart that is also stored in | ||
* an InstanceManager with the same identifier. | ||
* | ||
* <p>When an instance is added with an identifier, either can be used to retrieve the other. | ||
* | ||
* <p>Added instances are added as a weak reference and a strong reference. When the strong | ||
* reference is removed with [remove] and the weak reference is deallocated, the | ||
* `finalizationListener` is made with the instance's identifier. However, if the strong reference | ||
* is removed and then the identifier is retrieved with the intention to pass the identifier to Dart | ||
* (e.g. calling [getIdentifierForStrongReference]), the strong reference to the | ||
* instance is recreated. The strong reference will then need to be removed manually again. | ||
*/ | ||
@Suppress("UNCHECKED_CAST", "MemberVisibilityCanBePrivate", "unused") | ||
bparrishMines marked this conversation as resolved.
Show resolved
Hide resolved
|
||
class $instanceManagerClassName(private val finalizationListener: $_finalizationListenerClassName) { | ||
/** Interface for listening when a weak reference of an instance is removed from the manager. */ | ||
interface $_finalizationListenerClassName { | ||
fun onFinalize(identifier: Long) | ||
} | ||
|
||
private val identifiers = java.util.WeakHashMap<Any, Long>() | ||
private val weakInstances = HashMap<Long, java.lang.ref.WeakReference<Any>>() | ||
private val strongInstances = HashMap<Long, Any>() | ||
private val referenceQueue = java.lang.ref.ReferenceQueue<Any>() | ||
private val weakReferencesToIdentifiers = HashMap<java.lang.ref.WeakReference<Any>, Long>() | ||
Comment on lines
+45
to
+49
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'm sure you've already figured out this is the best way, but why is it that we need the java There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I used the Kotlin->Java converter from Android Studio and this is what it used. I tried using the Kotlin version of There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. What version do we need to use the kotlin version? Maybe we need a todo? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It looks like it was created in No version, including the latest |
||
private val handler = android.os.Handler(android.os.Looper.getMainLooper()) | ||
private var nextIdentifier: Long = minHostCreatedIdentifier | ||
private var hasFinalizationListenerStopped = false | ||
|
||
/** | ||
* Modifies the time interval used to define how often this instance removes garbage collected | ||
* weak references to native Android objects that this instance was managing. | ||
*/ | ||
var clearFinalizedWeakReferencesInterval: Long = 3000 | ||
set(value) { | ||
handler.removeCallbacks { this.releaseAllFinalizedInstances() } | ||
field = value | ||
releaseAllFinalizedInstances() | ||
} | ||
|
||
init { | ||
handler.postDelayed( | ||
{ releaseAllFinalizedInstances() }, | ||
clearFinalizedWeakReferencesInterval | ||
) | ||
} | ||
|
||
companion object { | ||
// Identifiers are locked to a specific range to avoid collisions with objects | ||
// created simultaneously from Dart. | ||
// Host uses identifiers >= 2^16 and Dart is expected to use values n where, | ||
// 0 <= n < 2^16. | ||
private const val minHostCreatedIdentifier: Long = 65536 | ||
private const val tag = "$instanceManagerClassName" | ||
|
||
/** | ||
* Instantiate a new manager. | ||
* | ||
* | ||
* When the manager is no longer needed, [stopFinalizationListener] must be called. | ||
* | ||
* @param finalizationListener the listener for garbage collected weak references. | ||
* @return a new `$instanceManagerClassName`. | ||
bparrishMines marked this conversation as resolved.
Show resolved
Hide resolved
|
||
*/ | ||
fun create(finalizationListener: $_finalizationListenerClassName): $instanceManagerClassName { | ||
return $instanceManagerClassName(finalizationListener) | ||
} | ||
} | ||
|
||
/** | ||
* Removes `identifier` and its associated strongly referenced instance, if present, from the | ||
* manager. | ||
* | ||
* @param identifier the identifier paired to an instance. | ||
* @param <T> the expected return type. | ||
* @return the removed instance if the manager contains the given identifier, otherwise `null` if | ||
* the manager doesn't contain the value. | ||
</T> */ | ||
fun <T> remove(identifier: Long): T? { | ||
logWarningIfFinalizationListenerHasStopped() | ||
return strongInstances.remove(identifier) as T? | ||
} | ||
|
||
/** | ||
* Retrieves the identifier paired with an instance. | ||
* | ||
* | ||
* If the manager contains a strong reference to `instance`, it will return the identifier | ||
* associated with `instance`. If the manager contains only a weak reference to `instance`, a new | ||
* strong reference to `instance` will be added and will need to be removed again with [remove]. | ||
* | ||
* | ||
* If this method returns a nonnull identifier, this method also expects the Dart | ||
* `$instanceManagerClassName` to have, or recreate, a weak reference to the Dart instance the | ||
* identifier is associated with. | ||
* | ||
* @param instance an instance that may be stored in the manager. | ||
* @return the identifier associated with `instance` if the manager contains the value, otherwise | ||
* `null` if the manager doesn't contain the value. | ||
*/ | ||
fun getIdentifierForStrongReference(instance: Any?): Long? { | ||
logWarningIfFinalizationListenerHasStopped() | ||
val identifier = identifiers[instance] | ||
if (identifier != null) { | ||
strongInstances[identifier] = instance!! | ||
} | ||
return identifier | ||
} | ||
|
||
/** | ||
* Adds a new instance that was instantiated from Dart. | ||
* | ||
* | ||
* The same instance can be added multiple times, but each identifier must be unique. This | ||
* allows two objects that are equivalent (e.g. the `equals` method returns true and their | ||
* hashcodes are equal) to both be added. | ||
* | ||
* @param instance the instance to be stored. | ||
* @param identifier the identifier to be paired with instance. This value must be >= 0 and | ||
* unique. | ||
*/ | ||
fun addDartCreatedInstance(instance: Any, identifier: Long) { | ||
logWarningIfFinalizationListenerHasStopped() | ||
addInstance(instance, identifier) | ||
} | ||
|
||
/** | ||
* Adds a new instance that was instantiated from the host platform. | ||
* | ||
* @param instance the instance to be stored. This must be unique to all other added instances. | ||
* @return the unique identifier (>= 0) stored with instance. | ||
*/ | ||
fun addHostCreatedInstance(instance: Any): Long { | ||
logWarningIfFinalizationListenerHasStopped() | ||
require(!containsInstance(instance)) { "Instance of \${instance.javaClass} has already been added." } | ||
val identifier = nextIdentifier++ | ||
addInstance(instance, identifier) | ||
return identifier | ||
} | ||
|
||
/** | ||
* Retrieves the instance associated with identifier. | ||
* | ||
* @param identifier the identifier associated with an instance. | ||
* @param <T> the expected return type. | ||
* @return the instance associated with `identifier` if the manager contains the value, otherwise | ||
* `null` if the manager doesn't contain the value. | ||
</T> */ | ||
fun <T> getInstance(identifier: Long): T? { | ||
logWarningIfFinalizationListenerHasStopped() | ||
val instance = weakInstances[identifier] as java.lang.ref.WeakReference<T>? | ||
return instance?.get() | ||
} | ||
|
||
/** | ||
* Returns whether this manager contains the given `instance`. | ||
* | ||
* @param instance the instance whose presence in this manager is to be tested. | ||
* @return whether this manager contains the given `instance`. | ||
*/ | ||
fun containsInstance(instance: Any?): Boolean { | ||
logWarningIfFinalizationListenerHasStopped() | ||
return identifiers.containsKey(instance) | ||
} | ||
|
||
/** | ||
* Stop the periodic run of the [$_finalizationListenerClassName] for instances that have been garbage | ||
bparrishMines marked this conversation as resolved.
Show resolved
Hide resolved
|
||
* collected. | ||
* | ||
* | ||
* The InstanceManager can continue to be used, but the [$_finalizationListenerClassName] will no | ||
* longer be called and methods will log a warning. | ||
*/ | ||
fun stopFinalizationListener() { | ||
handler.removeCallbacks { this.releaseAllFinalizedInstances() } | ||
hasFinalizationListenerStopped = true | ||
} | ||
|
||
/** | ||
* Removes all of the instances from this manager. | ||
* | ||
* | ||
* The manager will be empty after this call returns. | ||
*/ | ||
fun clear() { | ||
identifiers.clear() | ||
weakInstances.clear() | ||
strongInstances.clear() | ||
weakReferencesToIdentifiers.clear() | ||
} | ||
|
||
/** | ||
* Whether the [$_finalizationListenerClassName] is still being called for instances that are garbage | ||
* collected. | ||
* | ||
* | ||
* See [stopFinalizationListener]. | ||
*/ | ||
fun hasFinalizationListenerStopped(): Boolean { | ||
return hasFinalizationListenerStopped | ||
} | ||
|
||
private fun releaseAllFinalizedInstances() { | ||
if (hasFinalizationListenerStopped()) { | ||
return | ||
} | ||
var reference: java.lang.ref.WeakReference<Any>? | ||
while ((referenceQueue.poll() as java.lang.ref.WeakReference<Any>?).also { reference = it } != null) { | ||
val identifier = weakReferencesToIdentifiers.remove(reference) | ||
if (identifier != null) { | ||
weakInstances.remove(identifier) | ||
strongInstances.remove(identifier) | ||
finalizationListener.onFinalize(identifier) | ||
} | ||
} | ||
handler.postDelayed( | ||
{ releaseAllFinalizedInstances() }, | ||
clearFinalizedWeakReferencesInterval | ||
) | ||
} | ||
|
||
private fun addInstance(instance: Any, identifier: Long) { | ||
require(identifier >= 0) { "Identifier must be >= 0: \$identifier" } | ||
require(!weakInstances.containsKey(identifier)) { | ||
"Identifier has already been added: \$identifier" | ||
} | ||
val weakReference = java.lang.ref.WeakReference(instance, referenceQueue) | ||
identifiers[instance] = identifier | ||
weakInstances[identifier] = weakReference | ||
weakReferencesToIdentifiers[weakReference] = identifier | ||
strongInstances[identifier] = instance | ||
} | ||
|
||
private fun logWarningIfFinalizationListenerHasStopped() { | ||
if (hasFinalizationListenerStopped()) { | ||
Log.w( | ||
tag, | ||
"The manager was used after calls to the $_finalizationListenerClassName has been stopped." | ||
) | ||
} | ||
} | ||
} | ||
'''; | ||
|
||
const String _finalizationListenerClassName = | ||
'${classNamePrefix}FinalizationListener'; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why do we need Kotlin options in a class that's currently unaware of specific generators?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Since the generated code references the native class, each ProxyApi needs to provide the full class name with the package. I also think
Method.swiftFunction
is similar since it provides additional instructions for the Swift generator.