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 Activity
in plaats van op de Fragment
waaruit het is aangeroepen (waar de IabHelper
zich bevindt).
Ik zou de onActivityResult()
in de bovenliggende Activity
kunnen ontvangen en vervolgens handmatig de onActivityResult()
op het Fragment
, maar is er een manier om startIntentSenderForResult()
aan te roepen vanuit een Fragment
dat 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.Fragment
bevat nu een achterwaarts compatibele versie van startIntentSenderForResult()
, dus dit antwoord is verouderd.
Oud antwoord:
Vanaf ondersteuningsbibliotheek 23.2.0 werkt het wijzigen van de requestCode
niet langer: FragmentActivity
houdt nu de verzoeken bij die door de fragmenten zijn gedaan. Ik heb deze methode toegevoegd aan de FragmentActivity
die het Fragment
hostte (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 Fragment
de 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);
Antwoord 8, autoriteit 3%
if (requestCode == RC_REQUEST)
{
Intent intent = new Intent(ContainerAvtivity.this,ContainerAvtivity.class);
startActivity(intent);
finish();
}
RC_REQUEST
is hetzelfde als waarmee u het aankoopproces startte
Voeg dit toe in de onActivityResult
van 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 startIntentSenderForResult
aan 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);
}