如何为 Android 应用实施 Google Play 许可? [关闭]

2024-04-27

我看到了库存的 Android-Developer 许可库指示 http://developer.android.com/google/play/licensing/index.html,但概述似乎省略了该过程中的几个关键步骤,并且未能完全解释如何让某些东西发挥作用。

有人可以提供一组明确的操作来在 Android 应用程序上设置许可库,以便在允许使用之前进行检查以确保用户已在 Google Play 中为应用程序付费吗?


一段时间以来,我一直致力于在我的应用程序中实施许可,并终于让它发挥作用。我想与大家分享一些我发现对入门有帮助的东西以及我发现的一些问题和解决方案。我在下面链接的 android 开发教程还可以,但对我来说没那么有用,所以我决定制作一个教程。享受吧,希望对您有帮助!

链接到开发者页面here https://developer.android.com/google/play/licensing/index.html.

1. 入门

你需要的东西。

1.1 您的 Base64 唯一应用程序密钥

如何获得:

A。转到您的开发者控制台。Link. https://play.google.com/apps/publish

b.如果您尚未为您的应用创建申请草案,请立即创建。

C。创建草稿后,最好上传您的草稿.apk作为阿尔法或贝塔。让它不发布。

d.点击Services & APIs

e.向下滚动并找到YOUR LICENSE KEY FOR THIS APPLICATION

F。将密钥复制到您的应用程序中,如下所示:

private static final String BASE64_PUBLIC_KEY = "YOUR LICENSE KEY FOR THIS APPLICATION"; 

确保没有空格。

1.2 盐

A。什么是盐?

A salt https://en.wikipedia.org/wiki/Salt_%28cryptography%29是散列密码时附加输入的随机数据。它们被用来防御字典攻击 https://en.wikipedia.org/wiki/Dictionary_attacks and 彩虹桌 https://en.wikipedia.org/wiki/Rainbow_table攻击。

b.我怎样才能得到一个?

This http://www.random.org/strings/是生成随机盐的一个很好的链接。应该有exactly20个随机整数,所以输入20对于要生成的随机字符串的数量,每个字符串应该是2字符长(用于本示例,但不一定如此)。检查数字,并检查是否允许相同的字符串。它们也可以是负数。尝试删除任何冗余,例如00 -> 0,为了保持一致性。

C。我该把盐放在哪里?

声明变量时,只需放入此代码即可,除非使用随机盐。

private static final byte[] SALT = new byte[] {YOUR RANDOM SALT, COMMA SEPARATED, 20 INTEGERS};

2.将LVL(Licensing)库导入到Eclipse中以及你需要的代码

2.1 导入库

a. Open Android SDK Manager

b.去Extras

C。安装Google Play Licensing Library

d.找你的SDK安装路径列在 SDK 管理器的顶部。

e.到达那里后,导航至:<sdk>/extras/google/play_licensing

F。在日食中,单击file then import, then Existing Android Code Into Workspace当它询问您文件路径时,导航到play_licensing文件夹并单击library.

G。一旦项目命名为library已导入,右键单击它,然后点击properties. Click Android在左侧并导航到底部并检查Is Library,然后点击应用。这让 Eclipse 知道您可以将此项目代码用作库。

H。右键单击要添加许可的应用程序,然后单击“属性”,然后单击Android。转到底部并单击library并将其添加到构建路径中。这应该将库导入到Android Dependencies folder.

我。您的项目已设置好进入下一步。

2.2 随你的声明一起声明的变量SALT and KEY

private Handler mHandler;
private LicenseChecker mChecker;
private LicenseCheckerCallback mLicenseCheckerCallback;
boolean licensed;
boolean checkingLicense;
boolean didCheck;

2.3 代码

