- PVSM.RU - https://www.pvsm.ru -
Эта статья является продолжением предыдущей статьи [1], которая была написана в формате сделай за 5 минут. Эта статья является первой ступенькой для того, чтоб разобраться в вопросе, так как информации в русском сегменте интернета об IABv3 почти нет, а что есть, то передается из уст в уста.
Речь пойдет о последней библиотеке для приема платежей In-app billing version 3 от GooglePlay, на которую начался переход с начала декабря 2012 года.
Магазин GooglePlay позволяет нам создавать и продавать 3 типа продуктов:
Библиотека IABv3 считает все эти продукты Managed (управляемые), то есть при запросе у магазина нашим приложением списка купленных продуктов, мы получим список всех купленных наименований, но при этом товары типа Не контролируется Google могут быть израсходованы, при этом появится возможность повторной их покупки.
И так, в прошлой статье [1] мы рассмотрели как установить примеры использования биллинга от Google.
Откроем пример TrivialDrive, который расположен «Путь к расположению нашей SDKextrasgoogleplay_billingin-app-billing-v03samplesTrivialDrive» и рассмотрим его более подробно.
Внутри мы видим три пакета:
Для того, чтоб приложение могло взаимодействовать с биллингом магазина в AndroidManifest.xml добавляется следующее разрешение:
<uses-permission android:name="com.android.vending.BILLING" />
Это разрешение позволяет нам подключаться к Сервису биллинга приложения «Play Маркет».
Далее мы подробно разберем что происходит в самом приложении MainActivity.java, что в com.example.android.trivialdrivesample.
Определяются следующие переменные и константы для работы приложения:
// Метка для ведения логов
static final String TAG = "TrivialDrive";
// Переменная хранящая состояние активности премиум опции
boolean mIsPremium = false;
// Индефикаторы двух продуктов: premium (контролируется Google) и gas (не онтролируется Google)
static final String SKU_PREMIUM = "premium";
static final String SKU_GAS = "gas";
// (произвольный) код для возвращения в приложение данных при покупке из приложения "Play Маркет"
static final int RC_REQUEST = 10001;
// Массив графики для отображения состояния наполнения бака танка
static int[] TANK_RES_IDS = { R.drawable.gas0, R.drawable.gas1, R.drawable.gas2,
R.drawable.gas3, R.drawable.gas4 };
// Константа определяющая сколько SKU_GAS заполняет полностью бак танка.
static final int TANK_MAX = 4;
// Текущая наполненность бака танка
int mTank;
// Объект Хелпера для взаимодействия с биллингом.
IabHelper mHelper;
Переходим к методу onCreate:
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// Читаем сохраненные настройки приложения
loadData();
// В переменной base64EncodedPublicKey должен быть указан открытый ключ RSA приложения вместо
// CONSTRUCT_YOUR_KEY_AND_PLACE_IT_HERE
String base64EncodedPublicKey = "CONSTRUCT_YOUR_KEY_AND_PLACE_IT_HERE";
// Защита от дурака, проверяет, действительно ли был указан ключ RSA
// и было ли переименовано приложение из стандартного
if (base64EncodedPublicKey.contains("CONSTRUCT_YOUR")) {
throw new RuntimeException("Please put your app's public key in MainActivity.java. See README.");
}
if (getPackageName().startsWith("com.example")) {
throw new RuntimeException("Please change the sample's package name! See README.");
}
// Создается новая копия объекта хелпера для взаимодействия с биллингом
Log.d(TAG, "Creating IAB helper.");
mHelper = new IabHelper(this, base64EncodedPublicKey);
// Включается режим логирования в хелпере
mHelper.enableDebugLogging(true);
// Начинается настройка хелпера. Это ассинхронная процедура со своими слушателями,
// которые будут вызванны по завешении получения данных.
Log.d(TAG, "Starting setup.");
mHelper.startSetup(new IabHelper.OnIabSetupFinishedListener() {
public void onIabSetupFinished(IabResult result) {
Log.d(TAG, "Setup finished.");
if (!result.isSuccess()) {
// Произошла ошибка, сообщаем о ней пользователю и выходим из метода
complain("Problem setting up in-app billing: " + result);
return;
}
// Хелпер полностью инициализирован, получаем
Log.d(TAG, "Setup successful. Querying inventory.");
mHelper.queryInventoryAsync(mGotInventoryListener);
}
});
}
На строке «mHelper.queryInventoryAsync(mGotInventoryListener);» начинается самое интересное, после инициализации Хелпера мы в асинхронном режиме получаем от магазина список купленных пользователем продуктов. mGotInventoryListener является методом обратного вызова, по завершении получения данных от магазина. Посмотрим же, что происходит в нем:
// Вызывается по завершении выполнения запроса купленных продуктов в магазине.
// Создаем новый экземпляр класса IabHelper.QueryInventoryFinishedListener и определяем в нем
// метод onQueryInventoryFinished
IabHelper.QueryInventoryFinishedListener mGotInventoryListener = new IabHelper.QueryInventoryFinishedListener() {
public void onQueryInventoryFinished(IabResult result, Inventory inventory) {
Log.d(TAG, "Query inventory finished.");
if (result.isFailure()) {
// Сообщаем пользователю о том, что не удалось получить список купленных продуктов в магазине
complain("Failed to query inventory: " + result);
// Выходим из метода
return;
}
// Удачно получили список купленных товаров в магазине, начинаем разбор
Log.d(TAG, "Query inventory was successful.");
// Проверяем есть ли у пользователя активированный режим премиум
mIsPremium = inventory.hasPurchase(SKU_PREMIUM);
Log.d(TAG, "User is " + (mIsPremium ? "PREMIUM" : "NOT PREMIUM"));
// проверяем, есть ли купленное топливо, если есть, то сразу заправляем его в бак
// (расходуем, чтоб пользователь мог еще раз этот товар)
if (inventory.hasPurchase(SKU_GAS)) {
Log.d(TAG, "We have gas. Consuming it.");
// Сообщаем магазину в асинхронном режиме, что товар израсходован.
mHelper.consumeAsync(inventory.getPurchase(SKU_GAS), mConsumeFinishedListener);
// выходим из метода
return;
}
// Если нет товаров, которые необходимо израсходовать, то обновляем интефейс программы.
updateUi();
// Отключаем заставку ожидания данных в программе.
setWaitScreen(false);
Log.d(TAG, "Initial inventory query finished; enabling main UI.");
}
};
И так, тут мы столкнулись с двумя типами продуктов:
Если при наличии первого мы просто делаем отметку в boolean переменной mIsPremium, то при наличии второго, мы запускаем метод расходования продукта «mHelper.consumeAsync(inventory.getPurchase(SKU_GAS), mConsumeFinishedListener);», чтоб пользователь мог купить его ещё раз. mConsumeFinishedListener является методом обратного вызова, который вызывается, когда магазин подтверждает расходование продукта. Посмотрим же, что происходит в нем:
// Вызывается при завершении процедуры подтверждения расходования продукта магазином
// Создаем новый экземпляр класса IabHelper.OnConsumeFinishedListener и определяем в нем
// метод onConsumeFinished
IabHelper.OnConsumeFinishedListener mConsumeFinishedListener = new IabHelper.OnConsumeFinishedListener() {
public void onConsumeFinished(Purchase purchase, IabResult result) {
Log.d(TAG, "Consumption finished. Purchase: " + purchase + ", result: " + result);
// Мы знаем, что "gas" это единственный продукт потребляемый в нашем приложении,
// но если в вашем приложении более одного потребляемого продукта,
// то имеет смысл тут установить проверку на идентификатор продукта.
if (result.isSuccess()) {
// Потребление продукта прошло успешно.
// Активируем логику приложения и заправим наш танк.
Log.d(TAG, "Consumption successful. Provisioning.");
mTank = mTank == TANK_MAX ? TANK_MAX : mTank + 1;
// Сохраняем данные о танке.
saveData();
// Сообщаем пользователю, на сколько заполнен бак его танка
alert("You filled 1/4 tank. Your tank is now " + String.valueOf(mTank) + "/4 full!");
} else {
// Иначе сообщаем пользователю об ошибке
complain("Error while consuming: " + result);
}
// Обновляем графический интерфейс приложения
updateUi();
// Отключаем заставку ожидания данных в программе.
setWaitScreen(false);
Log.d(TAG, "End consumption flow.");
}
};
Теперь разберем два метода, которые обрабатывают нажатие на кнопки в приложении.
Метод onBuyGasButtonClicked:
// Пользователь нажимает на кнопку "Buy Gas"
public void onBuyGasButtonClicked(View arg0) {
Log.d(TAG, "Buy gas button clicked.");
// Проверяем не поный ли бак у нашего танка
if (mTank >= TANK_MAX) {
// если бак полный, то сообщаем об этом пользователю
complain("Your tank is full. Drive around a bit!");
// и выходим из метода
return;
}
// Если же в бак можно еще залить бензина, то включаем заставку получения данных приложением
setWaitScreen(true);
Log.d(TAG, "Launching purchase flow for gas.");
// И через хелпер связываемся с магазином для покупки продукта
mHelper.launchPurchaseFlow(this, SKU_GAS, RC_REQUEST, mPurchaseFinishedListener);
}
Метод onUpgradeAppButtonClicked:
// Пользователь нажимает на кнопку "Upgrade to Premium".
public void onUpgradeAppButtonClicked(View arg0) {
Log.d(TAG, "Upgrade button clicked; launching purchase flow for upgrade.");
// Включаем заставку получения данных приложением
setWaitScreen(true);
// И через хелпер связываемся с магазином для покупки продукта
mHelper.launchPurchaseFlow(this, SKU_PREMIUM, RC_REQUEST, mPurchaseFinishedListener);
}
* Тут многие могут заметить, что в методе нет проверки переменной mIsPremium на её состояние, но могу успокоить, автор кода эту проверку осуществляет в методе updateUi().
Оба метода привязываются к кнопка непосредственно в xml описании активити по средствам указания у элементов кнопок параметра android:onClick.
android:onClick="onUpgradeAppButtonClicked"
Так же многие могли заметить, что при вызове метода покупки магазина они используют метод обратного вызова mPurchaseFinishedListener, посмотри, что происходит в нем:
// Вызывается при завершении процедуры покупки продукта в магазине
// Создаем новый экземпляр класса IabHelper.OnIabPurchaseFinishedListener и определяем в нем
// метод onIabPurchaseFinished
IabHelper.OnIabPurchaseFinishedListener mPurchaseFinishedListener = new IabHelper.OnIabPurchaseFinishedListener() {
public void onIabPurchaseFinished(IabResult result, Purchase purchase) {
Log.d(TAG, "Purchase finished: " + result + ", purchase: " + purchase);
if (result.isFailure()) {
// Произошла ошибка, сообщаем об этом пользователю
complain("Error purchasing: " + result);
setWaitScreen(false);
// Выходим из метода
return;
}
Log.d(TAG, "Purchase successful.");
// Проверяем, что же именно купил пользователь
if (purchase.getSku().equals(SKU_GAS)) {
Log.d(TAG, "Purchase is gas. Starting gas consumption.");
// Ели это оказалось топливо, то запускаем метод потребления продукта
mHelper.consumeAsync(purchase, mConsumeFinishedListener);
}
else if (purchase.getSku().equals(SKU_PREMIUM)) {
Log.d(TAG, "Purchase is premium upgrade. Congratulating user.");
// Если это оказалась опция премиум, то благодарим пользователя.
alert("Thank you for upgrading to premium!");
// И делаем соответствующие настройки в приложении
mIsPremium = true;
updateUi();
setWaitScreen(false);
}
}
};
Мы рассмотрели основные методы приложения, которые отвечают за работу с покупками внутри приложения, но остались ещё два метода, которые обязательно нужно описать, это onActivityResult и onDestroy.
Метод onActivityResult:
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
Log.d(TAG, "onActivityResult(" + requestCode + "," + resultCode + "," + data);
// Отдаем переменные в хелпер
if (!mHelper.handleActivityResult(requestCode, resultCode, data)) {
// если переменные не для хелпера, то передаем их дальше
super.onActivityResult(requestCode, resultCode, data);
}
else {
Log.d(TAG, "onActivityResult handled by IABUtil.");
}
}
Этот метод позволяет приложению «Play Маркета» передавать в ваше приложение данные о работе с продуктами. То есть возвращать результат ваших запросов к магазину.
Метод onDestroy:
@Override
public void onDestroy() {
Log.d(TAG, "Destroying helper.");
if (mHelper != null) mHelper.dispose();
mHelper = null;
}
В этом методе мы разрушаем связь Хелпера с сервисом приложения «Play Маркет».
Остальные методы приложения являются реализацией логики самой мини игры и они настолько просты, что не имеет смысла их рассматривать в рамках этой статьи.
В этой статье, мы разобрали по косточкам действующий пример Google по работе с платежами внутри программы, как применить эти знания в своем приложении, можно ознакомиться в предыдущей статье [1].
Автор: neoksi
Источник [2]
Сайт-источник PVSM.RU: https://www.pvsm.ru
Путь до страницы источника: https://www.pvsm.ru/android/24377
Ссылки в тексте:
[1] предыдущей статьи: http://habrahabr.ru/post/165065/
[2] Источник: http://habrahabr.ru/post/165183/
Нажмите здесь для печати.