如何在Android应用程序中实现应用内计费?

2024-04-18

看来在Android应用程序中实现应用内计费是相当复杂的。我怎么能这样做呢? SDK 中的示例应用程序只有一个 Activity,这对于像我这样具有多个 Activity 的应用程序来说过于简化了。


好吧,我会尝试解释一下我的经历。我不认为自己是这方面的专家,但几天来我伤透了脑筋。

对于初学者来说,我在尝试理解示例和应用程序的工作流程时经历了一段非常糟糕的时光。我认为最好从一个简单的示例开始,但是将代码分成小块并且不知道是否破坏了任何内容非常困难。我将告诉您我拥有什么以及我对示例进行了哪些更改以使其正常工作。

我有一个活动,我的所有购买都来自其中。它被称为专业版。

首先,您应该使用公共市场开发人员密钥更新 Security 类中的变量 base64EncodedPublicKey,否则您将看到一个不错的异常。

好吧,我将 Activity 绑定到 BillingService,如下所示:

      public class Pro extends TrackedActivity implements OnItemClickListener {

            private BillingService mBillingService;
            private BillingPurchaseObserver mBillingPurchaseObserver;
            private Handler mHandler;

            @Override
            protected void onCreate(Bundle savedInstanceState) {    
                super.onCreate(savedInstanceState);     
                setContentView(R.layout.pro);


                //Do my stuff

                mBillingService = new BillingService();
                mBillingService.setContext(getApplicationContext());

                mHandler = new Handler();
                mBillingPurchaseObserver = new BillingPurchaseObserver(mHandler);

            }

        }



    @Override
    protected void onStart() {
       //Register the observer to the service
        super.onStart();
        ResponseHandler.register(mBillingPurchaseObserver);   
    }


    @Override
    protected void onStop() {
        //Unregister the observer since you dont need anymore
        super.onStop();
        ResponseHandler.unregister(mBillingPurchaseObserver);
    }

    @Override
    protected void onDestroy() {
       //Unbind the service
        super.onDestroy();
        mBillingService.unbind();
    }

这样,所有购买都会与该服务进行通信,然后该服务会将 JSON 请求发送到市场。您可能认为购买是在同一时刻进行的,但事实并非如此。您发送请求,购买可能会在几分钟或几小时后完成。我认为这主要是服务器过载和信用卡审批问题。

然后我有一个包含我的商品的 ListView,并在每个商品上打开一个 AlertDialog,邀请他们购买该商品。当他们点击某个项目时,我会这样做:

  private class BuyButton implements DialogInterface.OnClickListener {

       private BillingItem item = null;
       private String developerPayload;

       public BuyButton(BillingItem item, String developerPayload) {
        this.item = item;
        this.developerPayload = developerPayload;
        }

            @Override
            public void onClick(DialogInterface dialog, int which) {

                if (GeneralHelper.isOnline(getApplicationContext())){
                    //I track the buy here with GA SDK. 

        mBillingService.requestPurchase(this.item.getSku(), this.developerPayload);             
                } else {                
                    Toast.makeText(getApplicationContext(), R.string.msg_not_online, Toast.LENGTH_SHORT).show();
                }

            }

        }

好的,您应该看到市场打开并且用户完成或取消购买。

那么重要的是我的 PurChase Observer,它处理市场发送的所有事件。这是它的精简版本,但您应该明白要点(请参阅我在整个代码中的评论):

private class BillingPurchaseObserver extends PurchaseObserver {
        public BillingPurchaseObserver(Handler handler) {
            super(Pro.this, handler);
        }

        @Override
        public void onBillingSupported(boolean supported) {

            if (supported) {
                //Enable buy functions. Not required, but you can do stuff here. The market first checks if billing is supported. Maybe your country is not supported, for example. 
            } else {
                Toast.makeText(getApplicationContext(), R.string.billing_not_supported, Toast.LENGTH_LONG).show();
            }
        }

