В прошлых статьях (раз и два) была описана разработка мобильного приложения со всеми любимым VK в роли бесплатного и услужливого сервера. Как это часто случается со всякими хитровыкрученными системами, рано или поздно они обрастают мелкими, но досадными проблемами, затрудняющими их использование. И как только это случается, журнал «Хакер» снова приходит на помощь :).

 

Версия VK API

При вызове какого-либо метода нужно специально указывать версию API в параметре v — например, последнюю v=5.53. Иначе по умолчанию он ответит тебе версией, созданной еще при Дурове, а именно 3.0. Ее поддержку оставляют из-за очень старых программ, написанных давно, но работающих по сей день. Проблема с версией заключается в разных JSON-ответах сервера. Чтобы не встретиться с новой структурой при работе с методом wall.get , принудительно указывай версию и еще на всякий пожарный — число записей (count). Так API будет вести себя более предсказуемо.

Не забудь про версию API
Не забудь про версию 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-бэкенд хорош в использовании, но несет с собой много трудностей. С нашим журналом ты узнаешь о большинстве из них и всегда будешь готов к праведной борьбе за торжество программерской мысли :).

1 комментарий

  1. Аватар

    riot26

    08.12.2016 в 10:17

    На заметку:
    https://vk.com/wall-1_340410 — ВК 16-го декабря отключат аудио API
    https://habrahabr.ru/post/248725/ — как обращаться к VK API в 25 раз чаще и не получить бан

Оставить мнение