从片段调用startIntentSenderForResult(Android计费v3)
新的Android Billing v3文档和帮助程序代码在启动购买流时使用从片段调用startIntentSenderForResult(Android计费v3),android,android-fragments,android-activity,android-billing,Android,Android Fragments,Android Activity,Android Billing,新的Android Billing v3文档和帮助程序代码在启动购买流时使用startinentsenderforresult()。我想从片段启动一个购买流(并接收结果) 例如,建议打电话 startIntentSenderForResult(pendingIntent.getIntentSender(), 1001, new Intent(), Integer.valueOf(0), Integer.valueOf(0), Integer.valueOf(0)); 电话呢 它
startinentsenderforresult()
。我想从片段
启动一个购买流(并接收结果)
例如,建议打电话
startIntentSenderForResult(pendingIntent.getIntentSender(),
1001, new Intent(), Integer.valueOf(0), Integer.valueOf(0),
Integer.valueOf(0));
电话呢
它调用startinentsenderforresult()
问题是,调用startinentsenderforresult()
会导致在父活动上调用onActivityResult()
,而不是在调用它的片段上(在IabHelper
所在的位置)
我可以在父级活动中接收onActivityResult()
,然后手动调用片段上的onActivityResult()
,但是有没有办法调用startinentsenderforresult()呢
从一个片段
直接返回结果到该片段的onActivityResult()
?我建议两种解决方案:
1.)将IabHelper mHelper放在活动上,并从片段调用IabHelper
比如:
要使用此解决方案,请在活动中将IABHeloper声明为public,并使用方法从片段调用启动器
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对象的适当片段。适当的片段可以具有对helper对象的访问方法
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);
}
}
你需要打电话
super.onActivityResult(requestCode, resultCode, data);
在活动和片段开始时,onActivityResult将结果级联到片段
在我的零碎活动中,这读作
@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
// No action here, call super to delegate to Fragments
super.onActivityResult(requestCode, resultCode, data);
}
1) 您应该修改resultCode(RC_请求)以将片段索引放入其中
int rc_reqest = RC_REQUEST + ((getActivity().getSupportFragmentManager().getFragments().indexOf(this)+1)<<16) ;
mHelper.launchPurchaseFlow(getActivity(), sku, rc_reqest ,mPurchaseFinishedListener, payload);
到
在我的案例中,我在活动中做了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.");
}
}
如果您有权访问基本活动类,我建议您在基本活动类中创建某种类型的通用处理方法
例如:
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();
}
}
}
公共抽象类BaseActivity扩展活动{
私有列表mResultHandlers
=新的ArrayList();
公共无效注册表ActivityResultHandler(ActivityResultHandler结果Handler){
mResultHandlers.add(resultHandler);
}
@凌驾
受保护的void onActivityResult(int请求代码、int结果代码、意图数据){
super.onActivityResult(请求代码、结果代码、数据);
对于(ActivityResultHandler结果处理程序:mresultHandler){
resultHandler.handle();
}
}
}
当然,您需要通过片段实现ActivityResultHandler接口,并在activity启动时注册它们。关于上面LEO非常有用的第二个解决方案:
如果谷歌曾经用startinentsenderforresult解决了这个问题,现在它正确地将onActivityResult调用路由到片段,那么这个解决方案应该是经过未来验证的,以便
片段的onActivityResult不会被调用两次
我想提出以下由LEO提出的改进方案
在片段的父活动实现中:
@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);
}
}
/**
* 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);
}
}
然后在IAB片段的实现中:
@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);
}
}
/**
* 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);
}
}
RC\u请求
与您用于启动采购流程的请求相同
将此添加到活动的onActivityResult
中。清单侦听器将为您生成所需的结果。(我知道这是一个临时修复,但对我有效)。编辑:android.support.v4.app.Fragment
现在包含一个向后兼容的StartEntSenderForresult()
,所以这个答案已经过时了
旧答案:
从support library 23.2.0开始,修改requestCode
不再有效:FragmentActivity
现在跟踪其片段发出的请求。我将此方法添加到承载片段的FragmentActivity
(代码基于FragmentActivity.startActivityFromFragment(片段、意图、int、Bundle)
):
public void startinentsenderFromfragment(Fragment Fragment,IntentSender intent,int requestCode,@Nullable intent fillinitent,int flagsMask,int flagsvalue,int extraFlags)抛出IntentSender.SendIntentException{
如果(请求代码==-1){
startIntentSenderForResult(意图、请求代码、填充内容、flagsMask、FlagsValue、extraFlags);
返回;
}
如果((请求代码&0xffff0000)!=0){
抛出新的IllegalArgumentException(“只能对requestCode使用较低的16位”);
}
试一试{
方法Method=FragmentActivity.class.getDeclaredMethod(“allocateRequestIndex”,Fragment.class);
方法setAccessible(true);
int requestIndex=(int)method.invoke(这个,片段);
startinentsenderforresult(intent,((requestIndex+1)来自SDK 24和更高版本,支持片段中也提供了一个方法,它可以按预期工作。
请注意,还有一个附加的Bundle参数,可以作为null传递。因此,最终代码为:
startIntentSenderForResult(pendingIntent.getIntentSender(),
1001, new Intent(), Integer.valueOf(0), Integer.valueOf(0),
Integer.valueOf(0), null);
当然,对于API 23及以下版本,我们仍然需要使用其他答案中描述的技巧。如果您想在片段上获得回调,而不是从活动中调用super.onActivityResult()
这将调用您的片段onActivityResult()
不要忘记从片段上下文调用startintentssenderforresult
不要使用活动上下文getActivity()。startIntentSenderForResult
您需要将片段和数据传递给父活动,然后从父活动中的onActivityResult调用片段
像这样
片段:
HomeActivity=(HomeActivity)getActivity();
activity.purchaseLauncher(this,mHelper,productDTO.getSku(),RC_请求,mppurchaseFinishedListener,PAYLOAD);
我目前正在使用第二种方法,ho
if (requestCode == RC_REQUEST)
{
Intent intent = new Intent(ContainerAvtivity.this,ContainerAvtivity.class);
startActivity(intent);
finish();
}
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);
}
}
startIntentSenderForResult(pendingIntent.getIntentSender(),
1001, new Intent(), Integer.valueOf(0), Integer.valueOf(0),
Integer.valueOf(0), null);