Skip to content

Conversation

SynSzakala
Copy link
Contributor

@SynSzakala SynSzakala commented Feb 26, 2023

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

  • I read the Contributor Guide and followed the process outlined there for submitting PRs.
  • I read the Tree Hygiene wiki page, which explains my responsibilities.
  • I read and followed the relevant style guides and ran the auto-formatter. (Unlike the flutter/flutter repo, the flutter/packages repo does use dart format.)
  • I signed the CLA.
  • The title of the PR starts with the name of the package surrounded by square brackets, e.g. [shared_preferences]
  • I listed at least one issue that this PR fixes in the description above.
  • I updated pubspec.yaml with an appropriate new version according to the pub versioning philosophy, or this PR is exempt from version changes.
  • I updated CHANGELOG.md to add a description of the change, following repository CHANGELOG style.
  • I updated/added relevant documentation (doc comments with ///).
  • I added new tests to check the change I am making, or this PR is test-exempt.
  • All existing and new tests are passing.

If you need help, consider asking for advice on the #hackers-new channel on Discord.

…econnect

# Conflicts:
#	packages/in_app_purchase/in_app_purchase_android/CHANGELOG.md
#	packages/in_app_purchase/in_app_purchase_android/pubspec.yaml
@SynSzakala
Copy link
Contributor Author

@gmackall, @bparrishMines re-requesting review after migrating this PR. Hope we can get this merged soon!

@SynSzakala SynSzakala changed the title Recreating PR from flutter/plugins [in_app_purchase_android] Implement BillingClient connection management and introduce BillingClientManager Feb 26, 2023
@SynSzakala SynSzakala changed the title [in_app_purchase_android] Implement BillingClient connection management and introduce BillingClientManager [in_app_purchase_android] Implement BillingClient connection management and introduce BillingClientManager Feb 26, 2023
Copy link
Contributor

@bparrishMines bparrishMines left a 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(() {
Copy link
Contributor

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());
Copy link
Contributor

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());
Copy link
Contributor

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,
Copy link
Contributor

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.

Copy link
Contributor Author

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?

Copy link
Contributor

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 {
Copy link
Contributor

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];
Copy link
Contributor

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>(
Copy link
Contributor

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.

Copy link
Contributor Author

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.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How about runWithClient?

Copy link
Contributor Author

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;
Copy link
Contributor

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.

Copy link
Contributor Author

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.

@SynSzakala SynSzakala requested review from bparrishMines and removed request for gmackall March 6, 2023 09:09
Copy link
Contributor

@bparrishMines bparrishMines left a 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] :
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit:

Suggested change
/// After calling [dispose] :
/// After calling [dispose]:

/// API to free up the resources.
///
/// After calling [dispose] :
/// - Further connection attempts will not be made;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: To be consistent

Suggested change
/// - 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;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit:

Suggested change
/// - [purchasesUpdatedStream] will be closed;
/// - [purchasesUpdatedStream] will be closed.

Comment on lines 48 to 50
/// In order to access the [BillingClient], consider using [runWithClient]
/// and [runWithClientNonRetryable]
/// methods.
Copy link
Contributor

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:

Suggested change
/// In order to access the [BillingClient], consider using [runWithClient]
/// and [runWithClientNonRetryable]
/// methods.
/// In order to access the [BillingClient], use [runWithClient]
/// or [runWithClientNonRetryable] methods.

jakub.walusiak added 3 commits March 11, 2023 19:13
…_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
Copy link
Contributor

@bparrishMines bparrishMines left a 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.
Copy link
Contributor

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
Copy link
Contributor

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?

@SynSzakala
Copy link
Contributor Author

Done! Also ping @stuartmorgan on this review.

Copy link
Contributor

@bparrishMines bparrishMines left a 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

Copy link
Contributor

@tarrinneal tarrinneal left a 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.

@SynSzakala
Copy link
Contributor Author

@tarrinneal I think we actually might go forward and merge this, since @stuartmorgan already gave his approval before the migration.

@bparrishMines
Copy link
Contributor

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.

@bparrishMines bparrishMines added the autosubmit Merge PR when tree becomes green via auto submit App label Apr 10, 2023
@auto-submit auto-submit bot merged commit b0a58d5 into flutter:main Apr 10, 2023
engine-flutter-autoroll added a commit to engine-flutter-autoroll/flutter that referenced this pull request Apr 10, 2023
…n management and introduce `BillingClientManager` (flutter/packages#3303)
engine-flutter-autoroll added a commit to engine-flutter-autoroll/flutter that referenced this pull request Apr 10, 2023
…n management and introduce `BillingClientManager` (flutter/packages#3303)
nploi pushed a commit to nploi/packages that referenced this pull request Jul 16, 2023
…ment and introduce `BillingClientManager` (flutter#3303)

[in_app_purchase_android] Implement `BillingClient` connection management and introduce `BillingClientManager`
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
autosubmit Merge PR when tree becomes green via auto submit App p: in_app_purchase platform-android
Projects
None yet
Development

Successfully merging this pull request may close these issues.

[in_app_purchase][android] BillingClient connection is not restored after it's lost
3 participants