46

我看到了库存的 Android-Developer 许可库说明,但概述似乎省略了该过程中的几个关键步骤,并且未能完全解释如何使某些东西正常工作。

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

4

1 回答 1

178

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

链接到这里的开发者页面。

1. 入门

你需要的东西。

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

如何获得它:

一个。转到您的开发者控制台。关联。

湾。如果您尚未为您的应用创建应用草稿,请立即进行。

C。创建草稿后,最好将您的草稿上传.apk为 Alpha 或 Beta。不发布。

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 一种盐

一个。什么是盐?

是在散列密码时附加输入的随机数据。它们用于防御字典攻击彩虹表攻击。

湾。我如何得到一个?

是生成随机盐的好链接。应该正好有20 个随机整数,所以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 导入库

一个。打开Android SDK Manager

湾。去Extras

C。安装Google Play Licensing Library

d。找到SDKSDK 管理器顶部列出的安装路径。

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

F。在 Eclipse 中,单击filethen import,然后Existing Android Code Into Workspace当它询问您文件路径时,导航到play_licensing文件夹并单击library.

G。导入名为的项目library后,右键单击它,然后单击properties. 单击Android左侧并导航到底部并选中Is Library,然后单击应用。这让 eclipse 知道您可以将此项目代码用作库。

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

一世。您的项目已设置为进入下一步。

SALT2.2 与你的和一起声明的变量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 创建许可证检查器

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

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

当我在执行 LVL 时,我读到如果您在许可方面遇到问题,您可以更改tothis中的第一个,我的似乎没有它也可以工作,但以防万一。mChecker = new LicenseChecker(this...getApplicationContext()

2.6 添加权限

一个。您需要将两个权限添加到您的应用程序manifest文件中。

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

2.7 确保你有正确的导入!

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

2.8 如何调用被检查的license

一个。doCheck();只要您想检查许可证,只需致电。例如,如果应用程序是第一次运行,请进行检查。

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

3.1 配置测试设备

一个。我有我的个人电话,也用于测试。建议手机上只注册一个谷歌账号,历史上这样会方便一些。您可以通过转到 来检查帐户Settings -> Accounts

3.2 配置开发者控制台

一个。打开您的开发者控制台并转到Settings左侧。

湾。寻找License Testing

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

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

4.要尝试的事情

4.1 有条件的许可证检查

一个。如果您将didCheck数据保存在SharedPreferences.

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

4.2 加密您的SharedPreferences使用SecurePreferences

一个。转到此链接

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

C。阅读ReadMe.md有关实现此功能的信息。

5. 故障排除

许可可能是一个令人头疼的故障排除,仅仅是因为还有更多可能出错的事情。例如,可能有网络问题或服务器问题让你想扯掉你的头发。使用正确的日志记录将对此有所帮助,如果出现问题,您还可以获取服务器响应代码,并且可以将其跟踪到服务器或您的应用程序。我不得不多次这样做。

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

可能的修复:

一个。确保您的应用具有正确的KEY.

湾。确保您正在记录进度的每一步

C。检查您的日志以获取许可服务中的任何内容。它对于找出哪里出了问题很有用。

d。确保allow()并且dontAllow()applicationError()标签@Override

5.2 我的应用程序总是说LICENSED或者NOT_LICENSED不管我在测试响应中设置什么

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

湾。您可以清除 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 更多空间!他们会来的!

我希望这对你们有帮助!我试图尽我所能与你们分享我的头痛和解决方法,我希望这会有所帮助!

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

于 2013-08-20T01:45:22.937 回答