- PVSM.RU - https://www.pvsm.ru -
Всем привет!
Мы — разработчики (гордо звучит, не правда ли?), и мы активно пилим новые фичи, правим баги и стараемся сделать наш продукт лучше. Но чтобы понять, а как именно пользователь использует наш продукт, какие фишки продукта ему по душе, а какие — не очень, мы используем аналитику. Есть много разных средств, но в этой статье я бы хотел поговорить именно об аналитике от Google, которая активно развивается и меняется. Старого часового по имени Google Analytics сменяет новый боец — Google Analytics for Firebase (в девичестве — Firebase Analytics).
Уже даже в названиях вы можете уловить этот ветер перемен. А ветер перемен всегда порождает некоторый информационный вакуум, в который попадают разного рода слухи, далеко не всегда достоверные при этом.
Поэтому давайте попробуем разобраться подробно, а что сейчас с этой аналитикой, чем пользоваться-то в итоге. И как вообще дальше жить.
Если про Google Analytics информации довольно много, и она систематизирована (чего только стоит этот ресурс [1], идеальная справка), то у Google Analytics for Firebase типичная болезнь молодого и активно развивающегося продукта — информации мало, она разрознена и иногда даже противоречива. И я в свое время потратил немало сил и времени, чтобы разобраться, что к чему.
Собственно главная цель данной статьи — это систематизация знаний и нынешнего состояния Google Analytics for Firebase. Некоторая «дорожная карта» Google Analytics for Firebase.
Уверен, данная «карта» сэкономит вам прилично времени и нервов =)
Начну все-таки с самого горячего.
Мне кажется, что данный слух идет с самого появления Firebase Analytics. И с одной стороны, это логично, зачем «Гуглу» два средства аналитики. Но Google Analytics (будем именовать GA) и Google Analytics for Firebase (по старинке назовем FA) — это две аналитики с разными концепциями и подходами, про которые мы поговорим чуть ниже.
GA никуда не денется и не пропадет (по крайней мере сейчас), а также не будет кем-то поглощен. Это как информация от представителей москвовского офиса Гугла, так и инсайды от самих разработчиков.
Фанаты GA могут спать спокойно… пока что. Но кто знает, что будет дальше. Поэтому я настоятельно рекомендую продолжить чтение =)
FA — это аналитика с совершенно другой концепцией и философией. Она является event based и предназначена исключительно для мобилки. Тогда как GA — screen-based и сначала была для веба, а уж потом ее допилили для мобилки.
GA структурирована вокруг иерархичных событий с одним значением, FA — больше о записи одного события с большим количеством параметров (пар «ключ-значение»).
Эти аналитики очень разные. И поэтому они не могут быть взаимозаменяемыми.
Миграции с одной на другую не предусматривается. Но «Гугл» работает над определенной совместимостью этих аналитик, о чем мы тоже поговорим чуть позже.
Коль уж мы затронули тему событий. В плане осмысления «события» GA и FA действительно очень разные. И это особенно заметно на примере.
Допустим, ваше приложение — это игра. По окончании игры вы хотите послать статистику, как в итоге сыграл пользователь. И вы хотите узнать у пользователя общий счет, количество убитых врагов и количество пройденных раундов.
В GA все это будет выглядеть примерно вот так:
// total score
mTracker = googleAnalytics.newTracker(R.xml.tracker_global_config);
HitBuilders.EventBuilder builder = new HitBuilders.EventBuilder()
.setCategory("gameOver")
.setAction("totalScore")
.setLabel("")
.setValue(gameStats.getTotalScore());
mTracker.send(builder.build());
// enemies beaten
mTracker = googleAnalytics.newTracker(R.xml.tracker_global_config);
HitBuilders.EventBuilder builder = new HitBuilders.EventBuilder()
.setCategory("gameOver")
.setAction("enemiesBeaten")
.setLabel("")
.setValue(gameStats.getEnemiesBeaten());
mTracker.send(builder.build());
// roundsSurvived
mTracker = googleAnalytics.newTracker(R.xml.tracker_global_config);
HitBuilders.EventBuilder builder = new HitBuilders.EventBuilder()
.setCategory("gameOver")
.setAction("roundsSurvived")
.setLabel("")
.setValue(gameStats.getRoundsSurvived());
mTracker.send(builder.build());
В GA каждое событие по сути представляет собой иерархию параметров:
category -> action -> label -> value
И в самой консоли вы могли наблюдать данную иерархию параметров. Собственно при придумывании событий, которые вы бы хотели отслеживать, вы должны были руководствоваться данной парадигмой. Также в консоли можно строить различные фильтры по данным параметрам.
Но в GA в плане событий есть небольшой минус. Если вы хотите навешать событию дополнительные параметры, помимо вышеназванных, вот тут приходится танцевать вокруг "category" -> "action" -> "label" -> "value", придумывать новые формулировки и прочее. Неудобно. По крайней мере так было раньше.
А теперь посмотрим, как можно данную статистику обыграть с FA:
Bundle params = new Bundle();
params.putLong("totalScore", gameStats.getTotalScore());
params.putLong("enemiesBeaten", gameStats.getEnemiesBeaten());
params.putLong("roundsSurvived", gameStats.getRoundSurvived());
mFirebaseAnalytics.logEvent("game_over", params);
Как видите, вместо трех событий мы отправляем одно, что более логично и удобно. Про «события» в FA мы поговорим подробнее чуть ниже.
Второе, чем сильно отличаются аналитики, это — консоль.
Вот как выглядит консоль в GA (картинка кликабельна):
[2]
«События» спрятаны глубоко во вкладке «Поведение» слева. Но в стандартном отчете сразу идет разбивка на Category, Action, Label (рисунок кликабельный):
[3]
Вот так выглядит консоль FA (рисунок кликабельный):
[4]
Первое, что вы видите, это — «Сводка». И я бы сразу обратил внимание на карточку User engagement (рисунок кликабельный):
[5]
Наконец-то в FA-консоль добавили нормальный просмотр экранов. До мая мы жили без этого. То есть событие «user engagement» отсылалось, но в консоли его никак нельзя было посмотреть. Это было ужасно. И это, возможно, одна из причин, почему никто не хотел переходить на FA.
Как еще можно заметить, вкладка Events идет сразу за Dashboard, что еще раз подтверждает — FA заточена на работу с событиями. К консоли мы также вернемся чуть позже, а сейчас я предлагаю погрузиться в эту обширную тему «Событий» в FA.
Давайте сразу взглянем на код:
Bundle bundle = new Bundle();
bundle.putString(FirebaseAnalytics.Param.ITEM_ID, id);
bundle.putString(FirebaseAnalytics.Param.ITEM_NAME, name);
bundle.putString(FirebaseAnalytics.Param.CONTENT_TYPE, "image");
mFirebaseAnalytics.logEvent(FirebaseAnalytics.Event.SELECT_CONTENT, bundle);
Вы можете отправлять до 500 различных типов событий в своем приложении, включая предустановленные (FirebaseAnalytics.Event.SELECT_CONTENT
— это предустановленное, но вы можете задавать и свои типы). Общее количество отправляемых событий не лимитировано (источник [6]).
К каждому событию можно прикреплять до 25 параметров (то, что идет в Bundle
). Параметры также есть предопределенные, но никто не запрещает вам задавать кастомные параметры. Описано здесь [7].
Типы событий и параметров — это обычные String
.
Названия событий и параметров чувствительны к регистру. Одинаковые события должны совпадать по типу и параметрам.
Кроме того, есть события, которые отправляются по умолчанию. Весь список автоматически отправляемых событий с описанием приведен по данной ссылке [8]. Как вы можете заметить, там много действительно интересных событий, которые раньше нам не представлялось возможным получить. Круто!
Также по приведенной выше ссылке вы можете прочитать, какие предопределенные события и параметры можно выбрать для определенных событий.
Вы обратили внимание, что как-то подозрительно много говорится про предопределенные названия событий и параметров. И в показательных примерах обычно посылаются именно такие события с параметрами. А это ведь неспроста. Допустим, вы посылаете событие с десятью кастомными параметрами. И тогда в консоли по вашему событию вы увидите следующее (рисунок кликабельный):
[9]
«Но где же все мои параметры?» — спросите вы. А нет их на консоли, вот так вот.
Дело в том, что все красивые графики и прочее строятся, только если вы используете предопределенные названия. Используете свое, «кастомное», ничегошеньки не увидите. Только «количество событий» да «количество пользователей».
И до I/O 17 это было прям страшной болью. Графики можно было строить, играя, например, с параметром Value [10], как в этой статье [11]. Но это, конечно, все не то.
И тут, конечно же, пора бы вспомнить про GA, где все для людей, строй всякие там фильтры по чему угодно и сколько душе угодно.
Но и тут маленькая засада. Стандартные отчеты — да, стройте без проблем. Но в большинстве случаев нам нужны и кастомные отчеты. Например, добавить Secondary dimension, чтобы отсортировать события по моделям устройств. И вот тут всплывает страшное слово «Sampling».
В зависимости от отчета алгоритм сэмплирования в GA различается. То, как конкретно считается семпл для каждого отчёта, «Гугл» не раскрывает, но в целом все практики уже известны. Обычно это hi-based-сэмплирование или cookie-based-сэмплирование. В первом случае берется рандомная выборка из всех записей (событий, просмотров и т.д.), во втором — рандомная выборка по всем пользователям (размеченным кукам или gaid/idfa, если это мобильное приложение).
Поэтому нельзя достоверно говорить об ошибке по каждому полю.
По практике говорят, что при выборке больше 5% ошибка в абсолютных числах в отчетах по событиям составляла меньше 2,5%.
За предоставление информации о сэмплировании хочу выразить благодарность Александру Сергееву из «Яндекса».
Да уж. Все непросто с этими «Событиями». И на самом деле FA идет навстречу пожеланиям простого люда.
Во-первых, никакого сэмплинга в FA нет. Там доступны все данные.
И это очень круто, так как стоимость Google Analytics 360 (платной версии GA без сэмплинга) весьма немаленькая. А в FA вы можете ваши данные выгрузить в BigQuery и там делать с ними все что угодно.
Во-вторых, после I/O 17 появилась возможность строить отчеты и по кастомным параметрам.
Вам прямо на экране конкретного события предлагается зарегистрировать кастомные параметры [12] (рисунок кликабельный):
[13]
Но учтите, что всего для данного приложения вы можете зарегистрировать до 50 таких параметров (10 текстовых и 40 числовых). Пробовал лайфхак для обхода данного ограничения: регистрировал для разных событий кастомные параметры с одинаковыми именами. Не помогло, все равно делается «плюс один».
Кроме того, если вы ожидаете увидеть сразу готовые отчеты, спешу вас разочаровать. Отчеты строятся накопительным образом. Допустим, есть у вас «event_1» с кастомным параметром «custom_1», для которого вы хотите построить отчет. В консоли вы настроили, чтобы строился данный отчет в момент времени X. Так вот в отчет попадут все события «event_1», которые придут после момента времени X. А все «event_1» до момента X, увы, не будут обработаны. Так что будьте внимательны.
То есть вроде бы лучше стало, но не сильно. Что еще обидно, вы не можете эти отчеты как-то совмещать друг с другом. Но, пожалуй, мы слишком многого хотим от консоли. Если уж вы хотите делать с данными все что угодно, то добро пожаловать в удивительный мир BigQuery. Давайте немного приоткроем эту завесу таинства данных.
BigQuery — это вообще немного другая галактика.
С BigQuery можно было работать и через GA, но только если у вас premium-режим. В FA же вам прямо во вкладке Events предлагается установить связь (рисунок кликабельный):
[14]
Google говорит: «Мы дарим вам машину, но за бензин платите вы». С тарифными планами можно ознакомиться здесь [15], а еще лучше здесь [16]. Но поверьте, чтобы просто попробовать, вам вполне достаточно будет бесплатных лимитов тарифа Blaze. Да и даже при работе с боевыми продуктами, судя по отзывам товарищей, плата весьма условной получается.
Итак, начнем знакомство. Вот так выглядит консоль BigQuery (рисунок кликабельный):
[17]
В левом меню представлен список доступных данных. Например, TestStep — это мой тестовый проект с одним приложением в составе. А bigquery-public-data и Public Datasets — это, как можно догадаться, публичные данные, с которыми вы можете поэкспериментировать и на которых можете потренироваться в написании запросов.
Справа же вы видите список запросов, как успешных, так и не очень.
Теперь взглянем на данные тестового приложения за 14 марта 2017 года (таблица app_events_20170314, рисунок кликабельный):
[18]
В таблицу я перебросил все данные за сутки (52 события). Общий состав таблицы представлен перед вами. Как видно, тут каждое событие описывается максимально полно, включая все properties, о которых речь будет чуть ниже.
Давайте посмотрим на превью данных (вкладка Preview, рисунок кликабельный):
[19]
Табличный вид с ходу малоинформативен. Намного более понятная форма — это JSON (рисунок кликабельный):
[20]
И тогда наше событие представляется в полном виде. В UI почему-то нельзя расширить окно показа json, поэтому приведу полный json последних пяти событий отдельно:
[
{
"user_dim": {
"user_id": null,
"first_open_timestamp_micros": "1488878151620000",
"user_properties": [
{
"key": "first_open_time",
"value": {
"value": {
"string_value": null,
"int_value": "1488880800000",
"float_value": null,
"double_value": null
},
"set_timestamp_usec": "1488878151620000",
"index": null
}
}
],
"device_info": {
"device_category": "mobile",
"mobile_brand_name": null,
"mobile_model_name": null,
"mobile_marketing_name": null,
"device_model": "507SH",
"platform_version": "6.0.1",
"device_id": null,
"resettable_device_id": null,
"user_default_language": "ru-ru",
"device_time_zone_offset_seconds": "10800",
"limited_ad_tracking": "false"
},
"geo_info": {
"continent": "Europe",
"country": "Russia",
"region": "Moscow",
"city": "Moscow"
},
"app_info": {
"app_version": "1.0",
"app_instance_id": "d0c587de4d5804ddc1d34f8d54b981f9",
"app_store": "manual_install",
"app_platform": "ANDROID",
"app_id": "com.example.matsyuk.testfirebase"
},
"traffic_source": null,
"bundle_info": {
"bundle_sequence_id": "65",
"server_timestamp_offset_micros": "-496748"
},
"ltv_info": null
},
"event_dim": [
{
"date": "20170314",
"name": "user_engagement",
"params": [
{
"key": "firebase_screen_class",
"value": {
"string_value": "SecondActivity",
"int_value": null,
"float_value": null,
"double_value": null
}
},
{
"key": "firebase_event_origin",
"value": {
"string_value": "auto",
"int_value": null,
"float_value": null,
"double_value": null
}
},
{
"key": "firebase_screen_id",
"value": {
"string_value": null,
"int_value": "1109587836504693342",
"float_value": null,
"double_value": null
}
},
{
"key": "engagement_time_msec",
"value": {
"string_value": null,
"int_value": "4424",
"float_value": null,
"double_value": null
}
}
],
"timestamp_micros": "1489478210462000",
"previous_timestamp_micros": "1489478205970000",
"value_in_usd": null
}
]
},
{
"user_dim": {
"user_id": null,
"first_open_timestamp_micros": "1488878151620000",
"user_properties": [
{
"key": "first_open_time",
"value": {
"value": {
"string_value": null,
"int_value": "1488880800000",
"float_value": null,
"double_value": null
},
"set_timestamp_usec": "1488878151620000",
"index": null
}
}
],
"device_info": {
"device_category": "mobile",
"mobile_brand_name": null,
"mobile_model_name": null,
"mobile_marketing_name": null,
"device_model": "507SH",
"platform_version": "6.0.1",
"device_id": null,
"resettable_device_id": null,
"user_default_language": "ru-ru",
"device_time_zone_offset_seconds": "10800",
"limited_ad_tracking": "false"
},
"geo_info": {
"continent": "Europe",
"country": "Russia",
"region": "Moscow",
"city": "Moscow"
},
"app_info": {
"app_version": "1.0",
"app_instance_id": "d0c587de4d5804ddc1d34f8d54b981f9",
"app_store": "manual_install",
"app_platform": "ANDROID",
"app_id": "com.example.matsyuk.testfirebase"
},
"traffic_source": null,
"bundle_info": {
"bundle_sequence_id": "64",
"server_timestamp_offset_micros": "-515257"
},
"ltv_info": null
},
"event_dim": [
{
"date": "20170314",
"name": "user_engagement",
"params": [
{
"key": "firebase_screen_class",
"value": {
"string_value": "MainActivity",
"int_value": null,
"float_value": null,
"double_value": null
}
},
{
"key": "firebase_event_origin",
"value": {
"string_value": "auto",
"int_value": null,
"float_value": null,
"double_value": null
}
},
{
"key": "firebase_screen_id",
"value": {
"string_value": null,
"int_value": "1109587836504693341",
"float_value": null,
"double_value": null
}
},
{
"key": "engagement_time_msec",
"value": {
"string_value": null,
"int_value": "17278",
"float_value": null,
"double_value": null
}
}
],
"timestamp_micros": "1489478205970000",
"previous_timestamp_micros": "1489153178047000",
"value_in_usd": null
}
]
},
{
"user_dim": {
"user_id": null,
"first_open_timestamp_micros": "1488878151620000",
"user_properties": [
{
"key": "first_open_time",
"value": {
"value": {
"string_value": null,
"int_value": "1488880800000",
"float_value": null,
"double_value": null
},
"set_timestamp_usec": "1488878151620000",
"index": null
}
}
],
"device_info": {
"device_category": "mobile",
"mobile_brand_name": null,
"mobile_model_name": null,
"mobile_marketing_name": null,
"device_model": "507SH",
"platform_version": "6.0.1",
"device_id": null,
"resettable_device_id": null,
"user_default_language": "ru-ru",
"device_time_zone_offset_seconds": "10800",
"limited_ad_tracking": "false"
},
"geo_info": {
"continent": "Europe",
"country": "Russia",
"region": "Moscow",
"city": "Moscow"
},
"app_info": {
"app_version": "1.0",
"app_instance_id": "d0c587de4d5804ddc1d34f8d54b981f9",
"app_store": "manual_install",
"app_platform": "ANDROID",
"app_id": "com.example.matsyuk.testfirebase"
},
"traffic_source": null,
"bundle_info": {
"bundle_sequence_id": "63",
"server_timestamp_offset_micros": "-500210"
},
"ltv_info": null
},
"event_dim": [
{
"date": "20170314",
"name": "ga_event",
"params": [
{
"key": "label",
"value": {
"string_value": "label1",
"int_value": null,
"float_value": null,
"double_value": null
}
},
{
"key": "firebase_screen_class",
"value": {
"string_value": "MainActivity",
"int_value": null,
"float_value": null,
"double_value": null
}
},
{
"key": "action",
"value": {
"string_value": "action1",
"int_value": null,
"float_value": null,
"double_value": null
}
},
{
"key": "firebase_event_origin",
"value": {
"string_value": "app",
"int_value": null,
"float_value": null,
"double_value": null
}
},
{
"key": "value",
"value": {
"string_value": null,
"int_value": "1",
"float_value": null,
"double_value": null
}
},
{
"key": "category",
"value": {
"string_value": "category1",
"int_value": null,
"float_value": null,
"double_value": null
}
},
{
"key": "firebase_screen_id",
"value": {
"string_value": null,
"int_value": "1109587836504693341",
"float_value": null,
"double_value": null
}
}
],
"timestamp_micros": "1489478204880000",
"previous_timestamp_micros": "1489137436229000",
"value_in_usd": null
}
]
},
{
"user_dim": {
"user_id": null,
"first_open_timestamp_micros": "1488878151620000",
"user_properties": [
{
"key": "first_open_time",
"value": {
"value": {
"string_value": null,
"int_value": "1488880800000",
"float_value": null,
"double_value": null
},
"set_timestamp_usec": "1488878151620000",
"index": null
}
}
],
"device_info": {
"device_category": "mobile",
"mobile_brand_name": null,
"mobile_model_name": null,
"mobile_marketing_name": null,
"device_model": "507SH",
"platform_version": "6.0.1",
"device_id": null,
"resettable_device_id": null,
"user_default_language": "ru-ru",
"device_time_zone_offset_seconds": "10800",
"limited_ad_tracking": "false"
},
"geo_info": {
"continent": "Europe",
"country": "Russia",
"region": "Moscow",
"city": "Moscow"
},
"app_info": {
"app_version": "1.0",
"app_instance_id": "d0c587de4d5804ddc1d34f8d54b981f9",
"app_store": "manual_install",
"app_platform": "ANDROID",
"app_id": "com.example.matsyuk.testfirebase"
},
"traffic_source": null,
"bundle_info": {
"bundle_sequence_id": "62",
"server_timestamp_offset_micros": "-499813"
},
"ltv_info": null
},
"event_dim": [
{
"date": "20170314",
"name": "select_content",
"params": [
{
"key": "firebase_screen_class",
"value": {
"string_value": "MainActivity",
"int_value": null,
"float_value": null,
"double_value": null
}
},
{
"key": "content_type",
"value": {
"string_value": "image",
"int_value": null,
"float_value": null,
"double_value": null
}
},
{
"key": "item_name",
"value": {
"string_value": "name1",
"int_value": null,
"float_value": null,
"double_value": null
}
},
{
"key": "firebase_event_origin",
"value": {
"string_value": "app",
"int_value": null,
"float_value": null,
"double_value": null
}
},
{
"key": "firebase_screen_id",
"value": {
"string_value": null,
"int_value": "1109587836504693341",
"float_value": null,
"double_value": null
}
},
{
"key": "item_id",
"value": {
"string_value": "1",
"int_value": null,
"float_value": null,
"double_value": null
}
}
],
"timestamp_micros": "1489478204208000",
"previous_timestamp_micros": "1489137435605000",
"value_in_usd": null
}
]
},
{
"user_dim": {
"user_id": null,
"first_open_timestamp_micros": "1488878151620000",
"user_properties": [
{
"key": "first_open_time",
"value": {
"value": {
"string_value": null,
"int_value": "1488880800000",
"float_value": null,
"double_value": null
},
"set_timestamp_usec": "1488878151620000",
"index": null
}
}
],
"device_info": {
"device_category": "mobile",
"mobile_brand_name": null,
"mobile_model_name": null,
"mobile_marketing_name": null,
"device_model": "507SH",
"platform_version": "6.0.1",
"device_id": null,
"resettable_device_id": null,
"user_default_language": "ru-ru",
"device_time_zone_offset_seconds": "10800",
"limited_ad_tracking": "false"
},
"geo_info": {
"continent": "Europe",
"country": "Russia",
"region": "Moscow",
"city": "Moscow"
},
"app_info": {
"app_version": "1.0",
"app_instance_id": "d0c587de4d5804ddc1d34f8d54b981f9",
"app_store": "manual_install",
"app_platform": "ANDROID",
"app_id": "com.example.matsyuk.testfirebase"
},
"traffic_source": null,
"bundle_info": {
"bundle_sequence_id": "61",
"server_timestamp_offset_micros": "-537470"
},
"ltv_info": null
},
"event_dim": [
{
"date": "20170314",
"name": "session_start",
"params": [
{
"key": "firebase_screen_class",
"value": {
"string_value": "MainActivity",
"int_value": null,
"float_value": null,
"double_value": null
}
},
{
"key": "firebase_event_origin",
"value": {
"string_value": "auto",
"int_value": null,
"float_value": null,
"double_value": null
}
},
{
"key": "firebase_screen_id",
"value": {
"string_value": null,
"int_value": "1109587836504693341",
"float_value": null,
"double_value": null
}
}
],
"timestamp_micros": "1489478198696000",
"previous_timestamp_micros": "1489137330069000",
"value_in_usd": null
}
]
}
]
Красота, да и только!
Теперь более подробно рассмотрим Queries. Выберем первый (рисунок кликабельный):
[21]
И перед нами откроется следующий экран (рисунок кликабельный):
[22]
Запрос наш довольно произвольный. Обратите внимание на вкладку Results. Собственно, в ней вы и увидите результаты вашего запроса.
Если открыть вкладку Explanation, то вы увидите более подробный процесс прохождения запроса (рисунок кликабельный):
[23]
Ну и самая интересная вкладка — Job information (рисунок кликабельный):
[24]
Обратите внимание на Bytes Processed, Bytes Billed и Bites Tier. В ходе запроса было обработано 26,4 KB, но платите вы по нижней границе для Bites Tier = 1, то есть платите как за 10 MB. Однако, судя по документации [16], 1 TB в месяц для вас будет бесплатным, а каждый последующий будет стоить $5. Вполне вам хватит наиграться и напробоваться. Ну и важное дополнение — платите вы только за успешные запросы!
Даже очень краткий обзор по BigQuery получается немаленьким. Это очень мощный и функциональный инструмент, с помощью которого вы можете анализировать данные как угодно. Но за 5 минут в BigQuery вы точно не разберетесь, в отличие от обычной консоли в GA или FA. Поэтому очень круто, если в вашей команде или компании есть человек, который в этом разбирается, и который может получить какие угодно результаты.
Если этим человеком хотите стать вы, то начать можете со вступительного видео от «Гугла» [25], где, кстати, рассказывается и про расчет стоимости. Также есть неплохие статьи — раз [26] и два [27]. Далее советую вам копать в сторону официальной доки [28] и книги по BigQuery [29] (целая книга, Карл!).
Будет здорово, если кто-то уже хорошо покопал в эту сторону и может поделиться советами и опытом =)
Отмечу также, что существуют UI-обертки над BigQuery типа Data Studio [30], позволяющие загружать туда данные и удобно их визуализировать. Data Studio пока в бете, но в будущем обещает стать очень удобным инструментом.
Мы, по сути, продолжаем тему событий, так как user properties — ее неотъемлемая часть.
User properties (по-русски «Свойства пользователя») — это признаки, с помощью которых вы можете описывать различные сегменты вашей пользовательской базы, такие как язык, географическая локация и т.д. Их еще называют sticky params, так как они прикрепляются к каждому событию.
Изначально к каждому событию прикрепляются только properties по-умолчанию. А если в коде вы вызываете подобный код:
mFirebaseAnalytics.setUserProperty("license_property", mLicenseType);
то к каждому последующему вашему событию будет прикрепляться property «license_property» с заданным заранее значением (значение «mLicenseType»). И даже после перезапуска приложения, телефона и прочее данное property будет прикрепляться. То есть property является еще и persistence.
При этом вы должны предварительно зарегистрировать ваше property в консоли (рисунок кликабельный):
[31]
Все подробно расписано здесь [32] и в api [33].
Отмечу, что для конкретного приложения вы можете отправлять до 25 properties (без учета properties, которые отправляются по умолчанию). Список properties, отправляемых по умолчанию, здесь [34].
Собственно в консоли вы можете фильтровать все, что угодно, по properies и «аудитории» (про «аудиторию» скажем чуть ниже). Например, события (рисунки кликабельные):
[35]
Аналогом setUserProperty(...)
в GA являются методы setCustomDimension(...)
и setCustomMetric(...)
. Единственное, данные dimension и metric не являются sticky и persistence, и вам будет необходимо к каждому событию каждую сессию вручную прикреплять их.
Думаю, в каждом приложении есть как минимум два аналитических инструмента. Обычно их гораздо больше. Аналитики тоже прогрессивные люди и не стоят на месте. Но нам все это поддерживать. Да и плюс трафик. Так что же лучше сделать?
Есть очень хорошая гугловская статья [37], которая уже была упомянута мною, где описываются различные варианты.
Кратко представлю их, чтобы вы имели представление:
Чтобы настроить на своем проекте аналитику, вам нужно четко следовать данной документации [41]. Есть уже встроенный в Android Studio плагин, который делает за вас половину работы. Если у вас все в первый раз, то процесс займет не более 15 минут. Хотите API? А вот и оно [42], довольно короткое и вроде понятное.
После настойки FA через Android Studio Assistant у себя в проекте вы заметите появление нового файла google-services.json
. Это специальный файл, в котором прописаны все идентификаторы и пути, которые необходимы для работы в вашем приложении различных гугловских сервисов, в данном случае — для работы FA.
Также в ваш корневой build.gradle
добавилась такая строчка:
dependencies {
classpath 'com.google.gms:google-services:3.0.0'
// ...
}
Собственно google-services
— это специальный плагин, который распарсивает google-services.json
, преобразуя его в обычные строчки, используемые FA. А также google-services
добавляет все необходимые зависимости для используемых гугловских сервисов (в нашем случае только для FA). Правда, для этого нужно в app/build.gradle
в самом конце добавить:
apply plugin: 'com.google.gms.google-services'
В google-services.json
находится вся необходимая информация для подключения вашего проекта к Firebase, причем не только к аналитике, но и ко всем инстументам.
{
"project_info": {
"project_number": "887654601522",
"firebase_url": "https://fir-test3-4bab3.firebaseio.com",
"project_id": "fir-test3-4bab3",
"storage_bucket": "fir-test3-4bab3.appspot.com"
},
"client": [
{
"client_info": {
"mobilesdk_app_id": "1:887654601522:android:9c6c1c11f784b956",
"android_client_info": {
"package_name": "com.example.matsyuk.firebasetest3"
}
},
"oauth_client": [
{
"client_id": "887654601522-o8rolth1g5mq5qq650844chk07mib2un.apps.googleusercontent.com",
"client_type": 1,
"android_info": {
"package_name": "com.example.matsyuk.firebasetest3",
"certificate_hash": "82f13b732dec32c5ebd4498c3a7acf4bda23a846"
}
},
{
"client_id": "887654601522-4riqkg424gb236q6mqehksn03u4hoqqg.apps.googleusercontent.com",
"client_type": 3
}
],
"api_key": [
{
"current_key": "AIzaSyAYRPNTcgxWP7qUzI__kx9gSwxnIgc3iBo"
}
],
"services": {
"analytics_service": {
"status": 1
},
"appinvite_service": {
"status": 2,
"other_platform_oauth_client": [
{
"client_id": "887654601522-4riqkg424gb236q6mqehksn03u4hoqqg.apps.googleusercontent.com",
"client_type": 3
}
]
},
"ads_service": {
"status": 2
}
}
}
],
"configuration_version": "1"
}
С помощью плагина google-services
данный json преобразуется в набор строчек, сгенерированных в файл your_projectappbuildgeneratedresgoogle-servicesdebugvaluesvalues.xml
:
<?xml version="1.0" encoding="UTF-8"?>
<resources>
<string translatable="false" name="default_web_client_id">887654601522-4riqkg424gb236q6mqehksn03u4hoqqg.apps.googleusercontent.com</string>
<string translatable="false" name="firebase_database_url">https://fir-test3-4bab3.firebaseio.com</string>
<string translatable="false" name="gcm_defaultSenderId">887654601522</string>
<string translatable="false" name="google_api_key">AIzaSyAYRPNTcgxWP7qUzI__kx9gSwxnIgc3iBo</string>
<string translatable="false" name="google_app_id">1:887654601522:android:9c6c1c11f784b956</string>
<string translatable="false" name="google_crash_reporting_api_key">AIzaSyAYRPNTcgxWP7qUzI__kx9gSwxnIgc3iBo</string>
<string translatable="false" name="google_storage_bucket">fir-test3-4bab3.appspot.com</string>
</resources>
Обратите внимание на такие строчки, как firebase_database_url
, google_storage_bucket
и т.д. Захотите подключить еще один инструмент от Firebase, в проекте у вас уже все будет готово для этого.
Подробнее про плагин и устройство google-services.json
написано здесь [43].
А теперь рассмотрим жизненный пример. Есть у нас приложение Example с applicationId
, равное, допустим, com.fa.example
. И есть у нас в продукте flavors:
productFlavors {
dev {
applicationId "com.fa.example.dev"
}
qa {
applicationId "com.fa.example.qa"
}
prod {
// applicationId "com.fa.example"
}
}
Далее мы хотим зарегистрировать проект в FA через Android Studio Assistant. Делаем все по инструкции и получаем в консоли проект Example, в котором три приложения:
И если вы посмотрите в проекте app/google-services.json
, то в нем будет информация о ваших трех приложениях (трех flavors с разными applicationId
). То есть для каждого flavor аналитика будет собираться отдельно.
Также отмечу, что вы можете самостоятельно скачать google-services.json
с любого приложения вашего проекта. Но все google-services.json
вашего проекта будут одинаковы и будут содержать информацию о всех приложениях в проекте.
Далее такая ситуация. Ваш проект Example настроен с FA. Но вам вдруг понадобилось добавить в проект еще один flavor с другим именем пакета. И для этого flavor вы также хотите собирать аналитику отдельно. Тогда вам необходимо сделать следующее:
Зарегистрировать новое приложение в проекте в консоли:
Скачать новый google-services.json
(в котором будет информация о четырех приложениях в проекте) и подставить его вместо старого.
Теперь такой пример. Допустим, есть у вас в проекте buildTypes
, и выглядят они в build.gradle
-файле следующим образом:
buildTypes {
release {
}
ultra_debug {
applicationIdSuffix ".ultra_debug"
}
debug {
applicationIdSuffix ".debug"
}
}
То есть для сборок ultra_debug
и debug
вы добавляете суффикс к имени пакета. Таким образом, у вас в проекте три вышеназванных buildTypes и три flavors:
productFlavors {
dev {
applicationId "com.fa.example.dev"
}
qa {
applicationId "com.fa.example.qa"
}
prod {
// applicationId "com.fa.example"
}
}
Вы запускаете Android Studio Assistant для подключения к проекту FA. Как вы думаете, сколько будет зарегистрировано в консоли приложений и с какими именами пакетов?
Не догадаетесь =) Появятся в консоли такие приложения:
com.fa.example.debug
com.fa.example.dev.debug
com.fa.example.qa.debug
Почему именно только с суффиксом «debug», осталось для меня загадкой. Так что имейте в виду данный баг.
Ну и заключительный пример.
У вас в проекте все также те самые три flavors. И вы хотите добавить в проекте еще один flavor (например, custom
), но для него нет необходимости в другом applicationId
, и при этом данный flavor тоже желательно отдельно от всех остальных просматривать с точки зрения аналитики:
productFlavors {
dev {
applicationId "com.fa.example.dev"
}
qa {
applicationId "com.fa.example.qa"
}
prod {
// applicationId "com.fa.example"
}
custom {
// applicationId "com.fa.example"
}
}
Ситуация усложняется еще тем, что в консоли в проект вы не можете добавлять приложения с одинаковым applicationId
. Как тогда быть? Делаем следующее:
com.fa.example
.google-services.json
.google-services.json
следующим образом (выделено красным).То есть в вашем андроидовском проекте будут лежать уже два google-services.json
. При сборке google-services plugin
сначала смотрит в папку конкретного flavor. Если в этой папке есть google-services.json
, то плагин берет его. Если нет, то тогда берется google-services.json
с app
папки. Довольно удобно и гибко получается в итоге.
Вроде бы жизнь разработчика стала проще. Зарегистрировал проект в консоли, скачал google-services.json
, закинул в app/
(ну это все в случае без flavors и прочего), и все, больше ни о чем не думаешь. Но иногда бывает необходимость на лету переключить канал аналитики. И если в GA вы могли задавать в коде id, то в FA пока что такая возможность отсутствует. И были у меня надежды сначала на такую конструкцию (взято с SO [44]):
FirebaseOptions options = new FirebaseOptions.Builder()
.setApplicationId("bla-bla") // Required for Analytics.
.setApiKey("bla-bla") // Required for Auth.
.setDatabaseUrl("bla-bla") // Required for RTDB.
.build();
FirebaseApp.initializeApp(this /* Context */, options, "secondary");
Но выдается ошибка «Missing google_app_id. Firebase Analytics disabled». Команда Firebase знает про это и постепенно работает над данной проблемой.
Более подробно про все описанные примеры вы можете прочитать в вышеназванной статье [43] про google-services
-плагин и здесь [45].
В GA есть метод setLocalDispatcher(...)
. С помощью него мы можем задавать интервал периодической отправки данных. Хорошо, что FA заботится о нас и нашем трафике и не дает нам возможность самим регулировать данный параметр. Но в GA с помощью метода setLocalDispatcher(-1)
мы можем отменить автоматическую отправку событий, а с методом dispatchLocalHits()
вручную отправлять накопившиеся события. Это очень удобно, когда, например, мы не хотим отправлять события до принятия соглашения и т.д.
У FA подобной возможности накопления и отправки событий нет, придется все руками делать.
Зато хотя бы есть метод setAnalyticsCollectionEnabled(boolean enabled)
, с помощью которого мы можем включать и отключать аналитику. Например, если мы не хотим отправлять аналитику до принятия пользователем нужного соглашения, то в манифесте прописываем:
<meta-data android:name="firebase_analytics_collection_enabled" android:value="false" />
А потом, когда нужно, в коде вызовем:
setAnalyticsCollectionEnabled(true);
Также можно отключить аналитику на постоянной основе. То есть даже вызов setAnalyticsCollectionEnabled(true)
не поможет. Для этого в манифесте прописываем:
<meta-data android:name="firebase_analytics_collection_deactivated" android:value="true" />
Информация взята из данной статьи [46].
В FA события с реальных устройств приходят в консоль лишь спустя сутки. И в начале не было возможности просмотреть действия пользователя в реальном времени. Чтобы увидеть первые данные, приходилось ждать целые сутки. Сейчас же вы можете воспользоваться вкладкой StreamView/DebugView (рисунок кликабельный):
[47]
На картинке выше представлен StreamView, на котором вы можете наблюдать, как себя ведут пользователи в данный момент времени. Также вы можете выбрать режим Snapshot (кнопка User snapshot справа снизу), и вам покажутся действия случайно выбранного пользователя (рисунок кликабельный):
[48]
Подобным образом выглядит и DebugView [49]. Наконец-то отлаживаться можно в режиме реального времени. Вы будете видеть все events и properties, которые посылаются вашим приложением, включая и events c properties по умолчанию. Как можно представить, до DebugView процесс отладки был воистину ужасным.
О StreamView и DebugView хорошо расписано здесь [50].
Что в GA, что в FA, все мы видим такие слова, как «сессия», «количество событий за сессию» и т.д. И, наверное, может сложиться впечатление, что сессия = время жизни процесса. Но это не так. Сессия — это просто временной промежуток, в течение которого ваше приложение активно (находится в foreground). В API FA есть такие методы:
setMinimumSessionDuration (long milliseconds); // default 10 sec
setSessionTimeoutDuration (long milliseconds); // default 30 min
То есть если вы запустили приложение и убили его менее чем за minimumSessionDuration, то сессия даже не начнется. Если же запущенное приложение находится в foreground более minimumSessionDuration, то сессия стартует.
Если ваше приложение было выгружено системой, но оно успело подняться до истечения sessionTimeoutDuration, то это все будет одна сессия. Если вы запустили приложение, что-то поделали там, потом вышли из него (то есть приложение не в foreground), и только через sessionTimeoutDuration+ зашли обратно (при этом приложение не было убито, к примеру), то первая сессия завершится и стартует вторая.
Вы можете формировать разные аудитории, по которым в дальнейшем можно выставлять фильтры, организовывать компании и т.д. Как это выглядит (рисунок кликабельный):
[51]
Создание новой «аудитории» (рисунок кликабельный):
[52]
Допустим, вам нужна аудитория «Мужчины из России, которые прошли регистрацию». Тогда при создании «аудитории» вы выбираете properties «country» = «Russia» и «sex» = «male» и event «reg_comleted» (это уже ваш кастомный event) = «true».
На этой вкладке у вас есть возможность строить разные воронки (рисунок кликабельный):
[53]
Очень нравится маркетологам это делать =)
Отмечу, что подобный функционал есть и у GA.
Есть еще вкладки Attribution и Cohorts. Но, честно говоря, я ими вообще не пользовался. Для чего они нужны, лучше распишут аналитики.
Полное описание консоли можно прочитать здесь [12].
Немаленькая у нас в итоге получилась статья. Давайте попробуем подвести итоги.
Плюсы:
Минусы:
Меня часто спрашивают, так стоит ли использовать FA или нет. Может, вполне достаточно GA? Или сразу обе аналитики не достойны места в вашем продукте?
Однозначного ответа нет. Все очень зависит от потребностей ваших аналитиков и маркетологов. А также зависит от способностей ваших аналитиков осилить BigQuery. Все-таки мы, разработчики, — это «пехотинцы продукта», особенно в части аналитики. Что нам скажут, то мы и будем делать. Но лично я бы смотрел в сторону связки FA + BigQuery. Уж очень она крутая, и вы никак не ограничены возможностями консоли.
Большое спасибо, что дочитали до конца! Пишите комментарии, дополняйте и поправляйте! Сделаем нашу разработческую жизнь лучше!
P.S. Большое спасибо хочу сказать Тимуру Ахметгарееву за помощь и за то, что никогда не бросал в беде =)
P.P.S. И еще добавлю. 16 сентября 2017 совместно с независимым сообществом разработчиков MOSDROID мы организовываем вечернюю встречу для всех, кто заинтересован в разработке под Android. По традиции, мы подготовили для вас несколько докладов. Ждём всех желающих. Зарегистрироваться на встречу можно здесь [54].
Автор: xoxol_89
Источник [55]
Сайт-источник PVSM.RU: https://www.pvsm.ru
Путь до страницы источника: https://www.pvsm.ru/android-development/263694
Ссылки в тексте:
[1] ресурс: https://support.google.com/analytics/answer/4553001?hl=en
[2] Image: https://habrastorage.org/web/645/97f/53a/64597f53af2c42a2b73acb59bdec39a2.png
[3] Image: https://habrastorage.org/web/1b2/0df/eaf/1b20dfeaf0ac45b3b22f95fd583ae73e.png
[4] Image: https://habrastorage.org/web/6b5/321/9a0/6b53219a00544170a3068d134a9ac493.png
[5] Image: https://habrastorage.org/web/7c5/281/979/7c52819791d24520a3d84e7176521092.png
[6] источник: https://firebase.google.com/docs/analytics/android/events
[7] здесь: https://firebase.google.com/docs/reference/android/com/google/firebase/analytics/FirebaseAnalytics.Event
[8] данной ссылке: https://support.google.com/firebase/answer/6317485
[9] Image: https://habrastorage.org/web/4fe/1eb/9c7/4fe1eb9c77f940c0b7150f29c4a1a531.png
[10] Value: https://firebase.google.com/docs/reference/android/com/google/firebase/analytics/FirebaseAnalytics.Param#VALUE
[11] статье: https://firebase.googleblog.com/2017/02/firebase-analytics-quick-tip-value.html
[12] зарегистрировать кастомные параметры: https://support.google.com/firebase/answer/7397304?hl=en&ref_topic=6317489
[13] Image: https://habrastorage.org/web/eef/6d7/eed/eef6d7eed02d4836b56a45b18e2d1d98.png
[14] Image: https://habrastorage.org/web/c55/176/6ae/c551766aefb843b89b0edf85e0cb7c4a.png
[15] здесь: https://firebase.google.com/pricing/
[16] здесь: https://cloud.google.com/bigquery/pricing
[17] Image: https://habrastorage.org/web/278/e40/52f/278e4052f8874c88a9ae74d8962abf38.png
[18] Image: https://habrastorage.org/web/f58/16e/1f9/f5816e1f91d543b79143dc330200a811.png
[19] Image: https://habrastorage.org/web/64a/73d/997/64a73d997238412b916b380f288b85db.png
[20] Image: https://habrastorage.org/web/36d/578/d3d/36d578d3d37c42e3925939ff06f3f76b.png
[21] Image: https://habrastorage.org/web/9d7/361/197/9d7361197b724c86b2b7fd6bcccf3870.png
[22] Image: https://habrastorage.org/web/e82/e6c/921/e82e6c921c744bffaf1264d28f876d37.png
[23] Image: https://habrastorage.org/web/d8e/594/4fd/d8e5944fdaf8419a80e0acc65b74156f.png
[24] Image: https://habrastorage.org/web/d3d/779/14b/d3d77914bd3946ff80b45e3053536628.png
[25] видео от «Гугла»: https://www.youtube.com/watch?v=Ki_F6VCOtXU
[26] раз: https://cloudplatform.googleblog.com/2016/09/using-BigQuery-and-Firebase-Analytics-to-understand-your-mobile-app.html
[27] два: https://cloud.google.com/solutions/mobile/mobile-firebase-analytics-big-query
[28] официальной доки: https://cloud.google.com/bigquery/docs/
[29] книги по BigQuery: http://eu.wiley.com/WileyCDA/WileyTitle/productCd-1118824822.html
[30] Data Studio: https://datastudio.google.com/navigation/reporting
[31] Image: https://habrastorage.org/web/351/067/2e7/3510672e7e9644479f22b330841a8555.png
[32] здесь: https://firebase.google.com/docs/analytics/android/properties
[33] api: https://firebase.google.com/docs/reference/android/com/google/firebase/analytics/FirebaseAnalytics.UserProperty
[34] здесь: https://support.google.com/firebase/answer/6317486?hl=en&ref_topic=6317484
[35] Image: https://habrastorage.org/web/ea5/461/8b5/ea54618b5bd3454fb08f618eeed1682e.png
[36] Image: https://habrastorage.org/web/6ff/a97/151/6ffa97151398416fae1b80a99231f6dc.png
[37] гугловская статья: https://firebase.googleblog.com/2017/02/how-do-i-add-firebase-analytics-to-app.html
[38] Google Tag Manager: https://www.google.com/analytics/tag-manager/?utm_source=ga_partner_gallery&utm_medium=referral&utm_campaign=app_listing
[39] прочие партнеры Google Tag Manager: https://support.google.com/tagmanager/answer/6106924?hl=en
[40] статью: https://developers.google.com/tag-manager/android/v5/
[41] документации: https://firebase.google.com/docs/analytics/android/start/
[42] оно: https://firebase.google.com/docs/reference/android/com/google/firebase/analytics/package-summary
[43] здесь: https://developers.google.com/android/guides/google-services-plugin
[44] SO: https://stackoverflow.com/questions/41606331/is-it-possible-to-configure-firebase-analytics-without-google-services-json-conf
[45] здесь: https://firebase.googleblog.com/2016/08/organizing-your-firebase-enabled-android-app-builds.html
[46] данной статьи: https://firebase.google.com/support/guides/disable-analytics
[47] Image: https://habrastorage.org/web/509/7b5/f26/5097b5f262a64da19fe0ce5388d98afc.png
[48] Image: https://habrastorage.org/web/e65/186/c4c/e65186c4c6ad49b8a7621342d1820aeb.png
[49] DebugView: https://firebase.google.com/docs/analytics/debugview
[50] здесь: https://firebase.googleblog.com/2017/03/realtime-analytics-for-everyone.html
[51] Image: https://habrastorage.org/web/646/5d6/a19/6465d6a19b384923a8b7e6170e80c7a8.png
[52] Image: https://habrastorage.org/web/9c9/61c/df6/9c961cdf6355417787ec1a9f542d261d.png
[53] Image: https://habrastorage.org/web/adb/7ae/924/adb7ae924adf42cbaffca6416202eb64.png
[54] здесь: https://events.kaspersky.com/event/android2
[55] Источник: https://habrahabr.ru/post/334292/
Нажмите здесь для печати.