-
Notifications
You must be signed in to change notification settings - Fork 3.5k
[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
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.
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
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.
site-shared
Outdated
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. |
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
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.yaml
with an appropriate new version according to the pub versioning philosophy, or this PR is exempt from version changes.CHANGELOG.md
to 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.