55

startIntentSenderForResult()启动购买流程时使用新的 Android Billing v3 文档和帮助代码。我想从Fragment.

例如文档建议调用

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

帮助代码调用

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

调用startIntentSenderForResult().

问题是,调用startIntentSenderForResult()会导致onActivityResult()调用父级Activity而不是Fragment调用它的源(IabHelper驻留位置)。

我可以onActivityResult()在父级中接收Activity,然后在onActivityResult()上手动调用Fragment,但是有没有办法调用startIntentSenderForResult()from a ,Fragment将结果直接返回给 that FragmentonActivityResult()

4

11 回答 11

36

我建议两种解决方案:

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

就像是:

要使用此解决方案,请在活动中将 IabHelper 声明为公共,并使用一种方法从 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);
    } 
}
于 2013-01-03T01:24:47.483 回答
9

1)您应该修改您的结果代码(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;
于 2014-03-16T09:10:55.400 回答
4

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

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

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

于 2016-08-23T12:31:51.553 回答
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);
    }
}
于 2014-09-20T15:18:37.687 回答
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 接口并在活动启动时注册它们。

于 2014-08-09T20:38:47.610 回答
2

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

像这样

在片段中:

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

在家长活动中:

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

于 2017-04-03T11:14:44.880 回答
2

编辑: android.support.v4.app.Fragment现在包含一个向后兼容的版本startIntentSenderForResult(),所以这个答案已经过时了。

老答案:

从支持库 23.2.0 开始,修改requestCode不再起作用:FragmentActivity现在跟踪其片段发出的请求。我将此方法添加到FragmentActivity托管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人会收到 onActivityResult()调用。

于 2016-02-26T18:22:16.333 回答
1
if (requestCode == RC_REQUEST) 
{
    Intent intent = new Intent(ContainerAvtivity.this,ContainerAvtivity.class);
    startActivity(intent);
    finish();
}

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

将此添加到onActivityResult您的活动中。库存侦听器将为您产生所需的结果。(我知道它是临时修复,但对我有用))。

于 2014-11-21T12:08:54.527 回答
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.");
    }

}
于 2014-05-22T08:14:10.907 回答
0

如果您想在片段上获得回调而不是super.onActivityResult()从您的活动中调用。

这将调用您的 Fragments onActivityResult()

并且不要忘记startIntentSenderForResult从您的片段上下文中调用。

不要使用活动上下文getActivity().startIntentSenderForResult

于 2016-09-21T10:28:03.040 回答
-1

你需要打电话

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);
}
于 2013-04-09T15:40:17.047 回答