        @Override
        public void onPurchaseStateChange(PurchaseState purchaseState, String itemId,
                int quantity, long purchaseTime, String developerPayload) {

//This is the method that is called when the buy is completed or refunded I believe. 
// Here you can do something with the developerPayload. Its basically a Tag you can use to follow your transactions. i dont use it. 

        BillingItem item = BillingItem.getBySku(getApplicationContext(), itemId);

        if (purchaseState == PurchaseState.PURCHASED) {
            if (item != null){
//This is my own implementation that sets the item purchased in my database. BillingHelper is a class with methods I use to check if the user bought an option and update the UI. You should also check for refunded. You can see the Consts class to find what you need to check for. 

                    boolean resu = item.makePurchased(getApplicationContext());
                    if (resu){                      
                        Toast.makeText(getApplicationContext(), R.string.billing_item_purchased, Toast.LENGTH_LONG).show();
                    }
                }
            }
        }

        private void trackPurchase(BillingItem item, long purchaseTime) {           
            //My code to track the purchase in GA
        }

        @Override
        public void onRequestPurchaseResponse(RequestPurchase request,
                ResponseCode responseCode) {

               //This is the callback that happens when you sent the request. It doesnt mean you bought something. Just that the Market received it. 

            if (responseCode == ResponseCode.RESULT_OK) {               

                Toast.makeText(getApplicationContext(), R.string.billing_item_request_sent, Toast.LENGTH_SHORT).show();

            } else if (responseCode == ResponseCode.RESULT_USER_CANCELED) {
                //The user canceled the item. 
            } else {
            //If it got here, the Market had an unexpected problem. 
            }
        }

        @Override
        public void onRestoreTransactionsResponse(RestoreTransactions request,
                ResponseCode responseCode) {
            if (responseCode == ResponseCode.RESULT_OK) {
//Restore transactions should only be run once in the lifecycle of your application unless you reinstalled the app or wipe the data. 

                SharedPreferences.Editor edit = PreferencesHelper.getInstance().getDefaultSettings(getApplicationContext()).edit();
                edit.putBoolean(Consts.DB_INITIALIZED, true);
                edit.commit();

            } else {
    //Something went wrong
            }
        }
    }

我相信您不需要编辑其他任何内容。其余代码“有效”。 您可以首先在自己的项目“android.test.purchased”中尝试使用示例 SKU。到目前为止,我已经对此进行了测试并且它有效,但是我仍然需要涵盖诸如退款状态之类的所有内容。在这种情况下,我让用户保留这些功能,但我想在修改它之前确保它完美运行。

我希望它对您和其他人有所帮助。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

如何在Android应用程序中实现应用内计费? 的相关文章

