Платёжный плагин для Defold
Введение
Монетизация с помощью встроенных покупок распространена в мобильных играх, которые предлагают премиум-подписки, продажу внутриигровых валют, предметов и т. п.
Чтобы проводить платежи через платёжную систему RuStore непосредственно из интерфейсов мобильных приложений без перенаправлений к браузерам для открытия платёжных форм, можно использовать SDK платежей RuStore (SDK RuStore Billing).
В настоящем руководстве SDK платежей и SDK RuStore Billing означают одно и то же.
Настоящее руководство содержит сведения о создании плагина-обёртки над SDK RuStore Billing для интеграции с игровым движком Defold на платформе Android. Вы ознакомитесь с внутренним устройством плагина, а также сможете воспроизвести ключевые шаги по подготовке проекта Defold — эти сведения можно использовать, чтобы создать собственное приложение для публикации в RuStore.
Приведённые в настоящем руководстве действия выполнялись в следующем окружении.
- Операционная система: Windows 11 x64.
- SDK платежей для Defold 5.1.1.
- SDK Android:
- минимальная верси я ― 24;
- целевая версия ― 33.
Особенности реализации
SDK RuStore Billing предоставляет эффективный способ работы с платежами, однако он может быть сложен в использовании в игровых движках из-за того, что написан на языке Kotlin.
Взаимодействие системы скриптинга Defold на языке Lua с нативным SDK предполагает использование JNI (Java Native Interface), что требует громоздкой реализации вызовов и обратных вызовов (callback) JNI на C++.
Здесь мы рассмотрим удобный и гибкий способа интеграции платежных функций RuStore в приложения на игровом движке Defold на платформе Android.
В настоящем руководстве подробно рассматриваются следующие задачи.
- Создание плагина для Defold.
- Подключение нативных Android-библиотек SDK RuStore Billing.
- Создание JNI-обертки над методами SDK и их вызов из Lua.
- Создание и настройка демо-приложение для тестирования всех методов плагина.
Стек технологий
Технология | Краткое описание |
---|---|
Defold | Игровой движок. Для интеграции с внешними библиотеками в настоящей инструкции рассматривается система нативных расширений (плагинов) Defold. Шаги по созданию плагинов описаны в официальной документации Defold, доступна русскоязычная версия документации. |
Lua | Язык для создания скриптов, встроенный в Defold и использующийся для взаимодействия с нативным SDK. Пример использования методов плагина реализуем на Lua – языке скриптинга Defold. |
Kotlin | Для реализации функциональности платежей используется библиотека SDK RuStore Billing для Kotlin/Java. |
C++ | Язык программирования общего назначения. |
JNI | Java Native Interface — интерфейс, позволяющий вызвать нативные функции SDK. Методы Defold для работы с нативом Android описаны на странице Sdk android api documentation официального сайта. Большинство методов SDK используют асинхронные вызовы и лямбда-выражения. В JNI нет прямой поддержки лямбда-выражений, поскольку лямбда-выражения являются частью высокоуровневых конструкций языков программирования, таких как Java или Kotlin, и не имеют прямого эквивалента в нативном коде C++. Однако чтобы реализовать аналогичное поведение можно использовать указатели на функции в блоках extern "C" . В этом случае нам понадобится создать обёртку для методов SDK, которые будут принимать указатель на функцию обратного вызова (callback function) и вызывать её внутри лямбда-выражения. |
Android Studio | Писать обёртки над SDK методами для их вызова через JNI будем на Kotlin в Android Studio. |
Gradle | Удалённая сборка Defold не поддерживает использование локально расположенных пакетов .aar , но файлы .jar работают прекрасно. Для их создания с помощью Gradle реализуем распаковку наших .aar для извлечения .jar . |
RuStore Console | Консоль разработчика RuStore. Для тестирования работы плагина необходимо зарегистрироваться в RuStore Console и создать запись о приложении и тестовых продуктах доступных для покупки. |
Инструменты разработки
Для создания платёжного решения в рамках настоящего руководства необходимы следующие инструменты разработки (см. таблицу ниже).
Инструмент | Описание |
---|---|
Скачайте движок с официального сайта в разделе Download или GitHub. Установка не требуется — для работы нужно будет только запустить исполняемый файл (для Windows это к сведению В настоящем руководстве для примера используется версия Defold 1.8.0. | |
к сведению В настоящем руководстве для примера используется версия Android Studio Koala 2024.1.1. | |
Java Development Kit — набор инструментов разработчика Java. В состав Android Studio включён улучшенный набор инструментов JBR. Если по каким-то причинам при использовании JBR возникнут затруднения, скачайте JDK и установите в любое место на компьютере. Рекомендуется установить по пути по умолчанию. примечание В настоящем руководстве используется JDK версии 11.0.2, набор инструментов установлен по пути | |
У вас должен быть доступ к консоли разработчика RuStore. |
Предварительные действия
Создание файла хранилища ключей
Android требует, чтобы все установленные приложения были подписаны. Все магазины приложений также будут проверять загружаемые приложения на наличие подписи и соответствие его ранее заявленному отпечатку.
Для выполнения подписи понадобится файл хранилища ключей в формате .keystore
.
Для создания файла хранилища ключей в рамках настоящего руководства используется утилита командной строки keytool
из состава Android Studio.
Чтобы создать файл хранилища ключей, выполните следующие дей ствия.
-
Откройте консоль (в Windows с помощью команды
cmd
). -
Перейдите в папку с утилитой
keytool
.Для WindowsЕсли программное обеспечение Android Studio установлено в папку по умолчанию, что утилита
keytool
расположена по следующему пути:c:\Program Files\Android\Android Studio\jbr\bin\keytool.exe
. -
Выполните следующую команду.
keytool -genkeypair -v -keystore key.keystore -keyalg RSA -keysize 2048 -validity 10000 -alias key-alias
-
Вы процессе выполнения команды заполните необходимые поля (см. таблицу ниже).
Поле | Описание |
---|---|
| Введите пароль для защиты файла хранилища ключей. |
| Повторите введённый ранее пароль. подсказка Сохраните пароль в простом файле Внимание! Это небезопасный способ хранения пароля. В настоящем руководстве он используется для примера. При реализации полноценного платёжного решения руководствуйтесь рекомендациями безопасности. |
| Введите имя и фамилию, например: |
| Введите название организационного подразделения, например: |
| Введите название организации, например: |
| Введите название или обозначение города или населённого пункта, например: |
| Введите название или обозначение региона, например: |
| Введите двухбуквенный код страны, например: |
После заполнения полей вам будет предложено проверить свой ввод.
Is CN=John Doe, OU=DEV, O=ORG, L=MSK, ST=MSK, C=RU correct?
[no]:
- Введите
yes
и подтвердите ввод.
В случае успешного создания файла хранилища ключей отобразится следующее сообщение.Generating 2 048 bit RSA key pair and self-signed certificate (SHA256withRSA) with a validity of 10 000 days
for: CN=John Doe, OU=DEV, O=ORG, L=MSK, ST=MSK, C=RU
[Storing key.keystore] - Сохраните созданный файл хранилища ключей
key.keystore
в безопасном месте — он потребуется для настройки сборки Defold-проекта под Android.
Для целей настоящ его руководства будет использоваться путьc:\Defold\keystore\key.keystore
.
Если вы потеряете этот файл, то потеряете возможность обновлять загруженные в магазин приложения.
Вы создали файл хранилища ключей, и у вас есть два сохранённых файла (см. таблицу ниже).
Файл | Описание |
---|---|
key.keystore | Файл ключевого хранилища. В настоящем руководстве для примера используется путьc:\Defold\keystore\key.keystore . |
password.txt | Файл пароля ключевого хранилища и закрытого ключа. В настоящем руководстве для примера используется путьc:\Defold\keystore\password.txt . |
Эти файлы потребуются вам позднее, чтобы настроить сборку Defold-проекта платёжного решения.
Создание эмулятора Android
Создайте виртуальный эмулятор для будущего Defold-приложения со следующими параметрами, после чего переходите к созданию расширений.
Параметр | Значение |
---|---|
Архитектура | x86 |
Версия Android | 11 (максимальная для архитектуры x86) |
OpenGL ES renderer (Extended Controls) | Desktop native OpenGL — для обеспечения наилучшего быстродействия. |
OpenGL ES API level (Extended Controls) | Renderer maximum (up to OpenGL ES 3.1) — для обеспечения наилучшего быстродействия. |
Если вы не знаете, как создать виртуальный эмулятор с указанными параметрами, ниже представлена пошаговая процедура (пропустить).
-
В Android Studio откройте менеджер виртуальных устройств — Virtual Device Manager.
Virtual Device Manager -
В окне Device Manager нажмите значок .
Отобразится окно добавления виртуального устройства.
Окно добавления виртуального устройства -
В списке Category слева выберите Phone.
-
В списке в центральной части окна выберите профиль устройства.
примечаниеВ настоящей документации для примера будет использоваться Medium Phone.
-
Нажмите Next.
Отобразится окно выбора образа системы.
Выбор образа системы -
Выберите нужную систему.
предупреждениеАрхитектура эмулятора должна быть x86.
примечаниеМаксимальная версия Android доступная для x86-эмуляторов — 11.
В настоящей документации для примера используется система с именем релиза Release name: R.
-
Нажмите на значке загрузки напротив выбранной системы.
Отобразится окно загрузки и установки образа системы (SDK Component Installer).
Окно загрузки и установки образа системыПо завершении установки окно примет следующий вид.
Выбор образа системы -
Нажмите Finish.
-
В окне выбора образа системы (System Image) нажмите Next.
Отобразится окно подтверждения конфигурации виртуального устройства.
Выбор образа системы -
Нажмите Finish.
Установленный образ появится в списке Device Manager основного окна Android Studio.
По завершении установки окно примет следующий вид.
Виртуальное устройство установленок сведениюДальнейшая настройка эмулятора устройства Android нужна для обеспечения наилучшего быстродействия.
-
В секции Device Manager Нажмите значок Start напротив установленного виртуального устройства.
Отобразится окно виртуального эмулятора Android.
Виртуальный эмулятор Android -
Нажмите на значок .
Отобразится окно расширенных функций контроля (Extended Controls).
Окно Extended Controls -
В левом меню перейдите в раздел Settings.
-
В разделе Settings выберите вкладку Advanced.
Окно примет следующий вид.
Окно Extended Controls -
Задайте значения полей, руководствуясь таблицей ниже.
Поле Значение OpenGL ES renderer Desktop native OpenGL OpenGL ES API level Renderer maximum (up to OpenGL ES 3.1) -
Закройте окно Extended Controls.
Создание Defold-проекта и настройка сборки под Android
-
Запустите исполняемый файл Defold.
-
Выберите шаблон Mobile game и назовите его billing_example (см. изображение ниже).
Создание проекта Defold -
При необходимости задайте путь к проекту в поле Location или оставьте путь по умолчанию.
к сведениюВ настоящем руководстве для примера будет использоваться следующий путь:
C:\Defold\billing_example
. -
Нажмите Create New Project, чтобы создать новый проект.
- Отобразится окно приветствия Welcome to Defold созданного проекта.
- Также будут созданы папки
.gitignore
и.gitattributes
для хранения проекта в репозиториях Git.
(см. изображение ниже).
Окно приветствия проекта DefoldПроект создан. Следом настройте и создайте сборку под Android (см. далее).
подсказкаПроекты на Defold собираются нелокально — это упрощает настройку проекта для сборки под Android.
-
Откройте для редактирования как формы файл
game.project
, который расположен в корне проекта (см. изображение ниже).
Открыть как форму -
В секции Platforms выберите раздел Android (см. изображение ниже).
Открыть как форму -
В секции настроек Android выполните задайте следующие значения полей (см. таблицу ниже).
Поле Настройка Minimum Sdk Version 24 Target Sdk Version 33 Package Задайте название пакета ru.rustore.billing.defold. Настройки должны выглядеть, как показано на изображении ниже.
Настройка проекта Defold под Android -
В меню инструментов сверху выберите Project > Bundle > Android Application…, как показано на изображении ниже.
Project > Bundle > Android Application…
Отобразится окно Bundle Application.
Bundle Application -
Укажите путь к файлам, необходимым для подписи приложения (см. таблицу ниже).
Поле Описание Keystore Укажите путь к ранее подготовленному файлу хранилища ключей
key.keystore
.Keystore Password Укажите путь к ранее подготовленному файлу пароля
password.txt
.Key Password -
Убедитесь, что в настройке Architectures установлен флажок архитектуры 32-bit (armv7).
примечаниеЧтобы приложение могло быть запущено на архитектурах 32-bit (armv7) и 64-bit (arm64), отметьте оба флажка.
-
В раскрывающемся списке Variant выберите пункт Release.
-
Снимите флажок Generate debug symbols.
Настройки должны выглядеть следующим образом.
Настройки Bundle Application -
Нажмите Create Bundle
-
В отобразившемся окне укажите папку, в которой будет размещена сборка проекта, и подтвердите выбор.
В настоящем руководстве для примера будет использоваться папкаC:\Defold
.
Если все действия были выполнены верно, по указанному пути будет создана папка сборки для Android, в нашем примере:C:\Defold\armv7-android
.
В папку armv7-android
попадают сборки как armv7
, так и arm64
.
Создание расширений
В настоящей инструкции приведены минимально необходимые настройки.
Для работы платёжного плагина необходимо создать два расширения Defold (см. таблицу ниже).
Расширение | Описание |
---|---|
RuStoreCore | Это расширение будет содержать common-код для работы со JNI и некоторыми методами для работы с Android API. Методы этого расширения смогут использовать в том числе другие плагины на основе SDK RuStore. |
RuStoreBilling | Это расширение будет содержать все необходимые настройки, исходный код, библиотеки и ресурсы, связанные с конечным плагином. Конструктор расширений автоматически распознает структуру папок и интегрирует все исходные файлы и библиотеки. |
Каждое из расширений в файловой системе, согласно документации Defold, должно иметь структуру следующего вида.
my_extension/
│
├── ext.manifest
│
├── src/
│
├── include/
│
├── lib/
│ └── [platforms]
│
├── manifests/
│ └── [platforms]
│
└── res/
└── [platforms]
Ниже представлено описание структуры расширения.
Элемент структуры | Обязательно | Описание |
---|---|---|
my_extension/ | Да | Папка расширения в корне проекта (
|
ext.manifest | Да | Файл формата YAML, который подхватывается конструктором расширений. Минимальный файл манифеста должен содержать название расширения. |
src/ | Да | Обязательная папка папка, должна содержать все файлы исходного кода в т.ч. нативный код. |
include/ | Нет | Содержит все включаемые заголовочные файлы (header files) с расширением .h . |
lib/ | Нет | Содержит все скомпилированные библиотеки, от которых зависит расширение. Файлы библиотек должны быть помещены в подпапки с именем платформы — в зависимости от того, какие архитектуры поддерживаются вашими библиотеками. |
manifests/ | Нет | Содержит дополнительн ые файлы, используемые в процессе сборки или комплектации. |
res/ | Нет | Содержит любые дополнительные ресурсы, от которых зависит расширение. Не используется для целей настоящей инструкции. |
Структура расширений Defold
RuStoreCore
- В корне проекта
billing_example
создайте папкуextension_rustore_core
. - Создайте файл
extension_rustore_core/ext.manifest
, который будет содержать только строку с именем расширения.extension_rustore_core/ext.manifestname: RuStoreCore
- Создайте файл
extension_rustore_core/src/rustorecore.cpp
и пока оставьте его пустым. - Создайте папку
extension_rustore_core/include
и оставьте её пока пустой. В расширении RuStoreCore мы будем создавать заголовочные файлы для классов, которые будут использоваться в других расширениях SDK RuStore. - Создайте папку
extension_rustore_core/lib/android
— в дальнейшем в неё будут помещены файлы.jar
. - Создайте файл
extension_rustore_core/manifests/android/build.gradle
Структура расширения RuStoreCore на текущем этапе должна иметь следующий вид.
extension_rustore_core/
│
├── include/
│
├── lib/
│ └── android/
│
├── manifests/
│ └── android
│ └── build.gradle
│
├── src/
│ └── rustorecore.cpp
│
└── ext.manifest
RuStoreBilling
- В корне проекта (
billing_example
) создайте папкуextension_rustore_billing
. - Создайте файл
extension_rustore_billing/ext.manifest
со следующим содержимым, который будет содержать только строчку с именем расширения.extension_rustore_billing/ext.manifestname: RuStoreBilling
- Создайте файл
extension_rustore_billing/src/rustorebilling.cpp
— этот файл будет содержать JNI-вызовы нативных методов.
Сейчас оставьте этот файл пустым — описание его содержимого будет приведено далее. - Создайте папку
extension_rustore_billing/lib/android
— в ней впоследствии нужно будет разместить созданные в дальнейшем файлы.jar
, которые будут содержать обёртку над классами из SDK RuStore billing. Эта обёртка сделает методы SDK пригодными для вызова через JNI. - Создайте файл
extension_rustore_billing/manifests/android/build.gradle
.
Структура расширения RuStoreBilling на текущем этапе должна иметь следующий вид.
extension_rustore_billing/
│
├── lib/
│ └── android/
│
├── manifests/
│ └── android
│ └── build.gradle
│
├── src/
│ └── rustorebilling.cpp
│
└── ext.manifest
Создание точек входа в расширения
Далее займёмся настройкой точек входа в расширения. Для этого Defold SDK использует макрос DM_DECLARE_EXTENSION
.
RuStoreCore
Реализуйте вызов DM_DECLARE_EXTENSION
для расширения RuStoreCore в ранее созданном файле extension_rustore_core/src/rustorecore.cpp
.
#define EXTENSION_NAME RuStoreCore
#define LIB_NAME "RuStoreCore"
#define MODULE_NAME "rustorecore"
#if defined(DM_PLATFORM_ANDROID)
static const luaL_reg Module_methods[] =
{
{0, 0}
};
#else
static const luaL_reg Module_methods[] =
{
{0, 0}
};
#endif
static void LuaInit(lua_State* L)
{
int top = lua_gettop(L);
luaL_register(L, MODULE_NAME, Module_methods);
lua_pop(L, 1);
assert(top == lua_gettop(L));
}
static dmExtension::Result InitializeMyExtension(dmExtension::Params* params)
{
LuaInit(params->m_L);
return dmExtension::RESULT_OK;
}
static dmExtension::Result UpdateMyExtension(dmExtension::Params* params)
{
return dmExtension::RESULT_OK;
}
static dmExtension::Result FinalizeMyExtension(dmExtension::Params* params)
{
return dmExtension::RESULT_OK;
}
DM_DECLARE_EXTENSION(EXTENSION_NAME, LIB_NAME, nullptr, nullptr, InitializeMyExtension, UpdateMyExtension, nullptr, FinalizeMyExtension)
RuStoreBilling
Реализуйте вызов DM_DECLARE_EXTENSION
для расширения RuStoreBilling в ранее созданном файле extension_rustore_billing/src/rustorebilling.cpp
.
#define EXTENSION_NAME RuStoreBilling
#define LIB_NAME "RuStoreBilling"
#define MODULE_NAME "rustorebilling"
#if defined(DM_PLATFORM_ANDROID)
static const luaL_reg Module_methods[] =
{
{0, 0}
};
#else
static const luaL_reg Module_methods[] =
{
{0, 0}
};
#endif
static void LuaInit(lua_State* L)
{
int top = lua_gettop(L);
luaL_register(L, MODULE_NAME, Module_methods);
lua_pop(L, 1);
assert(top == lua_gettop(L));
}
static dmExtension::Result InitializeMyExtension(dmExtension::Params* params)
{
LuaInit(params->m_L);
return dmExtension::RESULT_OK;
}
static dmExtension::Result FinalizeMyExtension(dmExtension::Params* params)
{
return dmExtension::RESULT_OK;
}
DM_DECLARE_EXTENSION(EXTENSION_NAME, LIB_NAME, nullptr, nullptr, InitializeMyExtension, nullptr, nullptr, FinalizeMyExtension)
JNI-вызовы и обратные вызовы (callback)
Для примера рассмотрим создание всплывающего окна простого уведомления (тоста) в рамках создаваемого приложения. Отображение тоста можно реализовать с помощью вызова метода или с помощью обратного вызова (callback).
Создание метода отображения тоста
-
Создайте файл
extension_rustore_core/src/Example.java
со следующим содержимым.extension_rustore_core/src/Example.javapackage ru.rustore.defold.example;
import android.app.Activity;
import android.widget.Toast;
public class Example {
public static void showToast(Activity activity, String message) {
activity.runOnUiThread(() -> Toast.makeText(activity, message, Toast.LENGTH_LONG).show());
}
} -
Для вызова метода
showToast
в файлеextension_rustore_core/src/rustorecore.cpp
создайте метод, реализующий JNI-запросы.примечаниеКод нужно вставить в секцию
#if defined(DM_PLATFORM_ANDROID)
, чтобы код выполнялся только на устройствах Android, и перед массивомModule_methods
.См. пример фаргмента кода До и После ниже.
- До
- После
extension_rustore_core/src/rustorecore.cpp//...
#if defined(DM_PLATFORM_ANDROID)
static const luaL_reg Module_methods[] =
{
{0, 0}
};
#else
//...extension_rustore_core/src/rustorecore.cpp//...
#if defined(DM_PLATFORM_ANDROID)
#include <dmsdk/sdk.h>
#include <dmsdk/dlib/android.h>
static int ShowToast(lua_State* L)
{
DM_LUA_STACK_CHECK(L, 0);
dmAndroid::ThreadAttacher thread;
JNIEnv* env = thread.GetEnv();
const char* msg = (char*)luaL_checkstring(L, 1);
jstring jmsg = env->NewStringUTF(msg);
jclass cls = dmAndroid::LoadClass(env, "ru/rustore/defold/example/Example");
jmethodID method = env->GetStaticMethodID(cls, "showToast", "(Landroid/app/Activity;Ljava/lang/String;)V");
env->CallStaticVoidMethod(cls, method, dmGraphics::GetNativeAndroidActivity(), jmsg);
env->DeleteLocalRef(jmsg);
thread.Detach();
return 0;
}
static const luaL_reg Module_methods[] =
{
{0, 0}
};
#else
//... -
В этом же файле (
extension_rustore_core/src/rustorecore.cpp
) в массивеModule_methods
добавьте псевдоним методаshow_toast
для вызова через Lua (см. пример фрагмента кода до и после).- До
- После
extension_rustore_core/src/rustorecore.cpp//...
static const luaL_reg Module_methods[] =
{
{0, 0}
};
#else
//...extension_rustore_core/src/rustorecore.cpp//...
static const luaL_reg Module_methods[] =
{
{"show_toast", ShowToast},
{0, 0}
};
#else
//...примечаниеСозданным методом можно воспользоваться в Lua через имя модуля, указанное в
MODULE_NAME
при вызовеluaL_register
. -
В файле
extension_rustore_billing/src/rustorebilling.cpp
вставьте строку#include <dmsdk/sdk.h>
перед секцией#if defined(DM_PLATFORM_ANDROID)
(см. пример кода ниже).- До
- После
extension_rustore_billing/src/rustorebilling.cpp#define EXTENSION_NAME RuStoreBilling
#define LIB_NAME "RuStoreBilling"
#define MODULE_NAME "rustorebilling"
#if defined(DM_PLATFORM_ANDROID)
static const luaL_reg Module_methods[] =
{
{0, 0}
};
#else
//...extension_rustore_billing/src/rustorebilling.cpp#define EXTENSION_NAME RuStoreBilling
#define LIB_NAME "RuStoreBilling"
#define MODULE_NAME "rustorebilling"
#include <dmsdk/sdk.h>
#if defined(DM_PLATFORM_ANDROID)
static const luaL_reg Module_methods[] =
{
{0, 0}
};
#else
//... -
Откройте файл
main/main.script
и добавьте в методon_input
вызова на шего метода (см. пример кода ниже).- До
- После
main.scriptfunction init(self)
msg.post(".", "acquire_input_focus")
msg.post("@render:", "use_fixed_fit_projection", { near = -1, far = 1 })
end
function on_input(self, action_id, action)
if action_id == hash("touch") and action.pressed then
print("Touch!")
end
endmain.scriptfunction init(self)
msg.post(".", "acquire_input_focus")
msg.post("@render:", "use_fixed_fit_projection", { near = -1, far = 1 })
end
function on_input(self, action_id, action)
if action_id == hash("touch") and action.pressed then
rustorecore.show_toast("Hello JNI")
end
end -
Выполните сборку:
- Project > Bundle > Android Application > Create Bindle.
ИЛИ - Project > Rebundle (если вы уже делали сборку во время сеанса работы с Defold).
- Project > Bundle > Android Application > Create Bindle.
Готово! Теперь каждый тап по экрану будет запускать тост (см. изображение ниже).
Запуск примера вызовов через JNI
Чтобы проверить работу сборки:
- запустите созданный ранее эмулятор Android;
- перетащите в него APK-файл из папки сборки;
- запустите приложение и в его интерфейсе сэмулируйте тап на экране смартфона.
Обратный вызов (callback)
Обратный вызов (callback) лишь немногим сложнее. Для примера напишем обратный вызов, который срабатывает после вызова ShowToast
и делает запись в Logcat
средствами Defold.
Logcat - инструмент, который позволяет получить доступ к системным сообщениям Android.
- Модифицируйте класс
Example
в файлеextension_rustore_core/src/Example.java
, для этого:- добавьте приватный метод
NativeCallback
с ключевым словомnative
; - используйте метод для получения текущей даты
Date
и вызовNativeCallback
.
После всех доработок Java-код примет следующий в ид.
- До
- После
extension_rustore_core/src/Example.javapackage ru.rustore.defold.example;
import android.app.Activity;
import android.widget.Toast;
public class Example {
public static void showToast(Activity activity, String message) {
activity.runOnUiThread(() -> Toast.makeText(activity, message, Toast.LENGTH_LONG).show());
}
}extension_rustore_core/src/Example.javapackage ru.rustore.defold.example;
import android.app.Activity;
import android.widget.Toast;
import java.util.Date;
public class Example {
private static native void NativeCallback(String value);
public static void showToast(Activity activity, String message) {
activity.runOnUiThread(() -> Toast.makeText(activity, message, Toast.LENGTH_LONG).show());
Date currentDate = new Date();
NativeCallback(currentDate.toString() + ": " + message);
}
} - добавьте приватный метод
- JNI требует использования соглашения о вызовах C для взаимодействия между кодом на Java и кодом на C++ — для этого в файле
extension_rustore_core/src/rustorecore.cpp
объявите секциюextern "C"
и внутри неё напишите функцию согласно соглашениям о вызовах JNI.- До
- После
extension_rustore_core/src/rustorecore.cpp//...
#if defined(DM_PLATFORM_ANDROID)
#include <dmsdk/sdk.h>
#include <dmsdk/dlib/android.h>
static int ShowToast(lua_State* L)
//...extension_rustore_core/src/rustorecore.cpp//...
#if defined(DM_PLATFORM_ANDROID)
#include <dmsdk/sdk.h>
#include <dmsdk/dlib/android.h>
#include <android/log.h>
extern "C"
{
JNIEXPORT void JNICALL Java_ru_rustore_defold_example_Example_NativeCallback(JNIEnv* env, jobject obj, jstring jvalue)
{
const char* value = env>GetStringUTFChars(jvalue, 0);
__android_log_print(ANDROID_LOG_INFO, "Example", value);
env>ReleaseStringUTFChars(jvalue, value);
}
}
static int ShowToast(lua_State* L)
//...
Описание кода представлено в таблице ниже.Элемент кода Описание extern "C" { }
Объявление, которое указывает компилятору C++ на использование соглашения о вызовах C для функции, чтобы функция могла быть вызвана из кода на Java. JNIEXPORT
Макрос, определённый в заголовочных файлах JNI, который обозначает экспорт функции для использования из других библиотек. Этот макрос обеспечивает правильное экспортирование функции для работы с JNI. JNICALL
Спецификатор, который указывает на использование соглашения о вызовах Java для функции. Java_ru_rustore_defold_example_Example_NativeCallback
Сигнатура JNI функции, включающая: id
пакетаru_rustore_defold_example
;- имя класса
Example
; - название метода
NativeCallback
.
JNIEnv* env
Указатель на структуру JNIEnv
, который предоставляет доступ к функциям JNI. Обязательный параметр функции JNI.jobject obj
Объект, который вызывает метод NativeCallback
. Обязательный параметр функции JNI.jstring jvalue
Параметр, передаваемый со стороны Java-кода. GetStringUTFChars
Метод выполняет преобразование объекта jstring
вconst char*
.__android_log_print
Метод из файла android/log.h
, позволяет выводить сообщения вlogcat
.ReleaseStringUTFChars
Этот метод выполняет необходимое удаление объектов строк. - Выполните сборку:
- Project > Bundle > Android Application > Create Bindle.
ИЛИ - Project > Rebundle (если вы уже делали сборку во время сеанса работы с Defold).
Каждый показ тоста теперь сопровождается записью в Logcat вида:
Tue May 14 16:51:16 GMT 2024: Hello JNI
.
- Project > Bundle > Android Application > Create Bindle.
Создание проекта в Android Studio
Весь необходимый нам нативный код можно написать непосредственно в редакторе Defold. Но работать в специальных IDE намного продуктивнее да и просто приятнее. Потому со здадим проект Android и настроим его на генерацию файлов .jar
, понятных Defold.
- Запустите Android Studio и создайте новый проект (кнопка New Project).
Отобразится следующее окно.
Создание нового проекта Android - В разделе Phone and Tablet выберите шаблон No Activity, после чего нажмите Next.
Отобразится окно New Project.
Создание нового проекта Android - Заполните поля, руководствуясь таблицей ниже.
Поле Описание Name Задайте название проекта: extension_libraries. Package name Задайте название пакета: ru.rustore.defold.billing. Save locaiton Задайте путь сохранения проекта. В настоящей инструкции будет использоваться путь C:\Projects\extension_libraries
.Language Выберите язык программирования Kotlin. Minimum SDK Выберите SDK – API 24: Android ("Nougat"; Android 7.0). warningЕсли в вашей версии Android Studio присутствует настройка Build Configuration Language, выберите пункт Groovy DSL (build.gradle).
- Нажмите Finish.
Проект отобразится в окне Android Studio.
Проект extension_libraries создан - для удобства дальнейшей работы на вкладке Project слева вверху переключитесь на отображение структуры Project вместо Android (см. изображение ниже).
Структура проекта выглядит следующим образом.
Выбор формата отображение проекта
Отображение структуры проекта
Переходите к настройке проекта.
Первичная настройка проекта
Чтобы настроить проект, выполните следующие действия.
- В корне проекта (
extension_libraries
) найдите файлsettings.gradle
: - Удалите из файла все данные, кроме строки
rootProject.name = "extension_libraries"
(см. примеры кода До и После).- До
- После
settings.gradlepluginManagement {
repositories {
google {
content {
includeGroupByRegex("com\\.android.*")
includeGroupByRegex("com\\.google.*")
includeGroupByRegex("androidx.*")
}
}
mavenCentral()
gradlePluginPortal()
}
}
dependencyResolutionManagement {
repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
repositories {
google()
mavenCentral()
}
}
rootProject.name = "extension_libraries"
include ':app'settings.gradlerootProject.name = "extension_libraries"
- В корне проекта (
extension_libraries
) найдите файлbuild.gradle
и замените содержимое файла следующей конфигурацией.warningОбратите внимание, что переменная
rustore_example_folder
должна содержать путь до проекта Defold. В нашем примере это
c:\Defold\billing_example\
. Для обратной наклонной черты нужно использовать экранирующий символ (см. пример До и После ниже.)- До
- После
build.gradle// Top-level build file where you can add configuration options common to all sub-projects/modules.
plugins {
alias(libs.plugins.android.application) apply false
alias(libs.plugins.jetbrains.kotlin.android) apply false
}build.gradleext {
sdk_version_code = 1
rustore_example_folder = "c:\\Defold\\billing_example\\"
rustore_sdk_build_variant = 'release'
}
buildscript {
ext.kotlin_version = "1.7.10"
repositories {
google()
mavenCentral()
}
dependencies {
classpath 'com.android.tools.build:gradle:4.1.1'
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
}
}
allprojects {
repositories {
google()
mavenCentral()
maven {
url = uri("https://artifactory-external.vkpartner.ru/artifactory/maven")
}
}
}
task clean(type: Delete) {
delete rootProject.buildDir
} - Справа вверху нажимаем кнопку Sync Now (см. изображение ниже).
Синхронизация проектаподсказкаМогут появиться предупреждения (warning). Здесь и далее, если предупреждения не препятствуют дальнейшей работе, игнорируйте их.
- После обновления проекта удалите папку
app
из контекстного меню (см. изображение ниже).
Удаление папкиapp
- В меню инструментов Android Studio выберите File > Project Structure (см. изображение ниже).
Отображение структуры проекта - В отобразившемся окне Project Structure убедитесь, что в разделе Project выставлены следующие настройки (см. таблицу и изображение ниже)
Перед созданием модулей проверьте, чтоб проект имеет следующие настройки (см. таблицу ниже)
Настройка Описание Android Gradle Plugin Version 4.1.1 Gradle Version 6.7.1
Структура проекта - Выполните одно из следующий действий:
- если настройки соответствуют указанным выше, переходите к созданию модулей.
- если настройка Gradle Version отличается от 6.7.1, выберите эту версию в списке, после чего нажмите OK для подтверждения и продолжите процедуру.
После выбора версии Gradle Version 6.7.1 в Android Studio может отобразиться ошибка синхронизации.
Ошибка синхронизации проектаподсказкаЕсли ошибка не появилась, переходите к созданию модулей. В противном случае продолжите процедуру.
- В панели инструментов Android Studio выберите File > Settings.
Отображение настроек проекта - В открывшемся окне Settings перейдите в раздел Build, Extension, Deployment > Build Tools > Gradle.
Окно настроек проекта - В списке Gradle JDK выберите JDK версии 11 (см. изображение ниже).
Выбор версии JDK 11подсказкаЕсли JDK 11 в списке отсутствует, нажмите под списком Gradle JDK и укажите путь к загруженной ранее версии JDK.
- Нажмите OK, чтобы сохранить изменения.
- Синхронизируйте проект.
Если все действия были выполнены верно, будет выполнена синхронизация.
Синхронизация успешна
Переходите к созданию модулей.
Создание модулей проекта
RuStoreDefoldCore
В Android Studio создайте новый модуль RuStoreDefoldCore. Для этого выполните следующие действия.
- В панели инструментов Android Studio выберите File > New > New Module.
- В открывшемся окне Create New Module в списке Templates выберите шаблон Android Library и выполните настройки, как показано в таблице ниже.
Поле Описание Module name Задайте значение RuStoreDefoldCore. Package name Задайте значение ru.rustore.defold.core. Language Выберите Kotlin. Bytecode Level Выберите 8 (slower build). Minimum SDK Выберите API 24 ("Nougat"; Android 7.0). Настройки должны быть как на изображении ниже.warningЕсли в вашей версии Android Studio присутствует настройка Build Configuration Language, выберите пункт Groovy DSL (build.gradle).
Создание модуля RuStoreDefoldCore - Нажмите Finish.
Модуль RuStoreDefoldCore создан. Переходите к настройке модуля.
build.gradle
Выполните следующие изменения в файле RuStoreDefoldCore/build.gradle
-
в разделе
android
найдите и удалите следующую строкуnamespace 'ru.rustore.defold.core'
.- До
- После
RuStoreDefoldCore/build.gradle//...
android {
namespace 'ru.rustore.defold.core'
compileSdk 34
defaultConfig {
//...RuStoreDefoldCore/build.gradle//...
android {
compileSdk 34
defaultConfig {
//... -
Замените содержимое раздела
dependencies
, подключив следующие зависимости (см. пример До и После).- До
- После
RuStoreDefoldCore/build.gradle//..
dependencies {
implementation libs.appcompat.v7
testImplementation libs.junit
androidTestImplementation libs.runner
androidTestImplementation libs.espresso.core
}RuStoreDefoldCore/build.gradle//..
dependencies {
implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
implementation "com.google.code.gson:gson:2.10.1"
implementation "androidx.fragment:fragment:1.3.0"
} -
В конец файла вставьте следующий код для извлечения и перемещения
.jar
-файла.- До
- После
RuStoreDefoldCore/build.gradle//..
dependencies {
implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
implementation "com.google.code.gson:gson:2.10.1"
implementation "androidx.fragment:fragment:1.3.0"
}RuStoreDefoldCore/build.gradle//..
dependencies {
implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
implementation "com.google.code.gson:gson:2.10.1"
implementation "androidx.fragment:fragment:1.3.0"
}
task createJar(type: Copy) {
def aarFile = file( [buildDir.absolutePath, 'outputs', 'aar', "${project.name}-release.aar"].join(File.separator) )
def outputDir = file( ["$rustore_example_folder", 'extension_rustore_core', 'lib', 'android'].join(File.separator) )
from zipTree(aarFile)
include '**/*.jar'
into outputDir
rename { fileName ->
fileName.replace("classes.jar", "${project.name}.jar")
}
}
afterEvaluate {
assembleRelease.finalizedBy('createJar')
}
AndroidManifest.xml
- В папке
RuStoreDefoldCore/src/main
найдите файлAndroidManifest.xml
. - В тег
<manifest>
добавьте атрибутpackage="ru.rustore.defold.core">
, как показано ниже.- До
- После
RuStoreDefoldCore/src/main<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
</manifest>RuStoreDefoldCore/src/main<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="ru.rustore.defold.core">
</manifest>
RuStoreDefoldBilling
В Android Studio создайте новый модуль RuStoreDefoldBilling. Для этого выполните следующие действия.
- В панели инструментов Android Studio выберите File > New > New Module.
- В открывшемся окне Create New Module в списке Templates выберите шаблон Android Library и выполните настройки, как показано в таблице ниже.
Поле Описание Module name Задайте значение RuStoreDefoldBilling. Package name Задайте значение ru.rustore.defold.billing. Language Выберите Kotlin. Bytecode Level Выберите 8 (slower build). Minimum SDK Выберите API 24 ("Nougat"; Android 7.0). Настройки должны быть как на изображении ниже.warningЕсли в вашей версии Android Studio присутствует настройка Build Configuration Language, выберите пункт Groovy DSL (build.gradle).
Создание модуля RuStoreDefoldBilling - Нажмите Finish.
Модуль RuStoreDefoldBilling создан. Переходите к настройке модуля.
build.gradle
Выполните следующие изменения в файле RuStoreDefoldBilling/build.gradle
.
- в разделе
android
найдите и удалите строкуnamespace 'ru.rustore.defold.billing'
.- До
- После
RuStoreDefoldBilling/build.gradle//...
android {
namespace 'ru.rustore.defold.billing'
compileSdk 34
defaultConfig {
//...