ВКонтакте Android SDK

    Решили поделиться своими наработками по реализации ВКонтакте API для Android. Данное SDK позволяет быстро авторизоваться и вызывать методы API, например отправить запись на стену, получить список друзей пользователя и многое другое. Изначально библиотека разрабатывалась как ядро проекта Kate Mobile. Совсем недавно пришла идея оформить часть кода в ощедоступную библиотеку. На скорую руку вынести её в отдельный проект и теперь она доступна всем желающим. Сейчас реализованы основные методы API, пополняем по мере необходимости. Будем улучшать на основании полученного фидбэка. Если он будет.

    SDK опубликовано на гитхабе https://github.com/thest1/Android-VKontakte-SDK. Распространяется по лицензии MIT. Вы можете её использовать в любых проектах, в том числе коммерческих. Можете её модифицировать. Ссылку на первоисточник желательно размещать по возможности.

    Быстро понять принципы работы библиотеки вам поможет демо-проект AndroidVkSdkSample. В нём реализована авторизация и, в качестве примера, отправка записи на свою стену.

    Чтобы подключить библиотеку к своему проекту, нужно выполнить следующие шаги:

    1. Импортировать проект AndroidVkSdk в Eclipse. AndroidVkSdk реализован как Android Library Project.
    2. Добавить в своём проекте ссылку на AndroidVkSdk.
    3. Убедиться что у вашего приложения есть разрешение на доступ в Интернет

      <uses-permission android:name="android.permission.INTERNET"/>
      

    4. Создать url авторизации и открыть его в WebView

      String url=Auth.getUrl(API_ID, Auth.getSettings());
      webview.loadUrl(url);
      

      Здесь в качестве API_ID должен быть указан ID вашего приложения, созданного на vk.com/developers.php
    5. Дождаться когда webview будет перенаправлен на Auth.redirect_url. Распарсить redirect_url чтобы получить из него access_token. Для примера смотрите как это сделано в LoginActivity.java в проекте AndroidVkSdkSample.

      String[] auth=Auth.parseRedirectUrl(url);
      

    6. Создать объект Api для выполнения запросов к серверу ВКонтакте:

      API api=new Api(access_token, API_ID);
      

    7. Теперь можно выполнять запросы к серверу, например так:

      api.createWallPost(user_id, text, null, null, false, false, false, null, null);
      


    Метки:
    Поделиться публикацией
    Похожие публикации
    Комментарии 45
    • +1
      А как сохранить логин/пароль, чтобы не заставлять пользователя каждый раз его вводить?
      • +1
        access_token многоразовый, достаточно сохранить его.
        • +1
          Использовать android.accounts.AccountManager было бы гораздо удобнее, чем писать свой велосипед с хранением токена
          • +1
            Эм… Я не говорил ничего о конкретной реализации, но какой бы она не была хранить токен всё равно придется, вообще то. По ссылке подробнее.
            • +2
              Всё правильно, достаточно сохранить токен и пользоваться им сколько угодно. Где хранить дело ваше. Можно в AccountManager, можно в SharedPreferences. Учтите, что если вы сохранили пароль в AccountManager, то вам придётся запретить установку приложения на SD. Аккаунт удаляется из AccountManager если отмонтировать карту. С SharedPreferences такой проблемы нет.
            • 0
              А, понятно. Видел в конкурсе на лучшее приложение упоминание о том, что участникам конкурса будет выдана возможность прямого логина через api без использования webview. Думал, без этого придется каждый раз запрашивать логин/пароль и это убьет сторонние реализации мессенджера.
              • 0
                Не правильно. Токен, полученный через WebView, можно сохранить и пользоваться им сколько угодно. Просить пользователя повторно вводить пароль не требуется.
                • 0
                  Ну да, я уже понял, что неправильно. А еще вопрос — можно залезть внутрь webview и поменять дизайн того, что там есть? js исполнить или как-то так? Чтобы окошко логина не выбивалось из общей стилистики приложения.
                  • 0
                    Вроде можно вообще без webview.

                    В API VK написано что достаточно GET запроса вида:
                    oauth.vk.com/token?grant_type=password&client_id=1914441&...=***&username=***&password=***

                    В результате которого получим:
                    {"access_token":"9d77c727986d7668986d7668049870402D1986d986d76684bbc9b1bf8488de9", "expires_in":0,"user_id":85635407}

                    WebView по идее просто самый простой способ. Хотя может я чего то не знаю. Ждем разработчиков имевших опыт с VK API =)
                    • +1
                      image

                      goo.gl/7F3ZH — вот от сюда.
                      • 0
                        Собственно ответ ниже всё прояснил. В случае отсутствия прямой авторизации, я не думаю что стоит вмешиваться в WebView. Лучше будет пообщаться с саппортом ВК и выпросить доступ, как мне кажется :)
                      • 0
                        Что бы сработал данный способ, у вашего приложения (client_id) должен быть доступ к прямой авторизации. Чтобы получить доступ к прямой авторизации вам нужно написать администрации контакта и попросить этот доступ. Какая политика давать/не давать я не знаю.
                        Без доступа к прямой авторизации этот метод вернёт ошибку.
                        • 0
                          Хм. Понял. Не знал, что прямая авторизация не всем дается =)
                          • 0
                            Раньше нам её ни в какую не давали. Возможно не давали никому кроме оффициальных приложений. Сейчас дают всем участникам конкурса.
                            • +1
                              Дают, кстати, в постоянное пользование, или временно, а потом оставят только победителям?
                              • +1
                                Нам (не участникам конкурса) дали в постоянное пользование. Насчёт остальных не знаю.
            • +7
              > api.createWallPost(user_id, text, null, null, false, false, false, null, null);

              Это же ужасно!

              Не удобнее ли опциональные параметры засунуть в словарь?
              • +3
                Именно ради таких комментариев мы и опубликовались на хабре. Спасибо! Будем внимательно анализировать все разумные комментарии.
                • 0
                  Вот вам ещё один комментарий. Используйте интерфейс, а не конкретный тип.
                  Bad:
                  ArrayList<Audio>
                  

                  Good:
                  Collection<Audio>
                  
                  • –2
                    The best:

                    public interface Audios {
                    Audio get(int i);
                    int count();
                    }

                    А то попортят коллекцию. Можно конечно Iterable наружу выставить, но с условием что Iterator обернут и не дает вызвать remove();
                    • +2
                      Чтобы не попортили коллекцию лучше использовать
                      Collections.unmodifiableCollection(Collection<? extends T> c),
                      

                      а не плодить лишние классы и интерфейсы, которые пользователям либы надо будет потом учить.
                      • 0
                        А потом вас пользователи попросят добавить возможность искать Audio по Title-у например. И что вы будете делать? В предложеном мною интерфейсе достаточно добавить метод (ну или за-extend-ить) и поправить реализацию (перейти с ArrayList на Hashmap например).

                        Что делать в случае с Collection?
                        • 0
                          Я бы предоставил это сделать пользователю, а не перегружал библиотеку всяким хламом.

                          К тому же, когда код возвращает Вам коллекцию, Вы уже знаете что с ней можно сделать, и без проблем это сделаете оптимальным, для какого-то конкретного случая, способом, а не будете пользоваться обобщенным кодом в библиотеке.
                          • 0
                            Простите, а что здесь хлам? Пользователи просят, значит им это нужно. Более того, могут запросто поменяться требования и вы вынуждены будете добавлять свойства к коллекции.

                            Насчет внешних навесов — в случае с поиском, например, коллекцию нужно будет _заново_ проиндексировать, хотя это совершенно не нужно если использовать внутри hashmap и дать наружу метод searchByTitle.

                            Кстати, раз вы уж прицепились к коллекциям. Custom коллекции часто нужны потому что коллекция уже сама является Business level Entity. Например представим что у вас есть две сущности: Playlist и AudioUploadQueue. В обоих случаях можно представить в виде Collection<Audio>. Но! При этом из кода совершенно нельзя будет сказать что это за сущность, если не видеть как она используется. Более того, никто не запретит использовать upload queue вместо Playlist и наоборот. CustomCollection лишает пользователей API такой возможности.

                            Все становится гораздо более печально если начинают использовать List<String>, Map<String, String>, Map<String, List<String>> и ему подобные. Нет лучше метода обфуцировать код.
                            • 0
                              Простите, а что здесь хлам?

                              Хлам это то, что делает что-либо комбайном, в котором есть всё, кроме того что нам нужно.

                              хотя это совершенно не нужно если использовать внутри hashmap и дать наружу метод searchByTitle.


                              Добро, допустим Вы так сделаете, и
                              пользователи попросят
                              искать по битрейту. Вы придумаете костыль, который должен будет поддерживать и старую функциональность и новую. Вам придется сменить коллекцию и её
                              нужно будет _заново_ проиндексировать

                              Из-за этого решение уже будет не оптимальным.

                              Опыт подсказывает что лучше сделать «худощавую» или «голодную» библиотеку, в данном случае просто обертку над API, чем делать так чтобы в ней «всё было» и тащить весь ненужный функционал.

                              Я не эксперт в программировании для мобильных устройств, но, как мне кажется, это особо актуально для мобильных приложений. Поправьте если ошибаюсь.
                              • 0
                                Если будет 100500 вариантов поиска, то будет и отдельный контроллер который будет искать, но не коллекция. Внутри API, out-of-scope of API — дело десятое на самом деле.

                                >чем делать так чтобы в ней «всё было» и тащить весь ненужный функционал.

                                Тащить «все» не нужно. Нужно сделать минимальное API, дать пользователям и прислушиваться к тому что они хотят.

                                Для мобильных приложений все равно впринципе. Если код не вызывается, то максимум что он потребляет — это память под codebase в RAM и на диске. На крайняк можно resource-consuming фичи делать отключаемыми (если пользователю они прям щас не нужны, он может их отключить).
                    • 0
                      Спасибо, сделали.
                  • +1
                    Что значит в словарь? Пользователь же не будет знать, что именно можно передавать через этот словарь. Сейчас выглядит страшно, зато в подсказке видно смысл параметров.
                    • +1
                      Ну словарь, хеш-таблица, как еще оно там в джаве называется. А насчет «пользователь не будет знать» — так документировать надо.

                      If it's not documented, it doesn't exist. Вот так.
                      • +1
                        хм не обязательно словарь, как вариант, думаю вполне можно сделать разные перегрузки функции.
                        • +1
                          Промахнулся с комментарием:

                          Согласен, перегрузки функции + JavaDoc.
                    • 0
                      Согласен, перегрузки функции + JavaDoc.
                      • 0
                        Да, перегрузки вариант хороший. Но думаете стоит их добавлять в такой простой библиотеке? Получится что у нас многие методы будут в пяти экземплярах. Не усложнит ли это чтение и понимание кода библиотеки.
                      • 0
                        Спасибо, как раз скоро конкурс
                        • 0
                          А попробуйте получить авторизацию, например при включенном vpn, уверяю вас вы очень сильно удивитесь, уже 15 версия библиотеки для контакта и не в одной не видел обработки этого момента.
                          Да и тонкостей там еще миллион, например токен на сутки (есть вариант бессрочного, но в доках его не нашел), хотя это не дело библиотеки, конечно.
                          • 0
                            Чтобы токен был бессрочным, нужно при авторизации указать право доступа offline. Насёт VPN не знаю, не пробовали. Ну и, если честно, для нас это не критично потому что из наших 50000 пользователей пока никто не пожаловался на такую проблему.
                          • 0
                            Слишком много ручной работы при авторизации: WebView, куки, разбор ответа. В каждом проекте клиентскому коду необходимо выполнять все то, что у вас находится в LoginActivity в примере. Почему бы не вынести все это в вашу библиотеку? В качестве примера смотрите facebook android sdk, где для авторизации вызывается один метод с необходимыми параметрами и колбеками, а библиотека берет на себя всю работу по открытию WebView в диалоге и прочую нудную работу. Хорошей фичей facebook android sdk также является то, что он может использовать нативное приложение facebook для авторизации, если оно установлено. Это тоже можно добавить в вашу библиотеку.

                            А пока что слишком сложно, ищу альтернативы.
                            • 0
                              А просто под ява это будет работать?
                              • 0
                                Авторизация сделана на основе Android WebView, так что SDK привязано к Android. Насчёт просто java, думаете есть смысл?
                              • 0
                                SDK оказалось чрезвычайно удобным, спасибо. Сэкономил массу времени)
                                Интересно, проект еще развивается? А то есть у меня пара мыслей для push-request'ов…
                                • 0
                                  Конечно развивается. Наполняется новыми методами. Если будете что-то предлагать помните, что оно должно быть в виде, который будет удобен всем пользователям.
                                • 0
                                  А вконтакт в вашим АПИ не позволяет реализовать классическую схему oAuth авторизации?
                                  я например попробовал сделать вот так
                                  Auth.redirect_url = «appName://appName»;
                                  String url = Auth.getUrl(Constants.API_ID, Auth.getSettings());
                                  this.startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse(url)));

                                  чтобы открылся стандартный браузер, а потом в нем был редирект, который перехватит уже мое приложение.

                                  вконтакт выдает ошибку, на описаный выше код :(
                                  • 0
                                    А как вы отловите редирект, произошедьший в браузере? Мы для этого используем WebView внутри нашего приложения. Посмотрите наш пример, так как раз реализована OAuth авторизация.
                                    • 0
                                      Андроид то как раз и позволяет отловить такой редирект

                                              <activity
                                                  android:name="maratische.юююю"
                                                  android:label="@string/app_name" 
                                                  android:exported="true">
                                                  <intent-filter>
                                                      <data android:scheme="myAppName" android:host="myAppName"/>
                                                      <action android:name="android.intent.action.VIEW" />
                                      
                                                      <category android:name="android.intent.category.DEFAULT" />
                                                      <category android:name="android.intent.category.BROWSABLE" />
                                      
                                                  </intent-filter>
                                              </activity>
                                      


                                      будет перехвачен редирект на myAppName://myAppName
                                      • 0
                                        Да, действительно позволяет. Но принимает ли контакт такой специфический redirect_url неизвестно. Мы не пробовали.
                                        Такие детали наверное удобнее обсуждать на github.

                                  Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.