StartIntentSenderForResult aanroepen vanuit Fragment (Android Billing v3)

De nieuwe documentatie en helpercode voor Android Billing v3 gebruikt startIntentSenderForResult()bij het starten van een aankoopstroom. Ik wil een aankoopstroom starten (en het resultaat ontvangen) van een Fragment.

Bijvoorbeeld de documentatiestelt voor om te bellen

startIntentSenderForResult(pendingIntent.getIntentSender(),
    1001, new Intent(), Integer.valueOf(0), Integer.valueOf(0),
    Integer.valueOf(0));

en de helpercodeoproepen

mHelper.launchPurchaseFlow(this, SKU_GAS, 10001,   
    mPurchaseFinishedListener, "bGoa+V7g/yqDXvKRqq+JTFn4uQZbPiQJo4pf9RzJ");

die startIntentSenderForResult()aanroept.

Het probleem is dat het aanroepen van startIntentSenderForResult()ervoor zorgt dat onActivityResult()wordt aangeroepen op de bovenliggende Activityin plaats van op de Fragmentwaaruit het is aangeroepen (waar de IabHelperzich bevindt).

Ik zou de onActivityResult()in de bovenliggende Activitykunnen ontvangen en vervolgens handmatig de onActivityResult()op het Fragment, maar is er een manier om startIntentSenderForResult()aan te roepen vanuit een Fragmentdat het resultaat rechtstreeks terugstuurt naar dat Fragment‘ s onActivityResult()?


Antwoord 1, autoriteit 100%

Ik stel twee oplossingen voor:

1.) Zet ​​de IabHelper mHelper op de activiteit en roep de IabHelper op vanaf het fragment.

Zoiets als:

Om deze oplossing te gebruiken, verklaart u IabHelper als openbaar in de activiteit en gebruikt u een methode om de launcher vanuit het fragment aan te roepen.

public class MyActivity extends Activity{
    public IabHelper mHelper
    public purchaseLauncher(){
    mHelper.launchPurchaseFlow(this, SKU_GAS, 10001,   
         mPurchaseFinishedListener, "bGoa+V7g/yqDXvKRqq+JTFn4uQZbPiQJo4pf9RzJ");
   }
    /*The finished, query and consume listeners should also be implemented in here*/
}
public class FragmentActivity extends Fragment{
      MyActivity myAct = (MyActivity) getActivity();
      myAct.purchaseLauncher();
}

2.) Roep in onActivityResult het juiste fragment aan dat het IabHelper-object bevat. Het juiste fragment kan een toegangsmethode hebben tot het helperobject.

protected void onActivityResult(int requestCode, int resultCode,Intent data)    
{
    super.onActivityResult(requestCode, resultCode, data);
    FragmentManager fragmentManager = getSupportFragmentManager();
    Fragment fragment = fragmentManager.findFragmentByTag("YourTag");       
    if (fragment != null)
    {
        ((MyFragmentWithIabHelper)fragment).onActivityResult(requestCode, resultCode,data);
    } 
}

Antwoord 2, autoriteit 25%

1) U moet uw resultCode (RC_REQUEST) wijzigen om er een fragmentindex aan toe te voegen.

int rc_reqest = RC_REQUEST +  ((getActivity().getSupportFragmentManager().getFragments().indexOf(this)+1)<<16)  ;      
mHelper.launchPurchaseFlow(getActivity(), sku, rc_reqest ,mPurchaseFinishedListener, payload);

2) in IabHelper.launchPurchaseFlow(…)

change mRequestCode = requestCode

naar

mRequestCode = requestCode&0xffff;

Antwoord 3, autoriteit 11%

Van SDK 24 en hoger is er een startIntentSenderForResultmethode beschikbaar in ondersteuning Fragment ook, dat werkt zoals bedoeld.
Merk op dat er een extra bundelparameter is, die als null kan worden doorgegeven. De uiteindelijke code is dus:

startIntentSenderForResult(pendingIntent.getIntentSender(),
    1001, new Intent(), Integer.valueOf(0), Integer.valueOf(0),
    Integer.valueOf(0), null);

