从Fragment调用startIntentSenderForResult(Android Billing v3)

时间:2013-01-03 00:00:38

标签: android android-fragments android-activity android-billing

新的Android Billing v3文档和帮助程序代码在启动采购流程时使用startIntentSenderForResult()。我想从Fragment开始购买流程(并收到结果)。

例如,documentation建议调用

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

helper code来电

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

调用startIntentSenderForResult()

问题是,调用startIntentSenderForResult()会导致onActivityResult()在父Activity上调用,而不是在调用它的Fragment上调用IabHelper onActivityResult()居住)。

我可以在父Activity中收到onActivityResult(),然后手动调用Fragment上的startIntentSenderForResult(),但有没有办法调用Fragment 1}}从Fragment直接将结果返回到onActivityResult()的{​​{1}}?

11 个答案:

答案 0 :(得分:36)

我建议两种解决方案:

1。)将IabHelper mHelper放在活动上,并从片段中调用IabHelper。

类似的东西:

要使用此解决方案,请在活动中将IabHelper声明为public,并使用方法从Fragment中调用启动器。

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.)在onActivityResult中,调用包含IabHelper对象的相应片段。适当的片段可以具有对辅助对象的访问方法。

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);
    } 
}

答案 1 :(得分:9)

1)您应该修改resultCode(RC_REQUEST)以将片段索引放入其中。

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

2)在IabHelper.launchPurchaseFlow(...)

change mRequestCode = requestCode

mRequestCode = requestCode&0xffff;

答案 2 :(得分:3)

关于LEO上面非常有用的第二个解决方案:

如果Google修复了startIntentSenderForResult的问题并且它现在正确地将onActivityResult调用路由到片段,那么这个解决方案应该是面向未来的,以便 片段的onActivityResult没有被调用两次。

我想提出LEO提出的以下修改方案。

在Fragment的父级Activity实现中:

@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);
    }
}

然后在你的IAB片段实现中:

/**
 * 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);
    }
}

答案 3 :(得分:2)

如果您有权访问它,我建议您在基本活动类中创建此问题的某种通用处理。

例如:

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();
        }
    }
}

当然,您需要通过片段实现ActivityResultHandler接口,并在活动启动时注册它们。

答案 4 :(得分:2)

修改: android.support.v4.app.Fragment现在包含startIntentSenderForResult()的向后兼容版本,因此此答案已过时。

旧回答:

从支持库23.2.0开始,修改requestCode不再有效:FragmentActivity现在可以跟踪其片段发出的请求。我将此方法添加到托管FragmentActivity的{​​{1}}(代码基于Fragment):

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); } } 才会收到Fragment来电。

答案 5 :(得分:2)

从SDK 24及更高版本开始,支持Fragment中还有一个startIntentSenderForResult方法,它可以按预期工作。 请注意,还有一个附加的Bundle参数,可以作为null传递。因此,最终代码将是:

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

当然,对于API 23及更低版本,我们仍然需要使用其他答案中描述的技巧。

答案 6 :(得分:2)

您需要将片段和数据传递给父活动,然后从父活动调用片段onActivityResult。

像这样

片段中的

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

父活动中的

&#13;
&#13;
@Override
    public void onActivityResult(int requestCode, int resultCode, Intent data) {
        if (storeFragment != null) {
            storeFragment.onActivityResult(requestCode, resultCode, data);
        }
    }

    public void purchaseLauncher(StoreFragment storeFragment, IabHelper mHelper, String sku, int requestCode, IabHelper.OnIabPurchaseFinishedListener mPurchaseFinishedListener, String payload) {
        this.storeFragment = storeFragment;
        mHelper.launchPurchaseFlow(this, sku, requestCode, mPurchaseFinishedListener, payload);
    }
&#13;
&#13;
&#13;

答案 7 :(得分:1)

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

RC_REQUEST与您用于启动购买流程的内容相同

在你的Activity的onActivityResult中添加它。库存监听器将为你产生所需的结果。(我知道它是一个临时修复,但对我有用)。)

答案 8 :(得分:0)

您需要致电

super.onActivityResult(requestCode, resultCode, data);

在您的Activity和Fragment的onActivityResult的开头,将结果级联到片段。

在我的FragmentActivity中,这读为

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

答案 9 :(得分:0)

在我的情况下,我在Activity中执行了onActivityResult:

@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.");
    }

}

并且在片段中相同,它使应用结算工作

@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.");
    }

}

答案 10 :(得分:0)

如果您想要对您的片段进行回调,而不是从您的活动中调用super.onActivityResult()

这将调用您的片段onActivityResult()

不要忘记从片段上下文中调用startIntentSenderForResult

请勿使用活动上下文getActivity().startIntentSenderForResult