从Fragment调用startIntentSenderForResult(Android Billing v3)

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

例如文档build议调用

 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上调用,而不是从IabHelper所在的Fragment调用。

我可以在父Activity接收onActivityResult() ,然后手动调用Fragment上的onActivityResult() ,但有没有办法从Fragment调用startIntentSenderForResult() ,直接返回FragmentonActivityResult()

我build议两个解决scheme:

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

就像是:

要使用此解决scheme,请在活动中声明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)你应该修改你的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; 

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

如果Google用startIntentSenderForResult修复了这个问题,并且现在正确地将onActivityResult调用路由到了这个片段,那么这个解决scheme应该是未来validation的,这样片段的onActivityResult不会被调用两次。

我想提出LEO提出的以下修改的解决scheme。

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

我build议在基本活动类中创build一些通用的处理这个问题,如果你有权访问它。

例如:

 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接口,并在活动启动时注册它们。

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

老答案:

从支持库23.2.0开始,修改requestCode不再有效: FragmentActivity现在跟踪由其片段所做的请求。 我将这个方法添加到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()调用。

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

喜欢这个

片段:

 HomeActivity activity = (HomeActivity) getActivity(); activity.purchaseLauncher(this, mHelper, productDTO.getSku(), RC_REQUEST, mPurchaseFinishedListener, PAYLOAD); 
 if (requestCode == RC_REQUEST) { Intent intent = new Intent(ContainerAvtivity.this,ContainerAvtivity.class); startActivity(intent); finish(); } 

RC_REQUEST与您用来启动购买stream程的相同

把这个添加到Activity的onActivityResult 。库存监听器会为你生成想要的结果(我知道它是一个临时修复,但是对我有效))。

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

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

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

你需要打电话

 super.onActivityResult(requestCode, resultCode, data); 

在活动和片段的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); } 

在我的情况下,我在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."); } } 

如果你想得到你的片段的callback,而不是从你的activity中调用super.onActivityResult()

这将调用你的片段onActivityResult()

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

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