Natuurlijk moeten we voor API 23 en lager nog steeds de trucs gebruiken die in andere antwoorden zijn beschreven.


Antwoord 4, autoriteit 8%

Met betrekking tot de zeer nuttige 2e oplossing van LEO hierboven:

Als Google ooit het probleem met startIntentSenderForResult oplost en de aanroep van onActivityResult nu correct naar het fragment wordt gerouteerd, moet deze oplossing toekomstbestendig zijn, zodat
de onActivityResult van het fragment wordt niet twee keer aangeroepen.

Ik wil graag de volgende aangepaste oplossing voorstellen, voorgesteld door LEO.

In de implementatie van de bovenliggende activiteit van het fragment:

@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data)
{
    boolean handled = false;
    // The following is a hack to ensure that the InAppPurchasesFragment receives
    // its onActivityResult call.
    //
    // For more information on this issue, read here:
    //
    // http://stackoverflow.com/questions/14131171/calling-startintentsenderforresult-from-fragment-android-billing-v3
    //
    // Note: If Google ever fixes the issue with startIntentSenderForResult() and
    // starts forwarding on the onActivityResult to the fragment automatically, we
    // should future-proof this code so it will still work.
    //
    // If we don't do anything and always call super.onActivityResult, we risk 
    // having the billing fragment's onActivityResult called more than once for
    // the same result.
    //
    // To accomplish this, we create a method called checkIabHelperHandleActivityResult
    // in the billing fragment that returns a boolean indicating whether the result was 
    // handled or not.  We would just call Fragment's onActivityResult method, except 
    // its return value is void.
    //
    // Then call this new method in the billing fragment here and only call 
    // super.onActivityResult if the billing fragment didn't handle it.
    if (inAppPurchasesFragment != null)
    {
        handled = inAppPurchasesFragment.checkIabHelperHandleActivityResult(requestCode, resultCode, data);
    }
    if (!handled)
    {
        super.onActivityResult(requestCode, resultCode, data);
    }
}

Vervolgens in de implementatie van uw IAB-fragment:

/**
 * Allow the IabHelper to process an onActivityResult if it can
 * 
 * @param requestCode The request code
 * @param resultCode The result code
 * @param data The data
 * 
 * @return true if the IABHelper handled the result, else false
 */
public boolean checkIabHelperHandleActivityResult(int requestCode, int resultCode, Intent data)
{
    return (iabHelper != null) && iabHelper.handleActivityResult(requestCode, resultCode, data);
}
@Override
public void onActivityResult(int requestCode, int resultCode, Intent data)
{
    if (!checkIabHelperHandleActivityResult(requestCode, resultCode, data))
    {
        super.onActivityResult(requestCode, resultCode, data);
    }
}

Antwoord 5, autoriteit 6%

Ik raad aan om een ​​soort van algemene behandeling van dit probleem te maken in je basisactiviteitsklasse als je er toegang toe hebt.

Bijvoorbeeld:

public abstract class BaseActivity extends Activity {
    private List<ActivityResultHandler> mResultHandlers 
        = new ArrayList<ActivityResultHandler>();
    public void registerActivityResultHandler(ActivityResultHandler resultHandler) {
        mResultHandlers.add(resultHandler);
    }
    @Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
        super.onActivityResult(requestCode, resultCode, data);
        for (ActivityResultHandler resultHandler : mResultHandlers) {
            resultHandler.handle();
        }
    }
}

Natuurlijk moet je de ActivityResultHandler-interface implementeren op je fragmenten en ze registreren bij het opstarten van de activiteit.


Antwoord 6, autoriteit 6%

Bewerken:android.support.v4.app.Fragmentbevat nu een achterwaarts compatibele versie van startIntentSenderForResult(), dus dit antwoord is verouderd.

Oud antwoord:

Vanaf ondersteuningsbibliotheek 23.2.0 werkt het wijzigen van de requestCodeniet langer: FragmentActivityhoudt nu de verzoeken bij die door de fragmenten zijn gedaan. Ik heb deze methode toegevoegd aan de FragmentActivitydie het Fragmenthostte (code gebaseerd op FragmentActivity.startActivityFromFragment(Fragment, Intent, int, Bundle)) :

