android - Android Studio 中 Android 应用的 Google Play 许可

标签 android android-studio kotlin licensing android-lvl

我正尝试在 Android Studio 中为一个用 Kotlin 编写的应用程序设置 Google Play 许可。我的目标是避免用户在未通过商店购买我的应用程序的情况下共享 APK 文件。

我尝试过的:

  • 我试过跟踪他们的 documentation .这不是很有用。它跳过了许多细节,它并不是真正的教程。我无法使用它。

  • 我看过 this问题,它确实有一个长而详细的类似教程的答案。但答案似乎早已过时。它会导致大量警告并以异常“Intent must be explicit”终止。

我的问题概括起来是:

我如何通过 Google 设置许可证检查,以便没有通过商店购买该应用程序的人无法安装它。这似乎是一件很常见的事情,尽管我找不到很多合适的答案。

最佳答案

以下是我在 2020 年的工作方式:

  1. 打开 Android Studio。

  1. 点击工具 -> SDK 管理器

enter image description here


  1. 切换到SDK 工具标签

enter image description here


  1. 确保安装了 Google Play 许可库。如果未安装,请单击复选标记并单击应用

enter image description here


  1. 在该屏幕上,您可以看到 Android SDK Location。复制该路径:

enter image description here


  1. 点击文件 -> 新建 -> 导入模块...:

enter image description here


  1. 粘贴您复制的路径,然后单击文本输入行右侧的小文件夹图标:

enter image description here


  1. 点击Android\Sdk\extras\google\market_licensing\library,然后点击确定:

enter image description here


  1. 点击下一步:

enter image description here


  1. 选中所有内容并单击完成:

enter image description here


  1. 现在你应该有一个 library项目中的文件夹:

enter image description here


  1. 右键单击 app然后点击打开模块设置:

enter image description here


  1. 点击依赖项:

enter image description here


  1. 点击加号按钮并选择3 Module Dependency:

enter image description here


  1. 检查library然后点击确定:

enter image description here


  1. 再次单击确定 并等待同步。

  1. 如果遇到错误

The minSdk version should not be declared in the android manifest file. You can move the version from the manifest to the defaultConfig in the build.gradle file.

转到 library > manifests > AndroidManifest.xml 并删除行 <uses-sdk android:minSdkVersion="3" android:targetSdkVersion="15" /> .


  1. 转到 Gradle Scripts > build.gradle(模块:库):

enter image description here


  1. 更改 minSdkVersion到 4 并更改 compileSdkVersion , buildToolsVersiontargetSdkVersion根据需要,然后单击 Sync Now :

enter image description here


  1. 现在库已准备就绪,我们需要实际执行许可证检查。转到 MainActivity.kt .

  1. 您需要找到您的 Base 64 公钥并生成盐,如 this 所示。回答。我将引用该答案的必要部分,但将代码翻译成 Kotlin:

1.1 Your Base64 unique application key

How to get it:

a. Go to your developer console. Link.

b. If you haven't already created an application draft for your app, do it now.

c. Once you have created the draft, it is a good idea to upload your .apk as Alpha or Beta. Leave it unpublished.

d. Click Services & APIs

e. Scroll down and find YOUR LICENSE KEY FOR THIS APPLICATION

f. Copy the key into your app like this:

private const val BASE64_PUBLIC_KEY = "YOUR LICENSE KEY FOR THIS APPLICATION";

Make sure that there are no spaces.

1.2 A salt

a. What is a salt?

A salt is random data that is additional input when hashing a password. They are used to defend against dictionary attacks and rainbow table attacks.

b. How do I get one?