将此代码粘贴到应用程序底部附近。如果许可证无效,此实现将通知用户并提示他们购买应用程序或退出应用程序。

    private void doCheck() {

        didCheck = false;
        checkingLicense = true;
        setProgressBarIndeterminateVisibility(true);

        mChecker.checkAccess(mLicenseCheckerCallback);
    }


    private class MyLicenseCheckerCallback implements LicenseCheckerCallback {

        @Override
        public void allow(int reason) {
            // TODO Auto-generated method stub
            if (isFinishing()) {
                // Don't update UI if Activity is finishing.
                return;
            }               
            Log.i("License","Accepted!");       

                //You can do other things here, like saving the licensed status to a
                //SharedPreference so the app only has to check the license once.

            licensed = true;
            checkingLicense = false;
            didCheck = true;

        }

        @SuppressWarnings("deprecation")
        @Override
        public void dontAllow(int reason) {
            // TODO Auto-generated method stub
             if (isFinishing()) {
                    // Don't update UI if Activity is finishing.
                    return;
                }
                Log.i("License","Denied!");
                Log.i("License","Reason for denial: "+reason);                                                                              

                        //You can do other things here, like saving the licensed status to a
                        //SharedPreference so the app only has to check the license once.

                licensed = false;
                checkingLicense = false;
                didCheck = true;               

                showDialog(0);

        }

        @SuppressWarnings("deprecation")
        @Override
        public void applicationError(int reason) {
            // TODO Auto-generated method stub
            Log.i("License", "Error: " + reason);
            if (isFinishing()) {
                // Don't update UI if Activity is finishing.
                return;
            }
            licensed = true;
            checkingLicense = false;
            didCheck = false;

            showDialog(0);
        }


    }

    protected Dialog onCreateDialog(int id) {
        // We have only one dialog.
        return new AlertDialog.Builder(this)
                .setTitle("UNLICENSED APPLICATION DIALOG TITLE")
                .setMessage("This application is not licensed, please buy it from the play store.")
                .setPositiveButton("Buy", new DialogInterface.OnClickListener() {
                    public void onClick(DialogInterface dialog, int which) {
                        Intent marketIntent = new Intent(Intent.ACTION_VIEW, Uri.parse(
                                "http://market.android.com/details?id=" + getPackageName()));
                        startActivity(marketIntent);
                        finish();
                    }
                })
                .setNegativeButton("Exit", new DialogInterface.OnClickListener() {
                    public void onClick(DialogInterface dialog, int which) {
                        finish();
                    }
                })
                .setNeutralButton("Re-Check", new DialogInterface.OnClickListener() {
                    public void onClick(DialogInterface dialog, int which) {
                        doCheck();
                    }
                })

                .setCancelable(false)
                .setOnKeyListener(new DialogInterface.OnKeyListener(){
                    public boolean onKey(DialogInterface dialogInterface, int i, KeyEvent keyEvent) {
                        Log.i("License", "Key Listener");
                        finish();
                        return true;
                    }
                })
                .create();

    }

2.4 获取设备ID

过去对此存在一些争论,即是否使用 sim 系列或TelephonyManager.getDeviceId();但通常建议您使用以下代码来获取ANDROID_ID您的设备的最大兼容性。

String deviceId = Secure.getString(getContentResolver(), Secure.ANDROID_ID);
Log.i("Device Id", deviceId);  //AN EXAMPLE OF LOGGING THAT YOU SHOULD BE DOING :)

2.5 创建许可证检查器

A。在您致电之前doCheck();您必须将此代码放入您的应用程序中,以确保正确创建所有内容。

mHandler = new Handler();
mLicenseCheckerCallback = new MyLicenseCheckerCallback();
mChecker = new LicenseChecker(this, new ServerManagedPolicy(this, new   AESObfuscator(SALT, getPackageName(), deviceId)), BASE64_PUBLIC_KEY);

