Не так давно Яндекс.Метрика анонсировала открытый API, с помощью которого можно получить доступ практически ко всем функциям Метрики из собственной программы.
Сегодня я хочу немного рассказать об использовании этого API и о том, как на его основе создать простой widget для Android-устройств.
API Яндекс.Метрики построен по принципу REST. Все чем можно управлять через API, представлено ресуром: счетчики, цели, фильтры и т.п. Операции над ресурсами (чтение, удаление, изменение) совершаются посредством HTTP запросов к серверам API Я.Метрики, каждому типу операции соответствует свой HTTP метод:
Например, для получения списка счетчиков надо обратиться к ресурсу counters:
Входные данные для REST вызовов и результаты могут кодироваться в двух форматах: XML и JSON. В дальнейшем мы будем использовать JSON, как более компактный и удобный для отображения на структуры, использующиеся в языках программирования, формат.
Естественно, работа через API возможно только со счетчиками, к которым есть доступ из своего аккаунта Я.Метрики. Для идентификации владельца аккаунта используется OAuth авторизация. Устроена она очень просто. Разработчик, желающий воспользоваться API Я.Метрики, регистрирует свое приложение (см. инструкции) и получает идентификатор приложения. Зарегистрировав приложение, можно получить для него отладочный OAuth-токен и сразу же начать работу с API Метрики. Отладочный токен разрешит работу со счетчиками, доступными из аккаунта разработчика, зарегистрировавшего приложение. Токен надо передавать в каждом HTTP запросе, или как дополнительный параметр в URL (
Чтобы приложение могло работать с произвольными счетчиками, а не только с доступными разработчику, нужен отдельный OAuth токен для каждого пользователя приложения. Как его можно получить:
Благодаря использованию REST, простейшая работа с API (запросы на чтение) возможна непосредственно из браузера, без какого-либо кодирования. Например, этот запрос выдаст информацию о счетчике в демо-аккаунте API Я.Метрики. Такой запрос выдаст отчет по посещаемости счетчика.
Несмотря на простоту REST интерфейса, для полноценного использования API в программе, написанной на языке высокого уровня, необходимо совершить довольно много телодвижений: формирование URL-ов запросов, отправка HTTP запросов и получение результатов, формирование и парсинг JSON, обработка ошибок и т.д. Чтобы упростить жизнь, я создал готовую библиотеку для работы с API Я.Метрики на языкe Java: Metrika4j. Код библиотеки распространяется под Apache License, вы можете свободно изменять и дополнять его под ваши требования. Далее я расскажу, как с помощью этой библиотеки создать widget для Android устройств, отображающий посещаемость сайта.
Metrika4j позволяет работать с Яндекс.Метрикой, оперируя привычными Java разработчику понятиями (классы, вызовы функций, типы) и не задумываясь о низкоуровневой работе с HTTP и JSON. Центральный интерфейс библиотеки — MetrikaApi. В нем есть методы для работы с главными сущностями Метрики: счетчиками и отчетами. Работа с дополнительными сущностями (цели, фильтры и т.п.) вынесена в отдельные мини-API, получаемые из главного класса
Структура методов примерно соответствует структуре REST-вызовов API Я.Метрики. Аргументы, передаваемые в REST-вызов, соответствуют аргументам, передаваемым в методы API (кроме API работы с отчетами, о котором мы расскажем отдельно).
Metrika4j поддерживает «из коробки» две библиотеки для работы с JSON: Jackson JSON processor и org.json. Для Jackson поддерживается 100% функциональности, для org.json — минимум, достаточный для работы с отчетами: чтение списка счетчиков и получение отчетов. Библиотека org.json встроена в Andriod API, поэтому её использование удобно для Android приложений. При необходимости, разработчик может воспользоваться любой другой JSON-библиотекой, имплементировав с её помощью интерфейсы JsonMapper и JsonObject.
Сначала необходимо создать экземпляр
Далее с помощью созданного экземпляра можно выполнять любые операции:
Через API доступны почти все отчеты, существующие в Метрике. Набор доступных отчетов содержится в классе Reports. Для построения выбранного отчета надо вызвать метод
Отчет возвращается в виде объекта Report, представляющего собой таблицу с результатами, плюс некоторая дополнительная информация (итоги, интервал дат, к которым относится отчет и т.п.). Каждая строка таблицы результатов — объект ReportItem, данные из которого можно получить одним из методов
Более подробное описание работы с Metrika4j содержится в Javadoc.
Имея в своем распоряжении такие мощные инструменты, как API Я.Метрики и Metrika4j, можно решать любые задачи, вплоть до создания альтернативных пользовательских интерфейсов к Яндекс.Метрике. Но в рамках этой статьи мы ограничимся более скромной целью: создадим виджет для Android, который будет показывать текущую посещаемость сайта. Исходный код виджета доступен на GitHub в проекте MetrikaWidget. Так же, как и код Metrika4j, он свободно распространяется с минимумом лицензионных ограничений.
Начнем с создания
Если приложение запускается в первый раз и OAuth token-а еще нет в SharedPreferences, надо получить его. Для этого переходим на URL запроса токена:
Для пользователя это будет выглядеть, как открывшаяся страница «Приложение запрашивает доступ к вашим данным на Яндексе». Предположим, что пользователь успешно авторизовался и разрешил доступ. Дальше начинается интересное: как передать токен из веб-интерфейса обратно в наше приложение? Чтобы сделать это, надо зарегистрировать одну из активностей в приложении, как обработчик специфического для нашего приложения протокола (специфического — чтобы наш обработчик не пересекся с другими приложениями). В
Мы зарегистрировали AuthTokenActivity, как обработчик протокола «metwd» для псевдохоста «oauthtoken». Теперь достаточно указать Callback URI
Код получения данных для виджета предельно прост:
Нюанс в том, что этот код нельзя выполнять «в лоб». Сервера API Метрики отвечают довольно быстро, а вот сам HTTP запрос может доооолго идти до сервера и обратно по медленным и ненадежным каналам мобильной связи. В результате виджет, ожидающий ответ, «зависнет» с точки зрения Android OS, и будет выдано окошко, предлагающее аварийно завершить подвисшее приложение. Это явно не то, что нам нужно. Поэтому все запросы к API Метрики выполняются асинхронно, с помощью класса AsyncTask. Вот упрощенный код загрузки списка счетчиков (полная версия — в классе WidgetSetupActivity):
Для виджетов работа с данными немного сложнее. Начнем с того, что виджет-провайдер, получающий события «пора обновить виджет», в терминологии Android является broadcast receiver-ом. Жизненный цикл broadcast receiver-а короток: он обрабатывает событие, после чего немедленно умирает. Если стартовать из обработчика события поток, то смерть receiver-a может наступить раньше, чем закончит работу поток: печаль. Android developer guide настоятельно рекомендует пользоваться для таких случаев сервисами. Так и сделаем: виджет-провайдер (MetrikaWidgetProvider) получает события и передаёт их для обработки в UpdateService.
Работа с виджетами в Android — отдельная большая тема, выходящая за рамки этой статьи и хорошо освещенная как в руководстве разработчика Android, так и в дополнительных статьях. Поэтому расскажу кратко, а за деталями отошлю к исходному коду классов
Виджет может находиться в трех состояниях: «данные получены», «обновление данных» и «нет связи». Обновление виджета происходит по таймеру или при клике на виджет.
Если виджет может находиться в состоянии «нет связи», логично было бы обновлять его при появлении этой самой связи, чтобы пользователь сразу увидел актуальные данные. Для этого виджет-провайдер подписывается на системные события, связанные с изменением статуса сетевого интерфейса:
При получении такого события происходит проверка, появилась ли связь, и вызывается обновление:
Виджет может быть собран из исходного кода, находящегося в проекте MetrikaWidget.
Вы не Android-разработчик, но хотите пользоваться виджетом? Нет проблем — можно загрузить готовый виджет. Релиз с Android Market или debug build c GitHub.
После установки у вас также появится приложение «Я.Метрика», которое на самом деле является мини-инструкцией к виджету.
Сегодня я хочу немного рассказать об использовании этого API и о том, как на его основе создать простой widget для Android-устройств.
Основы работы с API
API Яндекс.Метрики построен по принципу REST. Все чем можно управлять через API, представлено ресуром: счетчики, цели, фильтры и т.п. Операции над ресурсами (чтение, удаление, изменение) совершаются посредством HTTP запросов к серверам API Я.Метрики, каждому типу операции соответствует свой HTTP метод:
- GET: чтение содержимого
- POST: добавление ресурса
- PUT: изменение ресурса
- DELETE: удаление ресурса
Например, для получения списка счетчиков надо обратиться к ресурсу counters:
GET /counters
. Для получение информации об одном счетчике, обратиться к ресурсу по его идентификатору: GET /counter/{id}
. Для удаления — обратиться к тому же ресурсу, но используя метод DELETE: DELETE /counter/{id}
.Входные данные для REST вызовов и результаты могут кодироваться в двух форматах: XML и JSON. В дальнейшем мы будем использовать JSON, как более компактный и удобный для отображения на структуры, использующиеся в языках программирования, формат.
Естественно, работа через API возможно только со счетчиками, к которым есть доступ из своего аккаунта Я.Метрики. Для идентификации владельца аккаунта используется OAuth авторизация. Устроена она очень просто. Разработчик, желающий воспользоваться API Я.Метрики, регистрирует свое приложение (см. инструкции) и получает идентификатор приложения. Зарегистрировав приложение, можно получить для него отладочный OAuth-токен и сразу же начать работу с API Метрики. Отладочный токен разрешит работу со счетчиками, доступными из аккаунта разработчика, зарегистрировавшего приложение. Токен надо передавать в каждом HTTP запросе, или как дополнительный параметр в URL (
...&oauth_token=<acces_token>
), или как заголовок в HTTP запросе Authorization: OAuth <access_token>
.Чтобы приложение могло работать с произвольными счетчиками, а не только с доступными разработчику, нужен отдельный OAuth токен для каждого пользователя приложения. Как его можно получить:
- Наиболее безопасен способ, когда пользователь перенаправляется приложением на специальную страницу Яндекса, авторизуется на ней, и (если еще не сделал этого) дает приложению доступ к своим счетчикам. После этого пользователь перенаправляется обратно в приложение, причем в URL-е, на который идет перенаправление, содержится параметр с OAuth токеном для этого пользователя. Приложение должно прочитать этот параметр и запомнить токен. Детали этой процедуры описаны в руководстве.
- Приложения также могут получить токен, непосредственно запросив у пользователя логин и пароль, передав их OAuth серверу Яндекса, и получив токен в ответ. Этот способ менее безопасен: логин и пароль запрашиваются и передаются в явном виде.
Благодаря использованию REST, простейшая работа с API (запросы на чтение) возможна непосредственно из браузера, без какого-либо кодирования. Например, этот запрос выдаст информацию о счетчике в демо-аккаунте API Я.Метрики. Такой запрос выдаст отчет по посещаемости счетчика.
Несмотря на простоту REST интерфейса, для полноценного использования API в программе, написанной на языке высокого уровня, необходимо совершить довольно много телодвижений: формирование URL-ов запросов, отправка HTTP запросов и получение результатов, формирование и парсинг JSON, обработка ошибок и т.д. Чтобы упростить жизнь, я создал готовую библиотеку для работы с API Я.Метрики на языкe Java: Metrika4j. Код библиотеки распространяется под Apache License, вы можете свободно изменять и дополнять его под ваши требования. Далее я расскажу, как с помощью этой библиотеки создать widget для Android устройств, отображающий посещаемость сайта.
Metrika4j
Metrika4j позволяет работать с Яндекс.Метрикой, оперируя привычными Java разработчику понятиями (классы, вызовы функций, типы) и не задумываясь о низкоуровневой работе с HTTP и JSON. Центральный интерфейс библиотеки — MetrikaApi. В нем есть методы для работы с главными сущностями Метрики: счетчиками и отчетами. Работа с дополнительными сущностями (цели, фильтры и т.п.) вынесена в отдельные мини-API, получаемые из главного класса
MetrikaApi
.Структура методов примерно соответствует структуре REST-вызовов API Я.Метрики. Аргументы, передаваемые в REST-вызов, соответствуют аргументам, передаваемым в методы API (кроме API работы с отчетами, о котором мы расскажем отдельно).
Metrika4j поддерживает «из коробки» две библиотеки для работы с JSON: Jackson JSON processor и org.json. Для Jackson поддерживается 100% функциональности, для org.json — минимум, достаточный для работы с отчетами: чтение списка счетчиков и получение отчетов. Библиотека org.json встроена в Andriod API, поэтому её использование удобно для Android приложений. При необходимости, разработчик может воспользоваться любой другой JSON-библиотекой, имплементировав с её помощью интерфейсы JsonMapper и JsonObject.
Использование Metrika4j
Сначала необходимо создать экземпляр
MetrikaApi
c помощью ApiFactory. При создании надо передать OAuth-токен пользователя:// Создаем экземпляр API, использующий демо-токен API Метрики и Jackson JSON processor
MetrikaApi api = ApiFactory.createMetrikaAPI(
"05dd3dd84ff948fdae2bc4fb91f13e22", new JacksonMapper());
Далее с помощью созданного экземпляра можно выполнять любые операции:
// Получаем список счетчиков в текущем аккаунте
Counter[] myCounters = api.getCounters();
// Создание счетчика
Counter newCounter = new Counter();
newCounter.setSite("mysite.ru");
newCounter.setName("Мой сайт");
Counter createdCounter = api.createCounter(newCounter);
// В createdCounter содержится новый счетчик, загруженный из Метрики, со всем значениями полей,
// проставленными Метрикой, например Id
System.out.println(createdCounter.getId());
// Удаление счетчика
api.deleteCounter(createdCounter.id);
Работа с отчетами
Через API доступны почти все отчеты, существующие в Метрике. Набор доступных отчетов содержится в классе Reports. Для построения выбранного отчета надо вызвать метод
MetrikaApi.makeReportBuilder(Reports report, int counterId)
, который вернет специальный объект — построитель отчета ReportBuilder. В построителе отчета надо задать требуемые параметры отчета (временной интервал, сортировку и т.п.). Когда все параметры будут установлены, вызывайте метод ReportBuilder.build()
, который отправит запрос HTTP запрос к API Метрики и вернет отчет.// Создаем построитель отчета "популярное содержимое" для счетчика с id=2138128
ReportBuilder builder = api.makeReportBuilder(Reports.contentPopular, 2138128);
// Задаём параметры отчета (отчет за неделю) и строим отчет
Report report = builder.withDateFrom(MetrikaDate.yesterday())
.withDateTo(MetrikaDate.today())
.build();
Отчет возвращается в виде объекта Report, представляющего собой таблицу с результатами, плюс некоторая дополнительная информация (итоги, интервал дат, к которым относится отчет и т.п.). Каждая строка таблицы результатов — объект ReportItem, данные из которого можно получить одним из методов
getXXX(String fieldName)
(аналогично получению значений из ResultSet
при использовании JDBC). Имена полей и возвращаемые дополнительные данные надо уточнять для каждого отчета в документации на API Яндекс.Метрики.// Вытаскиваем результаты из отчета
ReportItem[] items = report.getData();
for (ReportItem item : items) {
System.out.printf("pageViews: %4d, url: %s", item.getInt("page_views"), item.getString("url"))
.println();
}
Более подробное описание работы с Metrika4j содержится в Javadoc.
Виджет для Android
Имея в своем распоряжении такие мощные инструменты, как API Я.Метрики и Metrika4j, можно решать любые задачи, вплоть до создания альтернативных пользовательских интерфейсов к Яндекс.Метрике. Но в рамках этой статьи мы ограничимся более скромной целью: создадим виджет для Android, который будет показывать текущую посещаемость сайта. Исходный код виджета доступен на GitHub в проекте MetrikaWidget. Так же, как и код Metrika4j, он свободно распространяется с минимумом лицензионных ограничений.
OAuth авторизация
Начнем с создания
MetrikaApi
и авторизации. Приложение работает только с одним аккаунтом, поэтому экземпляр MetrikaApi
можно сделать Singleton-ом. Его код содержится в классе Globals.private static MetrikaApi api;
public static synchronized MetrikaApi getApi(Context context) {
if (api == null) {
// Получаем сохраненный OAuth token из SharedPreferences
String token = getOAuthToken(context);
if (token == null) {
throw new AuthException();
} else {
// Используем библиотеку org.json, встроенную в Android
api = ApiFactory.createMetrikaAPI(token, new OrgJsonMapper());
}
}
return api;
}
Если приложение запускается в первый раз и OAuth token-а еще нет в SharedPreferences, надо получить его. Для этого переходим на URL запроса токена:
Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse("https://oauth.yandex.ru/authorize?response_type=token&client_id=1359488e196b4bfa92615d0885b106d4"));
startActivity(intent);
Для пользователя это будет выглядеть, как открывшаяся страница «Приложение запрашивает доступ к вашим данным на Яндексе». Предположим, что пользователь успешно авторизовался и разрешил доступ. Дальше начинается интересное: как передать токен из веб-интерфейса обратно в наше приложение? Чтобы сделать это, надо зарегистрировать одну из активностей в приложении, как обработчик специфического для нашего приложения протокола (специфического — чтобы наш обработчик не пересекся с другими приложениями). В
AndroidManifest.xml
укажем следующее:<activity android:name="ru.metrikawidget.AuthTokenActivity" android:label="OAuth">
<intent-filter>
<action android:name="android.intent.action.VIEW"/>
<category android:name="android.intent.category.DEFAULT"/>
<category android:name="android.intent.category.BROWSABLE"/>
<data android:scheme="metwd" android:host="oauthtoken"/>
</intent-filter>
</activity>
Мы зарегистрировали AuthTokenActivity, как обработчик протокола «metwd» для псевдохоста «oauthtoken». Теперь достаточно указать Callback URI
metwd://oauthtoken
в настройках зарегистрированного приложения, и после успешной OAuth-авторизации пользователя будет вызываться AuthTokenActivity
. В этой активности нужно распарсить полученную строку и сохранить токен в SharedPreferences:String fragment = getIntent().getData().getFragment();
String[] parts = fragment.split("\\&");
for (String part : parts) {
if (part.startsWith("access_token=")) {
String token = part.substring(param.length(), part.length());
// Запоминаем полученный токен в preferences
getSharedPreferences(Globals.PREF_FILE, 0)
.edit()
.putString(Globals.PREF_TOKEN, token)
.commit();
}
}
Получение данных
Код получения данных для виджета предельно прост:
MetrikaApi api = Globals.getApi(context);
// Собственно запрос к Metrika API
Report report = api.makeReportBuilder(Reports.trafficSummary, counterId)
.withDateFrom(new MetrikaDate())
.withDateTo(new MetrikaDate())
.build();
return new Result(report.getTotals().getInt("visits"));
Нюанс в том, что этот код нельзя выполнять «в лоб». Сервера API Метрики отвечают довольно быстро, а вот сам HTTP запрос может доооолго идти до сервера и обратно по медленным и ненадежным каналам мобильной связи. В результате виджет, ожидающий ответ, «зависнет» с точки зрения Android OS, и будет выдано окошко, предлагающее аварийно завершить подвисшее приложение. Это явно не то, что нам нужно. Поэтому все запросы к API Метрики выполняются асинхронно, с помощью класса AsyncTask. Вот упрощенный код загрузки списка счетчиков (полная версия — в классе WidgetSetupActivity):
private class CountersLoadTask extends AsyncTask<Void, Void, Counter[]> {
private ProgressDialog progressDialog;
protected void onPreExecute() {
progressDialog = ProgressDialog.show(...);
}
protected void onPostExecute(Counter[] counters) {
progressDialog.dismiss();
counterList.addAll(Arrays.asList(counters));
// Уведомляем адаптер, чтобы он перерисовал список счетчиков
listAdapter.notifyDataSetChanged();
}
protected Counter[] doInBackground(Void... voids) {
return Globals.getApi(WidgetSetupActivity.this).getCounters();
}
}
Для виджетов работа с данными немного сложнее. Начнем с того, что виджет-провайдер, получающий события «пора обновить виджет», в терминологии Android является broadcast receiver-ом. Жизненный цикл broadcast receiver-а короток: он обрабатывает событие, после чего немедленно умирает. Если стартовать из обработчика события поток, то смерть receiver-a может наступить раньше, чем закончит работу поток: печаль. Android developer guide настоятельно рекомендует пользоваться для таких случаев сервисами. Так и сделаем: виджет-провайдер (MetrikaWidgetProvider) получает события и передаёт их для обработки в UpdateService.
UpdateService
, в свою очередь, использует асинхронную загрузку данных через AsyncTask
(в противном случае опять получим окошко «Application Not Responding» при длительном ожидании ответа от API).Отображение виджета
Работа с виджетами в Android — отдельная большая тема, выходящая за рамки этой статьи и хорошо освещенная как в руководстве разработчика Android, так и в дополнительных статьях. Поэтому расскажу кратко, а за деталями отошлю к исходному коду классов
MetrikaWidgetProvider
и UpdateService
.Виджет может находиться в трех состояниях: «данные получены», «обновление данных» и «нет связи». Обновление виджета происходит по таймеру или при клике на виджет.
- Данные получены — штатное состояние виджета, в котором он отображает количество визитов на сайте за сегодняшний день. В этом состоянии виджет отображает стандартную столбчатую диаграмму — «радугу», которую видно в шапке интерфейса Яндекс.Метрики
- Обновление данных — промежуточное состояние, сделанное исключительно для удобства пользователя, чтобы он получил визуальный feedback от клика на виджет. Виджет переходит в него перед отправкой запроса к API Метрике, и выходит после завершения запроса. В этом состоянии отображается инвертированная «радуга»
- Нет связи — состояние, указывающее на то, что актуальных данных получить не удалось. Отображается «радугой» с уменьшенной насыщенностью, почти серого цвета.
Если виджет может находиться в состоянии «нет связи», логично было бы обновлять его при появлении этой самой связи, чтобы пользователь сразу увидел актуальные данные. Для этого виджет-провайдер подписывается на системные события, связанные с изменением статуса сетевого интерфейса:
<receiver android:name="ru.metrikawidget.MetrikaWidgetProvider">
<intent-filter>
<action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
<action android:name="android.net.conn.BACKGROUND_DATA_SETTING_CHANGED"/>
<action android:name="android.net.conn.CONNECTIVITY_CHANGE"/>
</intent-filter>
...
</receiver>
При получении такого события происходит проверка, появилась ли связь, и вызывается обновление:
if (intent.getAction().equals(ConnectivityManager.CONNECTIVITY_ACTION)) {
// Изменилось состояние подключения к Internet
NetworkInfo info = intent.getParcelableExtra(ConnectivityManager.EXTRA_NETWORK_INFO);
if (info != null && info.isConnected() && !info.isRoaming()) {
// Если появилась связь, обновляем виджеты, которые могли быть в состоянии "offline"
...
}
}
Установка виджета
Виджет может быть собран из исходного кода, находящегося в проекте MetrikaWidget.
Вы не Android-разработчик, но хотите пользоваться виджетом? Нет проблем — можно загрузить готовый виджет. Релиз с Android Market или debug build c GitHub.
После установки у вас также появится приложение «Я.Метрика», которое на самом деле является мини-инструкцией к виджету.