Skip to content

Commit a566c05

Browse files
authored
fix(api): JNI issues (#1509)
1 parent ba360d1 commit a566c05

File tree

3 files changed

+54
-20
lines changed

3 files changed

+54
-20
lines changed

packages/amplify_api/android/src/main/kotlin/com/amazonaws/amplify/amplify_api/AmplifyApiPlugin.kt

Lines changed: 32 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,6 @@
1515

1616
package com.amazonaws.amplify.amplify_api
1717

18-
import android.content.Context
1918
import android.os.Handler
2019
import android.os.Looper
2120
import androidx.annotation.NonNull
@@ -42,15 +41,25 @@ import kotlinx.coroutines.Dispatchers
4241
/** AmplifyApiPlugin */
4342
class AmplifyApiPlugin : FlutterPlugin, MethodCallHandler {
4443

45-
private lateinit var channel: MethodChannel
46-
private lateinit var eventchannel: EventChannel
47-
private lateinit var context: Context
48-
private val graphqlSubscriptionStreamHandler: GraphQLSubscriptionStreamHandler
44+
private companion object {
45+
/**
46+
* API authorization providers configured during the `addPlugin` call.
47+
*
48+
* The auth providers require a reference to the active method channel to be able to
49+
* communicate back to Dart code. If the app is moved to the background and resumed,
50+
* the `Amplify.addPlugin` call does not re-configure auth providers, so these must
51+
* be instantiated only once but still maintain a reference to the active method channel.
52+
*/
53+
var flutterAuthProviders: FlutterAuthProviders? = null
54+
}
55+
56+
private var channel: MethodChannel? = null
57+
private var eventchannel: EventChannel? = null
58+
private var graphqlSubscriptionStreamHandler: GraphQLSubscriptionStreamHandler? = null
4959
private val logger = Amplify.Logging.forNamespace("amplify:flutter:api")
5060
private var dispatcher: CoroutineDispatcher
5161

5262
constructor() {
53-
graphqlSubscriptionStreamHandler = GraphQLSubscriptionStreamHandler()
5463
dispatcher = Dispatchers.IO
5564
}
5665

@@ -66,14 +75,14 @@ class AmplifyApiPlugin : FlutterPlugin, MethodCallHandler {
6675
private val handler = Handler(Looper.getMainLooper())
6776

6877
override fun onAttachedToEngine(@NonNull flutterPluginBinding: FlutterPlugin.FlutterPluginBinding) {
78+
graphqlSubscriptionStreamHandler = graphqlSubscriptionStreamHandler ?: GraphQLSubscriptionStreamHandler()
6979
channel = MethodChannel(flutterPluginBinding.binaryMessenger, "com.amazonaws.amplify/api")
70-
channel.setMethodCallHandler(this)
80+
channel!!.setMethodCallHandler(this)
7181
eventchannel = EventChannel(
7282
flutterPluginBinding.binaryMessenger,
7383
"com.amazonaws.amplify/api_observe_events"
7484
)
75-
eventchannel.setStreamHandler(graphqlSubscriptionStreamHandler)
76-
context = flutterPluginBinding.applicationContext
85+
eventchannel!!.setStreamHandler(graphqlSubscriptionStreamHandler)
7786
}
7887

7988
@Suppress("UNCHECKED_CAST")
@@ -90,10 +99,14 @@ class AmplifyApiPlugin : FlutterPlugin, MethodCallHandler {
9099
val authProvidersList: List<String> =
91100
(arguments["authProviders"] as List<*>?)?.cast() ?: listOf()
92101
val authProviders = authProvidersList.map { AuthorizationType.valueOf(it) }
102+
if (flutterAuthProviders == null) {
103+
flutterAuthProviders = FlutterAuthProviders(authProviders)
104+
}
105+
flutterAuthProviders!!.setMethodChannel(channel)
93106
Amplify.addPlugin(
94107
AWSApiPlugin
95108
.builder()
96-
.apiAuthProviders(FlutterAuthProviders(authProviders, channel).factory)
109+
.apiAuthProviders(flutterAuthProviders!!.factory)
97110
.build()
98111
)
99112
logger.info("Added API plugin")
@@ -119,7 +132,7 @@ class AmplifyApiPlugin : FlutterPlugin, MethodCallHandler {
119132
"subscribe" -> FlutterGraphQLApi(dispatcher).subscribe(
120133
result,
121134
arguments,
122-
graphqlSubscriptionStreamHandler
135+
graphqlSubscriptionStreamHandler!!
123136
)
124137
else -> result.notImplemented()
125138
}
@@ -149,7 +162,13 @@ class AmplifyApiPlugin : FlutterPlugin, MethodCallHandler {
149162
}
150163
}
151164

152-
override fun onDetachedFromEngine(@NonNull binding: FlutterPlugin.FlutterPluginBinding) {
153-
channel.setMethodCallHandler(null)
165+
override fun onDetachedFromEngine(@NonNull flutterPluginBinding: FlutterPlugin.FlutterPluginBinding) {
166+
flutterAuthProviders?.setMethodChannel(null)
167+
channel?.setMethodCallHandler(null)
168+
channel = null
169+
eventchannel?.setStreamHandler(null)
170+
eventchannel = null
171+
graphqlSubscriptionStreamHandler?.close()
172+
graphqlSubscriptionStreamHandler = null
154173
}
155174
}

packages/amplify_api/android/src/main/kotlin/com/amazonaws/amplify/amplify_api/GraphQLSubscriptionStreamHandler.kt

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,10 @@ class GraphQLSubscriptionStreamHandler : EventChannel.StreamHandler {
3232
eventSink = null
3333
}
3434

35+
fun close() {
36+
eventSink?.endOfStream()
37+
}
38+
3539
fun sendEvent(payload: Map<String, Any>?, id: String, type: GraphQLSubscriptionEventTypes) {
3640
handler.post {
3741
val result: MutableMap<String, Any?> = mutableMapOf(

packages/amplify_api/android/src/main/kotlin/com/amazonaws/amplify/amplify_api/auth/FlutterAuthProvider.kt

Lines changed: 18 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -29,10 +29,8 @@ import kotlinx.coroutines.*
2929
* Manages the shared state of all [FlutterAuthProvider] instances.
3030
*/
3131
class FlutterAuthProviders(
32-
private val authProviders: List<AuthorizationType>,
33-
private val methodChannel: MethodChannel
32+
private val authProviders: List<AuthorizationType>
3433
) {
35-
3634
private companion object {
3735
/**
3836
* Timeout on a single [getToken] call.
@@ -50,6 +48,19 @@ class FlutterAuthProviders(
5048
val coroutineName = CoroutineName(tag)
5149
}
5250

51+
/**
52+
* The method channel used for Android -> Flutter communication. Should be cleared when the API
53+
* plugin is detached from Flutter and set when it is reattached.
54+
*/
55+
private var methodChannel: MethodChannel? = null
56+
57+
/**
58+
* Configures the method channel for API authorization.
59+
*/
60+
fun setMethodChannel(methodChannel: MethodChannel?) {
61+
this.methodChannel = methodChannel
62+
}
63+
5364
/**
5465
* A factory of [FlutterAuthProvider] instances.
5566
*/
@@ -74,7 +85,7 @@ class FlutterAuthProviders(
7485
fun getToken(authType: AuthorizationType): String? {
7586
// Not blocking the main thread is required for making platform channel calls without
7687
// deadlock.
77-
if (Thread.currentThread() == Looper.getMainLooper().thread) {
88+
if (Thread.currentThread() == Looper.getMainLooper().thread || methodChannel == null) {
7889
Log.e(tag, ExceptionMessages.createGithubIssueString)
7990
return null
8091
}
@@ -93,7 +104,7 @@ class FlutterAuthProviders(
93104
}
94105

95106
override fun error(
96-
errorCode: String?,
107+
errorCode: String,
97108
errorMessage: String?,
98109
errorDetails: Any?
99110
) {
@@ -109,7 +120,7 @@ class FlutterAuthProviders(
109120
}
110121
}
111122
launch(Dispatchers.Main) {
112-
methodChannel.invokeMethod(
123+
methodChannel!!.invokeMethod(
113124
"getLatestAuthToken",
114125
authType.name,
115126
result
@@ -152,4 +163,4 @@ class FlutterAuthProvider(
152163

153164
override fun getLatestAuthToken(): String =
154165
provider.getToken(type) ?: throw noTokenAvailable(type)
155-
}
166+
}

0 commit comments

Comments
 (0)