以下是我在 2020 年的工作方式:
- 打开 Android Studio。
- 点击工具 -> SDK 管理器
- 切换到 SDK 工具 标签
- 确保已安装 Google Play 许可库。如果未安装,请单击复选标记并单击应用。
- 在该屏幕上方,您可以看到 Android SDK 位置。复制该路径:
- 点击文件->新建->导入模块...:
- 粘贴您复制的路径,然后单击文本输入行右侧的小文件夹图标:
- 点击Android\Sdk\extras\google\market_licensing\library,然后点击确定:
- 点击下一步:
- 选中所有内容并单击完成:
- 现在您的项目中应该有一个
library 文件夹:
- 右击
app并点击打开模块设置:
- 点击依赖项:
- 点击加号按钮并选择3 Module Dependency:
- 勾选
library并点击OK:
- 再次点击OK,等待同步。
- 如果出现错误
不应在 android 清单文件中声明 minSdk 版本。您可以将版本从清单移动到 build.gradle 文件中的 defaultConfig。
转到 library > manifests > AndroidManifest.xml 并删除 <uses-sdk android:minSdkVersion="3" android:targetSdkVersion="15" /> 行。
- 转到 Gradle 脚本 > build.gradle(模块:库):
- 将
minSdkVersion更改为4,并根据需要更改compileSdkVersion、buildToolsVersion和targetSdkVersion,然后单击Sync Now:
- 现在库已准备就绪,我们需要实际实施许可证检查。转至
MainActivity.kt。
- 您需要找到您的 Base 64 公钥并生成一个盐,如this 答案所示。我将引用该答案的必要部分,但将代码翻译成 Kotlin:
1.1 您的 Base64 唯一应用程序密钥
如何获得:
一个。转到您的开发者控制台。 Link.
b.如果您尚未为您的应用创建应用草稿,请立即进行。
c。创建草稿后,最好上传您的
.apk 作为 Alpha 或 Beta。不要发布它。
d。点击Services & APIs
e。向下滚动找到YOUR LICENSE KEY FOR THIS APPLICATION
f。像这样将密钥复制到您的应用中:
private const val BASE64_PUBLIC_KEY = "YOUR LICENSE KEY FOR THIS APPLICATION";
确保没有空格。
1.2 一种盐
一个。什么是盐?
salt 是随机数据,在散列 a 时作为附加输入
密码。它们用于防御dictionary attacks 和
rainbow table 攻击。
b.如何获得?
This 是生成随机盐的好链接。 应该有
20 个随机整数,所以将20 放入随机字符串的数量为
生成,每个字符串应为2 个字符长(用于此
例如,它不一定是)。检查数字,然后检查
允许使用相同的字符串。它们也可以是负数。尝试
删除任何冗余,例如00 -> 0,为了保持一致。
c。我在哪里放盐?
当声明变量时,只需将这段代码放入,除了你的
随机盐。
private val SALT = byteArrayOf(YOUR RANDOM SALT COMMA SEPARATED 20 INTEGERS)
- 第 21 步中的变量应该添加到您的主要活动类中。现在,您应该在主要活动中添加一些代码。大概是这样的(注意
// TODOcmets):
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)
}
}
- 将这些权限添加到您的清单文件中:
<uses-permission android:name="android.permission.INTERNET"/>
<uses-permission android:name="com.android.vending.CHECK_LICENSE"/>
- 如果您收到类似以下消息的异常:
Service Intent must be explicit: Intent { act=com.android.vending.licensing.ILicensingService }
在this 答案中应用修复。
- 应该就是这样。有关更多信息,请参阅我之前引用的 answer。我希望这可以节省其他人一些时间。