当我实施 LVL 时,我读到如果您遇到许可问题,您可以更改第一个this in the mChecker = new LicenseChecker(this... to getApplicationContext(),我的似乎没有它就可以工作,但以防万一。

2.6 添加权限

A。您需要将两个权限添加到您的应用程序中manifest file.

<uses-permission android:name="android.permission.INTERNET"/>  
<uses-permission android:name="com.android.vending.CHECK_LICENSE"/>        

2.7 确保您有正确的导入!

您可能已经这样做了,但我认为这将是您检查的好地方。

2.8 如何调用待检查的许可证

A。只需致电doCheck();每当您想检查许可证时。例如,如果应用程序首次运行,请进行检查。

3. 在发布之前如何测试许可以确保其有效?

3.1 配置测试设备

A。我有我的个人电话,也用于测试。建议手机上只注册一个 Google 帐户,从历史上看,这会让事情变得更容易一些。您可以通过以下方式查看账户:Settings -> Accounts.

3.2 配置开发者控制台

A。打开您的开发者控制台并转到Settings在左手侧。

b. Find License Testing

C。确保您的电子邮件地址列在下面Gmail accounts with testing access

d.现在,您可以将测试响应更改为您喜欢的任何测试响应。应用程序应该做出相应的响应。请记住,如果您通过 SharedPrefs 保存数据,则每次测试时都需要清除应用程序数据。确保更改测试响应后单击“保存”,否则什么都不会发生!我多次忘记了这一点,最后我得了偏头痛,然后我看到了那个臭烘烘的保存按钮。哈哈。

4. 可以尝试的事情

4.1 有条件许可证检查

A。如果您要保存,可以尝试此代码didCheck数据输入SharedPreferences.

 if(didCheck==false){
        Toast.makeText(this, "Checking application license...",     Toast.LENGTH_SHORT).show();
        doCheck();
        Log.i("Checking!", "Checking license!");
    }   

4.2 加密您的SharedPreferences using SecurePreferences

A。转到这个link https://github.com/sveinungkb/encrypted-userprefs.

b.复制并粘贴代码SecurePreferences.java到您的项目中具有完全相同名称的类。

C。阅读ReadMe.md有关实施此操作的信息。

5. 故障排除

许可问题的解决可能是一件令人头疼的事情,因为有很多事情可能会出错。例如,可能存在网络问题或服务器问题,让您想抓狂。使用正确的日志记录将有助于解决此问题,如果出现问题,您还可以获取服务器响应代码,并且可以将其跟踪到服务器或您的应用程序。我曾多次不得不这样做。

5.1 我无法让我的应用程序从服务器返回任何内容

可能的修复:

A。确保您的应用程序具有正确的KEY.

b.确保您记录了进度的每一步

C。检查您的日志中是否有来自许可服务的任何内容。它对于找出问题所在非常有用。

d.确保allow() and dontAllow() and applicationError() have @Override tags.

5.2 我的应用程序总是说LICENSED or NOT_LICENSED无论我在测试响应中将其设置为什么

A。我对此最好的治疗方法就是等待。似乎如果你在短时间内进行大量测试,它总是会向你发送服务器代码291这是重试代码。我等了一夜,第二天早上一切正常。

b.您可以清除 Google Play 应用和 Google Play 服务应用的数据(不仅仅是缓存)。然后打开播放备份并接受所有许可证并重试。

C。清除您的应用数据。

5.3 用于调试的服务器响应代码列表

您应该得到这些十进制值int reason如果你记录它们。使用此表来参考服务器实际发送到您的应用程序的内容。

LICENSED = Hex: 0x0100, Decimal: 256
NOT_LICENSED = Hex: 0x0231, Decimal: 561
RETRY = Hex: 0x0123, Decimal: 291
LICENSED_OLD_KEY = Hex: 0x2, Decimal: 2
ERROR_NOT_MARKET_MANAGED = Hex: 0x3, Decimal: 3
ERROR_SERVER_FAILURE = Hex: 0x4, Decimal: 4
ERROR_OVER_QUOTA = Hex: 0x5, Decimal: 5
ERROR_CONTACTING_SERVER = Hex: 0x101, Decimal: 257
ERROR_INVALID_PACKAGE_NAME = Hex: 0x102, Decimal: 258 
ERROR_NON_MATCHING_UID = Hex: 0x103, Decimal: 259

5.4 空间更多!他们会来的!

我希望这对你们有帮助!我尽力与大家分享我的头痛问题和解决方法,希望这对您有所帮助!

如果我犯了任何错误,请务必告诉我,以便我尽快修复!

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

如何为 Android 应用实施 Google Play 许可? [关闭] 的相关文章

  • Java - 同步方法导致程序大幅减慢

    我正在尝试了解线程和同步 我做了这个测试程序 public class Test static List
  • 可以混合使用 JVM 语言吗?即:Groovy 和 Clojure

    我知道你可以轻松地混合groovy java clojure java 无论什么JvmLang java 这是否也意味着我也可以让 clojure 和 groovy 代码进行交互 如果我使用 Grails 或 jRoR 我也可以在该环境中使
  • SDK 管理器中缺少 Google Play 服务

    我想在我的应用程序中使用 Google 地图 我想在 SDK 管理器中安装 Google Play 服务 但是当我转到 SDK 管理器时 我没有看到 Google Play 服务 为什么 我该如何安装这个 我可以独立于 SDK Manage
  • org.hibernate.MappingException:没有 JDBC 类型的方言映射:1111

    我使用的是 postgres v8 3 它的列类型为 XML DDL 看起来像这样 CREATE TABLE contact ID INTEGER NOT NULL NAME VARCHAR NOT NULL Details XML 在映射
  • 为什么设置 MediaRecorder 时显示错误 IllegalStateException?

    我的代码设置 MediaRecorder 它显示行集质量错误 mMediaRecorder new MediaRecorder Step 1 Unlock and set camera to MediaRecorder mCamera st
  • Android - 具有可序列化对象的 SharedPreferences

    我知道 SharedPreferences 有putString putFloat putLong putInt and putBoolean 但我需要存储一个类型的对象Serializable in SharedPreferences 我
  • 在 javafx 中注册鼠标处理程序,但处理程序不是内联的

    我有一个 JavaFX 应用程序变得有点大 我想保持代码的可读性 我有一个折线图 我希望内置缩放功能 该功能在单击鼠标时发生 我知道我需要向图表注册鼠标侦听器 我无法从 Oracle 示例中弄清楚什么 即如下所示 http docs ora
  • 在 JSON 对象中强制执行非空字段

    我们的 REST API 接收一些 JSON 对象输入 其中某些字段要求不为空 这些可以是字符串 整数 甚至可以是其他一些类实例作为参考 我们正在尝试找到一种方法来强制这些字段不为空 而不是在 API 中进行空检查的正确方法 当前的 if
  • 上次更新arm64-v8a后,模拟器在M1 Mac上显示离线

    昨天模拟器运行得很好 系统镜像为arm64 v8a 我不太记得工作系统映像的版本名称 但是昨天我接受了 ARM 64 系统映像的更新 版本名称是 S 我可以从 AVD 管理器启动模拟器 当我按 运行应用程序 按钮时模拟器也会启动 但是 该应
  • 当创建 Android Jetpack Compose AndroidView 的参数发生变化时,如何替换它?

    我有一个应用程序 显示封装在其中的几个不同视图AndroidView 在下面重现的简单示例中 这些只是TextView实例 问题是更改文本 在本例中循环显示三个不同的值 似乎不会更新应用程序显示的内容 sealed class AppVie
  • 使用antlr4获取预处理器行并解析C代码

    我正在使用 Antlr4 来解析 C 代码 并使用以下语法来解析 链接到 C g4 https github com antlr grammars v4 blob master c C g4 上面的语法默认不提供任何解析规则来获取预处理器语
  • EclipseLink 2.7.0 和 JPA API 2.2.0 - 签名不匹配

    当运行由maven构建的具有以下依赖项的项目时
  • Android EditText 不起作用,android:imeOptions="actionNext" android:inputType="phone"

    我已经尝试过了 只有删除android inputType phone 键盘回车可以跳转到下一个EditText 不知道大家之间是否有过一些冲突android imeOptions actionNext and android inputT
  • Android - 保持用户登录状态

    我正在尝试使用 PHP 和 MySQLi for Android 进行登录 我不明白的是如何保持用户登录状态 我看到一个简单的教程 其中有人使用 SQLite 来保护信息 但我不知道这是否真的安全 如何保存用户信息以保持用户登录状态 谢谢
  • 线程睡眠阻止我的 Swing 应用程序执行

    我的应用程序发生的事情是有道理的 但我不知道如何修复它 以下是我的应用程序功能的简要描述 计时器窗口应显示在屏幕右下角并显示实时时间 一小时后 它应该执行一些操作 我还没有决定该操作 我面临的问题是定时器 java当我刷新实时计时器的秒数时
  • 为什么 HttpServletRequest 输入流为空?

    我有这段代码 我从请求输入流读取输入并使用 JacksonMapper 转换为 POJO 它在具有 guice 支持的 jetty 7 容器中运行 Override protected void doPost HttpServletRequ
  • Android应用程序中的模式输入

    我想知道是否有其他替代方案可以替代 Android 上平庸的 EditText 密码输入 是否有 API 或开源代码可以集成到我的应用程序中 类似于锁屏图案解锁 Intent 可能会返回哈希值 数字 字符串或代表用户输入的模式的任何内容 我
  • Android进程调度

    我试图更好地理解 以便在创建 Android 应用程序 服务时确定潜在的互操作性问题对可靠性的影响 我想弄清楚进程优先级是如何确定的 服务和活动之间优先级的差异以及调度程序是否以不同方式对待它们的优先级 基本上 我试图深入了解某个活动或服务
  • Android VideoView 中纵向视频方向错误

    我在 Android 设备上以肖像方向拍摄新视频 如下所示 Intent intent new Intent android provider MediaStore ACTION VIDEO CAPTURE startActivityFor
  • 如何检测文本是否可读?

    我想知道是否有一种方法可以告诉给定的文本是人类可读的 我所说的人类可读的意思是 它有一些含义 格式就像某人写的文章 或者至少是由软件翻译器生成的供人类阅读的文章 这是背景故事 最近我正在制作一个应用程序 允许用户将短文本上传到数据库 在部署

随机推荐