-
Notifications
You must be signed in to change notification settings - Fork 3.6k
[in_app_purchase_android] Implement BillingClient connection management and introduce BillingClientManager
#3303
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
[in_app_purchase_android] Implement BillingClient connection management and introduce BillingClientManager
#3303
Conversation
…econnect # Conflicts: # packages/in_app_purchase/in_app_purchase_android/CHANGELOG.md # packages/in_app_purchase/in_app_purchase_android/pubspec.yaml
|
@gmackall, @bparrishMines re-requesting review after migrating this PR. Hope we can get this merged soon! |
BillingClient connection management and introduce BillingClientManager
bparrishMines
left a comment
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.
Thanks for the contribution!
| } | ||
|
|
||
| bool _debugAssertNotDisposed() { | ||
| assert(() { |
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.
I don't understand why one would throw a FlutterError inside of an assert. It defeats the point of having an assert. Would it not be better to make it this:
assert(!_isDisposed, 'my error message');| Future<R> run<R extends HasBillingResponse>( | ||
| Future<R> Function(BillingClient client) block, | ||
| ) async { | ||
| assert(_debugAssertNotDisposed()); |
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.
This is running an assert for a method that runs an assert. I think you could just have:
_debugAssertNotDisposed();| /// See [runRaw] for operations that return a subclass | ||
| /// of [HasBillingResponse]. | ||
| Future<R> runRaw<R>(Future<R> Function(BillingClient client) block) async { | ||
| assert(_debugAssertNotDisposed()); |
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.
Same for here for removing the assert.
| /// See [runRaw] for operations that do not return a subclass | ||
| /// of [HasBillingResponse]. | ||
| Future<R> run<R extends HasBillingResponse>( | ||
| Future<R> Function(BillingClient client) block, |
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.
This parameter could use a more descriptive name. Callback methods in Dart are typically given a name such as on<Action>. My assumption is that this could be called onBillingClientAvailable or onBillingClientReady.
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.
I don't think that'd be a good choice, since function that's passed here isn't a callback, it's called in place (across an optional asynchronous gap). This is more similar to Flutter's setState with argument named just fn. I agree that block is a bit ambiguous, what do you think about action instead?
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.
That's a good point. action works for me!
| /// | ||
| /// See [runRaw] for operations that return a subclass | ||
| /// of [HasBillingResponse]. | ||
| Future<R> runRaw<R>(Future<R> Function(BillingClient client) block) async { |
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.
Same with this block parameter.
| ), | ||
| skuDetailsList: const <SkuDetailsWrapper>[], | ||
| ); | ||
| responses = <SkuDetailsResponseWrapper>[response, response]; |
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.
Nice, this is good cleanup.
nit: Could you add a short comment above why this is leaving two identical responses. It wasn't immediately clear that they correspond with each of the queries done above.
| /// | ||
| /// See [runRaw] for operations that do not return a subclass | ||
| /// of [HasBillingResponse]. | ||
| Future<R> run<R extends HasBillingResponse>( |
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.
I think this method name could be more descriptive. As in it could be runWhenClientIsReady or runOnClientAvailable. I think this would make it easier to understand when this method is being used in the code. Same goes for the method runRaw.
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.
I also agree that run/runRaw is ambiguous. I don't think that runWhenClientIsReady, runOnClientAvailable would be good though. Main idea of BillingClientManager is to handle BillingClient connection transparently, so its user doesn't have to worry about client readiness/availability/retries. I suggest withClient and withClientNonRetryable instead.
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.
How about runWithClient?
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.
Sure, done.
| bool _isDisposed = false; | ||
|
|
||
| // Initialized immediately in the constructor, so it's always safe to access. | ||
| late Future<void> _readyFuture; |
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.
nit: This is optional, but a Completer<void> could be used instead of a Future<void>. This would make it so you would not have to depend on a late initializer.
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.
In this case this wouldn't make much difference - new Completer would always have to be created in _connect function anyway.
bparrishMines
left a comment
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.
It looks like you need to pull in the latest changes and run the formatter. Everything else looks good to me besides a few nits.
cc @stuartmorgan for a secondary review
| /// Consider calling [dispose] after you no longer need the [BillingClient] | ||
| /// API to free up the resources. | ||
| /// | ||
| /// After calling [dispose] : |
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.
nit:
| /// After calling [dispose] : | |
| /// After calling [dispose]: |
| /// API to free up the resources. | ||
| /// | ||
| /// After calling [dispose] : | ||
| /// - Further connection attempts will not be made; |
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.
nit: To be consistent
| /// - Further connection attempts will not be made; | |
| /// - Further connection attempts will not be made. |
| /// | ||
| /// After calling [dispose] : | ||
| /// - Further connection attempts will not be made; | ||
| /// - [purchasesUpdatedStream] will be closed; |
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.
nit:
| /// - [purchasesUpdatedStream] will be closed; | |
| /// - [purchasesUpdatedStream] will be closed. |
| /// In order to access the [BillingClient], consider using [runWithClient] | ||
| /// and [runWithClientNonRetryable] | ||
| /// methods. |
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 this is only visible for testing, it should recommend only using these methods.
nit:
| /// In order to access the [BillingClient], consider using [runWithClient] | |
| /// and [runWithClientNonRetryable] | |
| /// methods. | |
| /// In order to access the [BillingClient], use [runWithClient] | |
| /// or [runWithClientNonRetryable] methods. |
…_android_billing_client_reconnect # Conflicts: # packages/in_app_purchase/in_app_purchase_android/CHANGELOG.md # packages/in_app_purchase/in_app_purchase_android/pubspec.yaml
bparrishMines
left a comment
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.
LGTM
| @@ -1,3 +1,8 @@ | |||
| ## 0.2.5 | |||
|
|
|||
| * Fixes the management of `BillingClient` connection. | |||
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.
nit: This should include how the management is being fixed.
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.
I'm confident this shouldn't be changed, but I'm not familiar with submodules. Can you remove this diff?
|
Done! Also ping @stuartmorgan on this review. |
bparrishMines
left a comment
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.
LGTM
cc @stuartmorgan for secondary review
tarrinneal
left a comment
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.
This seems solid to me. I think it would be best to wait for @stuartmorgan to add his review before merging. If he doesn't, and you'd like to merge here is mine to allow for that as well.
|
@tarrinneal I think we actually might go forward and merge this, since @stuartmorgan already gave his approval before the migration. |
|
This was already approved in flutter/plugins#6309 and I was essentially giving this a secondary review. So this should be fine to submit for @tarrinneal and I. |
…n management and introduce `BillingClientManager` (flutter/packages#3303)
…n management and introduce `BillingClientManager` (flutter/packages#3303)
…ment and introduce `BillingClientManager` (flutter#3303) [in_app_purchase_android] Implement `BillingClient` connection management and introduce `BillingClientManager`
This PR adds management of BillingClient connection to Android implementation of InAppPurchase. This should fix issues caused by lost connections.
New and previously existing connection management is extracted to a new component, BillingClientManager.
This component is exported in billing_client_wrappers.dart, so that it can also be used when interacting with BillingClient directly.
Fixes flutter/flutter#110663
Migrated from flutter/plugins PR #6309
Pre-launch Checklist
dart format.)[shared_preferences]pubspec.yamlwith an appropriate new version according to the pub versioning philosophy, or this PR is exempt from version changes.CHANGELOG.mdto add a description of the change, following repository CHANGELOG style.///).If you need help, consider asking for advice on the #hackers-new channel on Discord.