public void startIntentSenderFromFragment(Fragment fragment, IntentSender intent, int requestCode, @Nullable Intent fillInIntent, int flagsMask, int flagsValues, int extraFlags) throws IntentSender.SendIntentException {
    if (requestCode == -1) {
        startIntentSenderForResult(intent, requestCode, fillInIntent, flagsMask, flagsValues, extraFlags);
        return;
    }
    if ((requestCode & 0xffff0000) != 0) {
        throw new IllegalArgumentException("Can only use lower 16 bits for requestCode");
    }
    try {
        Method method = FragmentActivity.class.getDeclaredMethod("allocateRequestIndex", Fragment.class);
        method.setAccessible(true);
        int requestIndex = (int) method.invoke(this, fragment);
        startIntentSenderForResult(intent, ((requestIndex + 1) << 16) + (requestCode & 0xffff), fillInIntent, flagsMask, flagsValues, extraFlags);
    } catch (Exception e) {
        throw new RuntimeException(e);
    }
}

Als je dit aanroept, zal alleen het doorgegeven Fragmentde onActivityResult()-aanroep ontvangen.


Antwoord 7, autoriteit 6%

Je moet fragment en gegevens doorgeven aan bovenliggende activiteit en vervolgens het fragment aanroepen vanuit onActivityResult in bovenliggende activiteit.

zoals dit

in fragment:

HomeActivity activity = (HomeActivity) getActivity();
activity.purchaseLauncher(this, mHelper, productDTO.getSku(), RC_REQUEST, mPurchaseFinishedListener, PAYLOAD);

Snippet uitvouwen


Antwoord 8, autoriteit 3%

if (requestCode == RC_REQUEST) 
{
    Intent intent = new Intent(ContainerAvtivity.this,ContainerAvtivity.class);
    startActivity(intent);
    finish();
}

RC_REQUESTis hetzelfde als waarmee u het aankoopproces startte

Voeg dit toe in de onActivityResultvan uw activiteit. De inventarislistener zal het gewenste resultaat voor u produceren (ik weet dat het een tijdelijke oplossing is, maar werkte voor mij)).


Antwoord 9

In mijn geval deed ik onActivityResult in Activity :

@Override protected void onActivityResult(int requestCode, int resultCode, Intent data)
{
    if (!mHelper.handleActivityResult(requestCode, resultCode, data)) {
        // not handled, so handle it ourselves (here's where you'd
        // perform any handling of activity results not related to in-app
        // billing...
        super.onActivityResult(requestCode, resultCode, data);
    }
    else {
        Log.d(TAG, "onActivityResult handled by IABUtil.");
    }
}

en hetzelfde in fragment en het zorgt ervoor dat in app-facturering werkt

@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
    // Pass on the activity result to the helper for handling
    if (!mHelper.handleActivityResult(requestCode, resultCode, data)) {
        // not handled, so handle it ourselves (here's where you'd
        // perform any handling of activity results not related to in-app
        // billing...
        super.onActivityResult(requestCode, resultCode, data);
    }
    else {
        Log.d(ITEM_SKU, "onActivityResult handled by IABUtil.");
    }
}

Antwoord 10

als je teruggebeld wilt worden op je fragment, bel dan super.onActivityResult()vanaf je activiteit.

Dit roept je fragmenten onActivityResult()op.

En vergeet niet startIntentSenderForResultaan te roepen vanuit uw fragmentcontext.

Gebruik geen activiteitscontext getActivity().startIntentSenderForResult


Antwoord 11

Je moet bellen

super.onActivityResult(requestCode, resultCode, data);

aan het begin van de onActivityResult van uw activiteit en fragment om de resultaten naar de fragmenten te laten lopen.

In mijn FragmentActivity leest dit als

@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
    // No action here, call super to delegate to Fragments
    super.onActivityResult(requestCode, resultCode, data);
}

Other episodes