随机推荐

  • Google Translator API 和一个单词的多种翻译

    我正在使用 google api translate java 0 92 jar Translate setHttpReferrer http translate google com http translate google com t
  • LWJGL 窗口具有透明背景?

    我想创建一个没有 黑色背景 区域的窗口 但您可以看到任何其他打开的窗口等 也就是说 渲染场景并且仅渲染场景 不留框架 不留背景区域 我读过一种方法 该方法涉及渲染到隐藏的 OpenGL 窗口并将其缓冲在内存中 创建透明分层窗口以及从内存复制
  • 如何强制 Idea 和 Maven 下载我的项目的所有源代码?

    我的 Java 项目是使用 Maven 构建的 并在 Intellij Idea 的帮助下编写的 我有很多开源项目依赖项 我想广泛研究它们以了解它们是如何工作的 为此 我经常在 Idea 中使用用法搜索 即 查找用法 选项 它告诉我在哪里可
  • 使用 Groovy 在 Java 属性中进行变量扩展

    我经常使用标准 Java 属性文件来配置我的 Groovy 应用程序 我一直缺少的一项功能是能够使用变量作为属性值的一部分 以便它们可以在使用过程中动态扩展 我想我可以使用以下设计提供此功能 使用特殊的格式来注释应该扩展的属性 我选择将此类
  • Zookeeper 错过了连续更改的事件

    我目前有一个带有单个 Zookeeper 节点和 Curator 的设置来访问数据 读取数据是通过 Curator TreeCache 完成的 我有以下测试 public void test callback successive chan
  • GNU 链接器:适应名称修改算法的更改

    我正在尝试重新编译现有的 C 应用程序 不幸的是 我必须依赖一个专有库 我只有一个预编译的静态存档 我使用 g 版本 7 3 0 和 ld 版本 2 30 无论它是用什么 GCC 版本编译的 它都是古老的 头文件定义了方法 class fo
  • 如何将嵌入式 PostgreSQL Server Java 组件作为单独的服务使用?

    我正在尝试为基于 RESTful 服务 Java 的应用程序创建一个全面的集成测试套件 该应用程序在 Tomcat 7 x 中运行并依赖于 Postgresql 9 x 实例 此外 我希望能够将这个套件作为一个独立的进程运行 如果可能的话
  • 如何为 UILabel 指定日语编码?

    当我尝试在 iOS 上的 UILabel 中显示日语字符串时 它会使用中文编码而不是日语显示 这两个编码是nearly除少数特殊情况外 完全相同 例如 以下是字符 直 Unicode U 76F4 在中文 上 与日文 下 中的呈现方式 se
  • 在 Python 中设置系统日期(在 Windows 上)

    似乎有许多软件包用于获取 格式化当前日期 或查找从现在开始的 n 个时间间隔的日期 但我一定忽略了在 Python 中设置日期的简单方法 如 Windows 的 date exe 的存在 这样的功能肯定存在吗 我无法在 Google Pyt
  • 将字符串传递给 settimeout 是不好的做法吗?如果是,为什么? [复制]

    这个问题在这里已经有答案了 可能的重复 是否有充分的理由将字符串传递给 setTimeout https stackoverflow com questions 6081560 is there ever a good reason to
  • 无法获取动态导入的模块:

    我有一些延迟导入的 React 组件App tsx App tsx用于Index tsx它被渲染并附加到的地方body const IndexPage lazy gt import features IndexPage const Tags
  • 在 Playstore 中发布现成的 Kivy 应用程序之前的流程?

    我已经做了buildozer android debug并拥有 apk文件已安装并且工作正常 至少针对一个 两个设备进行了测试 这也是半默认的buildozer spec 例如 域名仍然是org test但随着应用程序的名称进行了调整 有了
  • 如何在可拖动和可放置之间划清界限?

    我正在使用优秀的 JQuery UI 进行 映射 以便用户可以 映射 来自一个计划的人员与来自另一计划的人员 使用这个简单的 JQuery document ready function div draggable draggable re
  • 如何在 iOS 上检测设备的方向?

    我有一个关于如何在 iOS 上检测设备方向的问题 我不需要接收更改通知 只需接收当前方向本身 这似乎是一个相当简单的问题 但我一直无法理解它 以下是我到目前为止所做的 UIDevice myDevice UIDevice currentDe
  • 插件 AppDomains 解决方法

    在处理自己的子目录中的插件程序集时 存在一个众所周知的问题 一旦这些程序集尝试从其子目录加载各自的依赖项 它们将无法加载 解决方案是在 AppDomains 中加载插件PrivateBinPath设置在他们的AppDomainSetup初始
  • 使用当前活动的 Google Drive 集成

    是否可以在不创建自己的 Activity 的情况下与 Google Drive 集成 而只是使用应用程序的当前 Activity 而不用 Google Drive 相关代码污染它 我有一个后台 服务 不是 Android 服务 只是一个与
  • 非 www 到 www htaccess 重定向

    我有一个 net 网址 它与前面的 www 完美配合 但是当网址只是http example net http example net它打开了一个正在建设的页面 我尝试了各种 htaccess 文件 但没有任何效果 帮助 我不是网络开发人员
  • CSS3 在动画元素上旋转导致不调用单击事件

    好吧 这个给我带来了很多问题 使用css3时 webkit transform具有任何类型 3d 旋转的样式 例如rotateY 30deg 给这个旋转的对象绑定点击事件是极其不可靠的 请参阅下面的示例代码或查看这把小提琴 http jsf
  • 序列化迁移执行顺序

    我似乎无法在任何地方找到这个问题的答案 我了解 Sequelize 迁移和播种器的工作原理 但我没有找到任何地方说明它们是否按某种特定顺序执行 因此 如果我从一个数据库开始 进行一系列迁移 然后决定从原始起点初始化一个全新的数据库 它将以完
  • 如何在Android应用程序中实现应用内计费?

    看来在Android应用程序中实现应用内计费是相当复杂的 我怎么能这样做呢 SDK 中的示例应用程序只有一个 Activity 这对于像我这样具有多个 Activity 的应用程序来说过于简化了 好吧 我会尝试解释一下我的经历 我不认为自己