Search code examples
androidin-app-billingplay-billing-librarybillingclient

Android - Context for BillingClient inside Fragment


I have a single activity application (java) and one of the fragments contains Subscription buttons. I'm setting up the Google Play Billing Library but when I set up the following:

private BillingClient billingClient = BillingClient.newBuilder(getActivity())
        .setListener(purchasesUpdatedListener)
        .enablePendingPurchases()
        .build();

I get this error:

java.lang.IllegalArgumentException: Please provide a valid Context.
at com.android.billingclient.api.BillingClient$Builder.build

I also tried

getContext()

and

getActivity.getApplicationContext() 

but didn't work either. Neither did requireContext().

What context should I pass if I'm inside a fragment?

UPDATE:

The subscription fragment is called by the SignUpFragemnt:

public class SignUpFragment extends Fragment {

@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
                         Bundle savedInstanceState) {

    View v = inflater.inflate(R.layout.fragment_sign_up, container, false);

    Button nextButton = v.findViewById(R.id.nextButton);
    nextButton.setOnClickListener(v1 -> {
        Navigation.findNavController(v1).navigate(R.id.subscriptionV3Fragment);
    });

    return v;
}
}

And this is Subscription Fragment:

public class SubscriptionV3Fragment extends Fragment implements View.OnClickListener {

    private static final String TAG = "SubscriptionV3Fragment";

    SkuDetails monthlyDetails;
    SkuDetails annualDetails;

    Context context = getActivity();

    private PurchasesUpdatedListener purchasesUpdatedListener = new PurchasesUpdatedListener() {
        @Override
        public void onPurchasesUpdated(@NonNull BillingResult billingResult, @Nullable List<Purchase> list) {

        }
    };

    private BillingClient billingClient = BillingClient.newBuilder(requireContext())
            .setListener(purchasesUpdatedListener)
            .enablePendingPurchases()
            .build();

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
                             Bundle savedInstanceState) {
        View v = inflater.inflate(R.layout.fragment_subscription_v3, container, false);

        startConnection();

        Button monthlySubscribe = v.findViewById(R.id.monthlySubscribeBut);
        monthlySubscribe.setOnClickListener(this);

        return v;
    }


    private void startConnection() {
        billingClient.startConnection(new BillingClientStateListener() {
            @Override
            public void onBillingServiceDisconnected() {
                // PENDING
                // Try to restart the connection on the next request to
                // Google Play by calling the startConnection() method.
            }

            @Override
            public void onBillingSetupFinished(@NonNull BillingResult billingResult) {
                if (billingResult.getResponseCode() ==  BillingClient.BillingResponseCode.OK) {
                    querySubscriptionProducts();
                }
            }
        });
    }


    private void querySubscriptionProducts() {
        List<String> skuList = new ArrayList<>();
        skuList.add("sub_monthly");
        skuList.add("sub_annual");

        SkuDetailsParams.Builder params = SkuDetailsParams.newBuilder();
        params.setSkusList(skuList).setType(BillingClient.SkuType.SUBS);

        billingClient.querySkuDetailsAsync(params.build(), new SkuDetailsResponseListener() {
            @Override
            public void onSkuDetailsResponse(@NonNull BillingResult billingResult, @Nullable List<SkuDetails> list) {
                Log.d(TAG, "onSkuDetailsResponse: " + list);
            }
        });
    }
}

Solution

  • The problem is not the context in this case, the problem is where it is being access from. When this part of your code is called, other methods of FragmentLifeCycle are still in work.

    private BillingClient billingClient = BillingClient.newBuilder(requireContext())
            .setListener(purchasesUpdatedListener)
            .enablePendingPurchases()
            .build();
    

    I would suggest you move it down inside after OnCreateView() or even better inside onViewCreated()

    Just assign it up top

    private BillingClient billingClient;
    

    Then inside onViewCreated() you can do this

    billingClient = BillingClient.newBuilder(requireContext())
            .setListener(purchasesUpdatedListener)
            .enablePendingPurchases()
            .build();