Skip to content

Commit ab91036

Browse files
Fix memory leaks in example app (#865)
**Summary** - Create `ActivitySourceCallback` to help with memory handling - Refactor and clean-up classes in example app to fix memory leaks as identified by LeakCanary - Call `CompositeSubscription#unsubscribe()` in `Activity#onDestroy()` where missing **Motivation** ANDROID-349 **Testing** Ran example app
1 parent 7208c62 commit ab91036

19 files changed

+236
-144
lines changed

example/src/main/java/com/stripe/example/activity/CustomerSessionActivity.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@ protected void onCreate(Bundle savedInstanceState) {
5050
@Override
5151
public void onStringResponse(String string) {
5252
if (string.startsWith("Error: ")) {
53-
mErrorDialogHandler.showError(string);
53+
mErrorDialogHandler.show(string);
5454
}
5555
}
5656
}));
@@ -68,7 +68,7 @@ public void onCustomerRetrieved(@NonNull Customer customer) {
6868
public void onError(int httpCode, @Nullable String errorMessage,
6969
@Nullable StripeError stripeError) {
7070
mSelectSourceButton.setEnabled(false);
71-
mErrorDialogHandler.showError(errorMessage);
71+
mErrorDialogHandler.show(errorMessage);
7272
mProgressBar.setVisibility(View.INVISIBLE);
7373
}
7474
});

example/src/main/java/com/stripe/example/activity/PaymentIntentActivity.java

