Содержание статьи
В прошлых статьях (раз и два) была описана разработка мобильного приложения со всеми любимым VK в роли бесплатного и услужливого сервера. Как это часто случается со всякими хитровыкрученными системами, рано или поздно они обрастают мелкими, но досадными проблемами, затрудняющими их использование. И как только это случается, журнал «Хакер» снова приходит на помощь :).
Версия VK API
При вызове какого-либо метода нужно специально указывать версию API в параметре v — например, последнюю v=5.53
. Иначе по умолчанию он ответит тебе версией, созданной еще при Дурове, а именно 3.0. Ее поддержку оставляют из-за очень старых программ, написанных давно, но работающих по сей день. Проблема с версией заключается в разных JSON-ответах сервера. Чтобы не встретиться с новой структурой при работе с методом wall.get , принудительно указывай версию и еще на всякий пожарный — число записей (count). Так API будет вести себя более предсказуемо.
Хранение записей со стены
Чтобы получить все записи со стены группы и иметь возможность обновлять данные, проще всего каждый раз выкачивать все записи со стены последовательно. Метод wall.get отдает данные с конца стены; используя смещение (offset), можно добраться до конца. Полное количество записей известно из переменной count ответа.
Каждую запись проще хранить в классе. Вот пример:
public class ProductStore implements Serializable, Comparable<ProductStore> {
private String id;
private String category;
private String subcategory;
private String name;
private String price;
private String annotation;
private String storeName;
private String address;
private String photo;
private boolean selected;
private Date startDate, endDate;
Он реализует два интерфейса: Serializable и Comparable. Первый используется для возможности сохранения всей его структуры целиком как строки в SharedPreferences, а второй — для упорядочивания списка по id . Его реализация:
@Override
public int compareTo(ProductStore productStore) {
return Integer.parseInt(this.getId()) - Integer.parseInt(productStore.getId());
}
Для обновления записей я каждый раз удаляю старую запись из списка (List
mProdList.remove(ps);
mProdList.add(ps);
Так у нас появляется возможность увидеть обновление в записи. Правда, VK дает редактировать новые записи примерно сутки. Позже редактирование отключается и обновление становится недоступно, можно только публиковать заново.
Когда нужно поработать со всем массивом, я запускаю сортировку:
Collections.sort(mProdList);
Загрузка картинок
Для загрузки картинок могут использоваться разные библиотеки: Fresco, Picasso, Universal Image Loader. Я использую Glide, поскольку он поддерживает GIF. Кому-то важна стабильность работы библиотеки, кому-то размер (в том числе количество методов). Я в работе столкнулся со странным поведением Glide при загрузке большого количества картинок в списках: картинки загружались хаотично, а некоторые вообще не отображались.
Поймать ошибку помог слушатель ответов загрузки. Устанавливается он так:
Glide.with(context).load(item.getPhoto())
.diskCacheStrategy(DiskCacheStrategy.SOURCE)
.listener(new RequestListener<String, GlideDrawable>() {
@Override
public boolean onException(Exception e, String model, Target<GlideDrawable> target, boolean isFirstResource) {
if (BuildConfig.DEBUG)
Log.e("IMAGE_EXCEPTION", "Exception " + e.toString());
return false;
}
@Override
public boolean onResourceReady(GlideDrawable resource, String model, Target<GlideDrawable> target, boolean isFromMemoryCache, boolean isFirstResource) {
return false;
}
})
.into(viewHolder.imageView);
В лог тут же свалилось
Exception java.net.SocketTimeoutException: timeout
Диагноз ясен: мобильная сеть плохо получает данные с VK с помощью стандартного компонента работы с сетью.
Выход был в использовании компонента okhttp3. Для его использования нужно просто прописать его в Gradle:
compile 'com.github.bumptech.glide:okhttp3-integration:1.4.0@aar'
Glide сам его подхватит при компиляции и будет использовать. Так стали загружаться все картинки, даже на самой плохой мобильной сети.
Продвинутая работа с Glide
Эта библиотека поддерживает загрузку эскизов изображений. Например, мы можем получить миниатюру GIF-файла и показать ее пользователю, пока загружается основной файл. Нам только нужно знать URL для загрузки картинки.VK API имеет для этого свойство thumb у объекта doc. Добавить в объект записи новое поле, думаю, не составит особого труда, так как он был подробно описан в предыдущих статьях. Теперь нам нужно сделать предзапрос и его результат передать в основную загрузку. Glide сам заменит эскиз на основной документ после его загрузки.
// Запрос на картинку эскиза
DrawableRequestBuilder<String> thumbnailRequest = Glide
.with(context)
.load(item.getThumb());
// Основная загрузка
Glide.with(catsViewHolder.image.getContext())
.load(item.getThumb())
.diskCacheStrategy(DiskCacheStrategy.SOURCE)
.thumbnail(thumbnailRequest)
.into(catsViewHolder.image);
Можно пойти еще дальше и сделать отображение загрузки основной картинки прямо поверх эскиза. Такие трюки доступны не всем, но настоящий хакер сможет разобраться с задачей, тем более что ее в общих чертах уже решил создатель библиотеки.
По рекомендации Балбеса из известного фильма, для тестов с загрузкой я использую кошек.
Получение аватара пользователя
При использовании vk-android-sdk первым делом проверяем, залогинился ли пользователь VKSdk.isLoggedIn. Если да, то нужно запустить метод VKApi.users().get() с дополнительными полями photo_50, photo_100, photo_200
. Если он не вернет нам в поле заглушку вроде -http://vk.com/images/camera_a.gif
, то мы получим от него аватар в разрешении 50 х 50,100 х 100 или 200 х 200. Зависит это от самого аватара пользователя, раньше можно было загрузить и совсем маленькую картинку.
Вот метод, устанавливающий фото и имя пользователя в ImageView и TextView соответственно:
private void setUserToDrawer(final ImageView userImageView, final TextView userHeader) {
if (VKSdk.isLoggedIn())
VKApi.users().get(VKParameters.from(VKApiConst.FIELDS, "photo_50, photo_100, photo_200")).executeWithListener(new VKRequest.VKRequestListener() {
@Override
public void onComplete(VKResponse response) {
VKApiUser user = ((VKList<VKApiUser>) response.parsedModel).get(0);
// Ищем самое большое изображение
String photo = null;
if (!user.photo_200.equals("http://vk.com/images/camera_a.gif"))
photo = user.photo_200;
else if (!user.photo_100.equals("http://vk.com/images/camera_b.gif"))
photo = user.photo_100;
else
photo = user.photo_50;
Glide.with(getApplicationContext())
.load(photo)
.diskCacheStrategy(DiskCacheStrategy.SOURCE)
.into(userImageView);
userHeader.setText(user.first_name + " " + user.last_name);
}
});
}
Еще не конец!
Вместо заключения хочу отметить, что бесплатный VK-бэкенд хорош в использовании, но несет с собой много трудностей. С нашим журналом ты узнаешь о большинстве из них и всегда будешь готов к праведной борьбе за торжество программерской мысли :).