This is a good link to generate a random salt. There should be exactly 20 random integers, so put 20 in for the amount of random strings to generate, each string should be 2 characters long (used for this example, it doesn't have to be). Check numeric digits, and check Identical strings are allowed. They can be negative numbers too. Try to remove any redundancy, e.g. 00 -> 0, for the sake of consistency.

c. Where do I put the salt?

When declaring variables just put this code in, except with your random salt.

private val SALT = byteArrayOf(YOUR RANDOM SALT COMMA SEPARATED 20 INTEGERS)

  1. 第 21 步中的变量应该添加到您的主要 Activity 类中。现在,您应该向主要 Activity 添加一些代码。它应该大致如下(注意 // TODO 评论):
import android.os.Bundle
import android.provider.Settings
import android.widget.Toast
import androidx.appcompat.app.AppCompatActivity
import com.google.android.vending.licensing.*
import kotlin.system.exitProcess

class MainActivity : AppCompatActivity()
{
    companion object
    {
        private const val BASE64_PUBLIC_KEY = "YOUR LICENSE KEY FOR THIS APPLICATION" // TODO replace with your own key

        private val SALT = byteArrayOf(YOUR RANDOM SALT COMMA SEPARATED 20 INTEGERS) // TODO replace with your own salt
        
    }
    
    private val deviceId: String by lazy {
        Settings.Secure.getString(getContentResolver(), Settings.Secure.ANDROID_ID);
    }
    private lateinit var licenseCheckerCallback: LicenseCheckerCallback
    private lateinit var checker: LicenseChecker
    
    private fun doCheck()
    {
        checker.checkAccess(licenseCheckerCallback)
    }

    override fun onDestroy()
    {
        super.onDestroy()
        checker.onDestroy()
    }



    override fun onCreate(savedInstanceState: Bundle?)
    {
        super.onCreate(savedInstanceState)

        // Construct the LicenseCheckerCallback. The library calls this when done.
        licenseCheckerCallback = MyLicenseCheckerCallback()

        // Construct the LicenseChecker with a Policy.
        checker = LicenseChecker(
            this,
            ServerManagedPolicy(this, AESObfuscator(SALT, packageName, deviceId)),
            BASE64_PUBLIC_KEY // Your public licensing key.
        )

        doCheck()

        setContentView(R.layout.activity_main) // TODO Replace with your own layout
    }

    private fun displayResult(result: String)
    {
         // TODO you can change this how the info is displayed
        Toast.makeText(this, result, Toast.LENGTH_SHORT).show()
    }

    private inner class MyLicenseCheckerCallback : LicenseCheckerCallback
    {
        override fun allow(reason: Int)
        {
            if (isFinishing)
            {
                // Don't update UI if Activity is finishing.
                return
            }
            // Should allow user access.
        }

        override fun applicationError(errorCode: Int)
        {
             // TODO handle the error your own way. Calling `dontAllow` is common.
            dontAllow(Policy.NOT_LICENSED)
        }

        override fun dontAllow(reason: Int)
        {
            if (isFinishing)
            {
                // Don't update UI if Activity is finishing.
                return
            }
            

            if (reason == Policy.RETRY)
            {
                // If the reason received from the policy is RETRY, it was probably
                // due to a loss of connection with the service, so we should give the
                // user a chance to retry. So show a dialog to retry.

                // TODO handle Policy.RETRY
            }
            else
            {
                // Otherwise, the user isn't licensed to use this app.
                // Your response should always inform the user that the application
                // isn't licensed, but your behavior at that point can vary. You might
                // provide the user a limited access version of your app or you can
                // take them to Google Play to purchase the app.

                // TODO implement goto market
            }
            displayResult("Not Licensed")
            
            // TODO you may not abort if you have some other way to handle the fail case
            abort()
        }
    }

    private fun abort()
    {
        finishAffinity()
        exitProcess(0) 
    }
}

  1. 将这些权限添加到您的 list 文件中:
<uses-permission android:name="android.permission.INTERNET"/>  
<uses-permission android:name="com.android.vending.CHECK_LICENSE"/>

  1. 如果您收到类似以下消息的异常:
Service Intent must be explicit: Intent { act=com.android.vending.licensing.ILicensingService }

应用 this 中的修复程序回答。


  1. 应该就这些了。查看answer我之前引用了更多信息。我希望这能为其他人节省一些时间。

关于android - Android Studio 中 Android 应用的 Google Play 许可,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/62852617/

相关文章:

flutter - 未配置 Dart SDK

android - 重量在哪里(在喷气背包中)?

java - 在 android 中将 contentView 设置为 Web View 后返回到 Activity contentView

android - Android 的 FM 信号强度

android - Android MediaPlayer暂停流

multithreading - 带参数的 Kotlin 线程安全 native 惰性单例

spring-boot - 未提供连接字符串(Azure Application Insights)

android - 当 EditText 获得焦点时,CollapsingToolbarLayout 不会折叠

android - 布局之外的边框

android - 如何在设备启动时使用 AlarmManager 启动警报?