RevenueCat / purchases-flutter

Flutter plugin for in-app purchases and subscriptions. Supports iOS, macOS and Android.
https://www.revenuecat.com/
MIT License
600 stars 164 forks source link

Android Leak Memory on Purchases.getOfferings() #687

Open meomap opened 1 year ago

meomap commented 1 year ago

Environment

Other information

Problem seems gone when I replaced getOnResult(result) with a dedicated Result var for getOffering that can be set null when engine detached

Describe the bug

Plugin leaked main activity context

RCGitBot commented 1 year ago

👀 SDKONCALL-276 We've just linked this issue to our internal tracker and notified the team. Thank you for reporting, we're checking this out!

vegaro commented 1 year ago

Thanks for reporting this issue. Can you please share a snippet with the changes you did to make the leak disappear? We are using Result in the same way in all our method calls so it's kinda weird this only happens for getOfferings. Are you accessing the activity in any way from your Flutter code?

meomap commented 1 year ago

@vegaro Sure here is my workaround

diff --git a/android/src/main/java/com/revenuecat/purchases_flutter/PurchasesFlutterPlugin.java b/android/src/main/java/com/revenuecat/purchases_flutter/PurchasesFlutterPlugin.java
index 42fe666..edaa1ca 100644
--- a/android/src/main/java/com/revenuecat/purchases_flutter/PurchasesFlutterPlugin.java
+++ b/android/src/main/java/com/revenuecat/purchases_flutter/PurchasesFlutterPlugin.java
@@ -52,6 +52,7 @@ public class PurchasesFlutterPlugin implements FlutterPlugin, MethodCallHandler,
     @Nullable private Context applicationContext;
     @Nullable private MethodChannel channel;
     @Nullable private Activity activity;
+    @Nullable private Result offeringResult;

     private final Handler handler = new Handler(Looper.getMainLooper());

@@ -97,6 +98,7 @@ public class PurchasesFlutterPlugin implements FlutterPlugin, MethodCallHandler,
         }
         this.channel = null;
         this.applicationContext = null;
+        this.offeringResult = null;
     }

     @Override
@@ -389,7 +391,28 @@ public class PurchasesFlutterPlugin implements FlutterPlugin, MethodCallHandler,
     }

     private void getOfferings(final Result result) {
-        CommonKt.getOfferings(getOnResult(result));
+        offeringResult = result;
+        CommonKt.getOfferings(new OnResult() {
+            @Override
+            public void onReceived(Map<String, ?> map) {
+                synchronized(this) {
+                    if (offeringResult != null) {
+                        offeringResult.success(map);
+                    }
+                    offeringResult = null;
+                }
+            }
+
+            @Override
+            public void onError(ErrorContainer errorContainer) {
+                synchronized(this) {
+                    if (offeringResult != null) {
+                        reject(errorContainer, offeringResult);
+                    }
+                    offeringResult = null;
+                }
+            }
+        });
     }

     private void getProductInfo(ArrayList<String> productIDs, String type, final Result result) {

I use bunch of other plugins so I think they will also access the activity. I don't know but maybe the problem is at stream listener. It's weird that I don't see leak report anymore after applying this change.