Lines changed: 20 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -45,9 +45,11 @@ public class PaymentIntentActivity extends AppCompatActivity {
4545
private static final String TAG = PaymentIntentActivity.class.getName();
4646

4747
private static final String RETURN_URL = "stripe://payment_intent_return";
48+
49+
@NonNull private final CompositeSubscription mCompositeSubscription =
50+
new CompositeSubscription();
4851
private ProgressDialogController mProgressDialogController;
4952
private ErrorDialogHandler mErrorDialogHandler;
50-
private CompositeSubscription mCompositeSubscription;
5153
private Stripe mStripe;
5254
private StripeService mStripeService;
5355
private String mClientSecret;
@@ -60,16 +62,15 @@ public class PaymentIntentActivity extends AppCompatActivity {
6062
protected void onCreate(@Nullable Bundle savedInstanceState) {
6163
super.onCreate(savedInstanceState);
6264
setContentView(R.layout.activity_payment_intent_demo);
63-
Button createPaymentIntent = findViewById(R.id.btn_create_payment_intent);
65+
final Button createPaymentIntent = findViewById(R.id.btn_create_payment_intent);
6466
mRetrievePaymentIntent = findViewById(R.id.btn_retrieve_payment_intent);
6567
mConfirmPaymentIntent = findViewById(R.id.btn_confirm_payment_intent);
6668
mPaymentIntentValue = findViewById(R.id.payment_intent_value);
6769
mCardInputWidget = findViewById(R.id.card_input_widget);
68-
mProgressDialogController =
69-
new ProgressDialogController(getSupportFragmentManager());
7070

71+
mProgressDialogController = new ProgressDialogController(getSupportFragmentManager(),
72+
getResources());
7173
mErrorDialogHandler = new ErrorDialogHandler(getSupportFragmentManager());
72-
mCompositeSubscription = new CompositeSubscription();
7374
mStripe = new Stripe(getApplicationContext());
7475
Retrofit retrofit = RetrofitFactory.getInstance();
7576
mStripeService = retrofit.create(StripeService.class);
@@ -98,6 +99,13 @@ public void onClick(View v) {
9899
});
99100
}
100101

102+
@Override
103+
protected void onDestroy() {
104+
mCompositeSubscription.unsubscribe();
105+
super.onDestroy();
106+
}
107+
108+
@NonNull
101109
private Map<String, Object> createPaymentIntentParams() {
102110
final Map<String, Object> params = new HashMap<>();
103111
params.put("allowed_source_types[]", "card");
@@ -113,14 +121,13 @@ void createPaymentIntent() {
113121
.doOnSubscribe(new Action0() {
114122
@Override
115123
public void call() {
116-
mProgressDialogController.setMessageResource(R.string.creating_payment_intent);
117-
mProgressDialogController.startProgress();
124+
mProgressDialogController.show(R.string.creating_payment_intent);
118125
}
119126
})
120127
.doOnUnsubscribe(new Action0() {
121128
@Override
122129
public void call() {
123-
mProgressDialogController.finishProgress();
130+
mProgressDialogController.dismiss();
124131
}
125132
})
126133
.subscribe(
@@ -143,7 +150,7 @@ public void call(ResponseBody responseBody) {
143150
new Action1<Throwable>() {
144151
@Override
145152
public void call(Throwable throwable) {
146-
mErrorDialogHandler.showError(throwable.getLocalizedMessage());
153+
mErrorDialogHandler.show(throwable.getLocalizedMessage());
147154
}
148155
}
149156
);
@@ -165,14 +172,13 @@ public PaymentIntent call() throws Exception {
165172
.doOnSubscribe(new Action0() {
166173
@Override
167174
public void call() {
168-
mProgressDialogController.setMessageResource(R.string.retrieving_payment_intent);
169-
mProgressDialogController.startProgress();
175+
mProgressDialogController.show(R.string.retrieving_payment_intent);
170176
}
171177
})
172178
.doOnUnsubscribe(new Action0() {
173179
@Override
174180
public void call() {
175-
mProgressDialogController.finishProgress();
181+
mProgressDialogController.dismiss();
176182
}
177183
})
178184
.observeOn(AndroidSchedulers.mainThread()).subscribe(
@@ -216,14 +222,13 @@ public PaymentIntent call() throws Exception {
216222
.doOnSubscribe(new Action0() {
217223
@Override
218224
public void call() {
219-
mProgressDialogController.setMessageResource(R.string.confirming_payment_intent);
220-
mProgressDialogController.startProgress();
225+
mProgressDialogController.show(R.string.confirming_payment_intent);
221226
}
222227
})
223228
.doOnUnsubscribe(new Action0() {
224229
@Override
225230
public void call() {
226-
mProgressDialogController.finishProgress();
231+
mProgressDialogController.dismiss();
227232
}
228233
})
229234
.subscribe(

example/src/main/java/com/stripe/example/activity/PaymentMultilineActivity.java

Lines changed: 14 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package com.stripe.example.activity;
22

33
import android.os.Bundle;
4+
import android.support.annotation.NonNull;
45
import android.support.annotation.Nullable;
56
import android.support.v7.app.AppCompatActivity;
67
import android.widget.ListView;
@@ -37,7 +38,9 @@ public class PaymentMultilineActivity extends AppCompatActivity {
3738
ErrorDialogHandler mErrorDialogHandler;
3839

3940
CardMultilineWidget mCardMultilineWidget;
40-
CompositeSubscription mCompositeSubscription;
41+
42+
@NonNull private final CompositeSubscription mCompositeSubscription =
43+
new CompositeSubscription();
4144

4245
private SimpleAdapter mSimpleAdapter;
4346
private List<Map<String, String>> mCardSources= new ArrayList<>();
@@ -47,11 +50,10 @@ protected void onCreate(Bundle savedInstanceState) {
4750
super.onCreate(savedInstanceState);
4851
setContentView(R.layout.activity_payment_multiline);
4952

50-
mCompositeSubscription = new CompositeSubscription();
5153
mCardMultilineWidget = findViewById(R.id.card_multiline_widget);
5254

53-
mProgressDialogController =
54-
new ProgressDialogController(getSupportFragmentManager());
55+
mProgressDialogController = new ProgressDialogController(getSupportFragmentManager(),
56+
getResources());
5557

5658
mErrorDialogHandler = new ErrorDialogHandler(getSupportFragmentManager());
5759

@@ -100,14 +102,14 @@ public Source call() throws Exception {
100102
new Action0() {
101103
@Override
102104
public void call() {
103-
mProgressDialogController.startProgress();
105+
mProgressDialogController.show(R.string.progressMessage);
104106
}
105107
})
106108
.doOnUnsubscribe(
107109
new Action0() {
108110
@Override
109111
public void call() {
110-
mProgressDialogController.finishProgress();
112+
mProgressDialogController.dismiss();
111113
}
112114
})
113115
.subscribe(
@@ -120,7 +122,7 @@ public void call(Source source) {
120122
new Action1<Throwable>() {
121123
@Override
122124
public void call(Throwable throwable) {
123-
mErrorDialogHandler.showError(throwable.getLocalizedMessage());
125+
mErrorDialogHandler.show(throwable.getLocalizedMessage());
124126
}
125127
}));
126128
}
@@ -139,4 +141,9 @@ private void addToList(@Nullable Source source) {
139141
mSimpleAdapter.notifyDataSetChanged();
140142
}
141143

144+
@Override
145+
protected void onDestroy() {
146+
mCompositeSubscription.unsubscribe();
147+
super.onDestroy();
148+
}
142149
}

example/src/main/java/com/stripe/example/activity/PaymentSessionActivity.java

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -123,7 +123,7 @@ private void setupCustomerSession() {
123123
@Override
124124
public void onStringResponse(String string) {
125125
if (string.startsWith("Error: ")) {
126-
mErrorDialogHandler.showError(string);
126+
mErrorDialogHandler.show(string);
127127
}
128128
}
129129
}));
@@ -142,7 +142,7 @@ public void onError(int httpCode, @Nullable String errorMessage,
142142
mCustomer = null;
143143
mSelectPaymentButton.setEnabled(false);
144144
mSelectShippingButton.setEnabled(false);
145-
mErrorDialogHandler.showError(errorMessage);
145+
mErrorDialogHandler.show(errorMessage);
146146
mProgressBar.setVisibility(View.INVISIBLE);
147147
}
148148
});
@@ -287,7 +287,7 @@ public void onError(int errorCode, @Nullable String errorMessage) {
287287
return;
288288
}
289289

290-
activity.mErrorDialogHandler.showError(errorMessage);
290+
activity.mErrorDialogHandler.show(errorMessage);
291291
}
292292

293293
@Override

example/src/main/java/com/stripe/example/activity/RedirectActivity.java

Lines changed: 11 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -44,8 +44,10 @@ public class RedirectActivity extends AppCompatActivity {
4444
private static final String QUERY_CLIENT_SECRET = "client_secret";
4545
private static final String QUERY_SOURCE_ID = "source";
4646

47+
@NonNull private final CompositeSubscription mCompositeSubscription =
48+
new CompositeSubscription();
49+
4750
private CardInputWidget mCardInputWidget;
48-
private CompositeSubscription mCompositeSubscription;
4951
private RedirectAdapter mRedirectAdapter;
5052
private ErrorDialogHandler mErrorDialogHandler;
5153
private RedirectDialogController mRedirectDialogController;
@@ -58,10 +60,10 @@ protected void onCreate(Bundle savedInstanceState) {
5860
super.onCreate(savedInstanceState);
5961
setContentView(R.layout.activity_polling);
6062

61-
mCompositeSubscription = new CompositeSubscription();
6263
mCardInputWidget = findViewById(R.id.card_widget_three_d);
6364
mErrorDialogHandler = new ErrorDialogHandler(this.getSupportFragmentManager());
64-
mProgressDialogController = new ProgressDialogController(this.getSupportFragmentManager());
65+
mProgressDialogController = new ProgressDialogController(this.getSupportFragmentManager(),
66+
getResources());
6567
mRedirectDialogController = new RedirectDialogController(this);
6668
mStripe = new Stripe(getApplicationContext());
6769

@@ -113,8 +115,8 @@ protected void onNewIntent(Intent intent) {
113115

114116
@Override
115117
protected void onDestroy() {
116-
super.onDestroy();
117118
mCompositeSubscription.unsubscribe();
119+
super.onDestroy();
118120
}
119121

120122
void beginSequence() {
@@ -149,8 +151,7 @@ public Source call() throws Exception {
149151
.doOnSubscribe(new Action0() {
150152
@Override
151153
public void call() {
152-
mProgressDialogController.setMessageResource(R.string.createSource);
153-
mProgressDialogController.startProgress();
154+
mProgressDialogController.show(R.string.createSource);
154155
}
155156
})
156157
.subscribe(
@@ -176,15 +177,15 @@ public void call(Source source) {
176177
// The card Source can be used to create a 3DS Source
177178
createThreeDSecureSource(source.getId());
178179
} else {
179-
mProgressDialogController.finishProgress();
180+
mProgressDialogController.dismiss();
180181
}
181182

182183
}
183184
},
184185
new Action1<Throwable>() {
185186
@Override
186187
public void call(Throwable throwable) {
187-
mErrorDialogHandler.showError(throwable.getMessage());
188+
mErrorDialogHandler.show(throwable.getMessage());
188189
}
189190
}
190191
));
@@ -221,7 +222,7 @@ public Source call() throws Exception {
221222
.doOnUnsubscribe(new Action0() {
222223
@Override
223224
public void call() {
224-
mProgressDialogController.finishProgress();
225+
mProgressDialogController.dismiss();
225226
}
226227
})
227228
.subscribe(
@@ -238,7 +239,7 @@ public void call(Source source) {
238239
new Action1<Throwable>() {
239240
@Override
240241
public void call(Throwable throwable) {
241-
mErrorDialogHandler.showError(throwable.getMessage());
242+
mErrorDialogHandler.show(throwable.getMessage());
242243
}
243244
}
244245
));

example/src/main/java/com/stripe/example/controller/AsyncTaskTokenController.java

Lines changed: 37 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
import com.stripe.android.model.Card;
1313
import com.stripe.android.model.Token;
1414
import com.stripe.android.view.CardInputWidget;
15+
import com.stripe.example.R;
1516

1617
/**
1718
* Logic needed to create tokens using the {@link android.os.AsyncTask} methods included in the
@@ -21,7 +22,6 @@ public class AsyncTaskTokenController {
2122

2223
@NonNull private final Stripe mStripe;
2324
@NonNull private final ErrorDialogHandler mErrorDialogHandler;
24-
@NonNull private final ListViewController mOutputListController;
2525
@NonNull private final ProgressDialogController mProgressDialogController;
2626

2727
@Nullable private CardInputWidget mCardInputWidget;
@@ -30,19 +30,22 @@ public AsyncTaskTokenController(
3030
@NonNull Button button,
3131
@NonNull CardInputWidget cardInputWidget,
3232
@NonNull Context context,
33-
@NonNull ErrorDialogHandler errorDialogHandler,
34-
@NonNull ListViewController outputListController,
35-
@NonNull ProgressDialogController progressDialogController) {
33+
@NonNull final ErrorDialogHandler errorDialogHandler,
34+
@NonNull final ListViewController outputListController,
35+
@NonNull final ProgressDialogController progressDialogController) {
3636
mCardInputWidget = cardInputWidget;
3737
mStripe = new Stripe(context);
3838
mErrorDialogHandler = errorDialogHandler;
3939
mProgressDialogController = progressDialogController;
40-
mOutputListController = outputListController;
4140

4241
button.setOnClickListener(new View.OnClickListener() {
4342
@Override
4443
public void onClick(View view) {
45-
saveCard();
44+
saveCard(new TokenCallbackImpl(
45+
errorDialogHandler,
46+
outputListController,
47+
progressDialogController
48+
));
4649
}
4750
});
4851
}
@@ -51,25 +54,39 @@ public void detach() {
5154
mCardInputWidget = null;
5255
}
5356

54-
private void saveCard() {
55-
final Card cardToSave = mCardInputWidget.getCard();
57+
private void saveCard(@NonNull TokenCallback tokenCallback) {
58+
final Card cardToSave = mCardInputWidget != null ? mCardInputWidget.getCard() : null;
5659
if (cardToSave == null) {
57-
mErrorDialogHandler.showError("Invalid Card Data");
60+
mErrorDialogHandler.show("Invalid Card Data");
5861
return;
5962
}
60-
mProgressDialogController.startProgress();
63+
mProgressDialogController.show(R.string.progressMessage);
6164
mStripe.createToken(
6265
cardToSave,
6366
PaymentConfiguration.getInstance().getPublishableKey(),
64-
new TokenCallback() {
65-
public void onSuccess(@NonNull Token token) {
66-
mOutputListController.addToList(token);
67-
mProgressDialogController.finishProgress();
68-
}
69-
public void onError(@NonNull Exception error) {
70-
mErrorDialogHandler.showError(error.getLocalizedMessage());
71-
mProgressDialogController.finishProgress();
72-
}
73-
});
67+
tokenCallback);
68+
}
69+
70+
private static class TokenCallbackImpl implements TokenCallback {
71+
@NonNull private final ErrorDialogHandler mErrorDialogHandler;
72+
@NonNull private final ListViewController mOutputListController;
73+
@NonNull private final ProgressDialogController mProgressDialogController;
74+
75+
private TokenCallbackImpl(@NonNull ErrorDialogHandler errorDialogHandler,
76+
@NonNull ListViewController outputListController,
77+
@NonNull ProgressDialogController progressDialogController) {
78+
this.mErrorDialogHandler = errorDialogHandler;
79+
this.mOutputListController = outputListController;
80+
this.mProgressDialogController = progressDialogController;
81+
}
82+
83+
public void onSuccess(@NonNull Token token) {
84+
mOutputListController.addToList(token);
85+
mProgressDialogController.dismiss();
86+
}
87+
public void onError(@NonNull Exception error) {
88+
mErrorDialogHandler.show(error.getLocalizedMessage());
89+
mProgressDialogController.dismiss();
90+
}
7491
}
7592
}

0 commit comments

Comments
 (0)