...

четверг, 17 сентября 2015 г.

[Из песочницы] Обзор Intent Sender плагина к Android Studio и отдельных особенностей его разработки

При разработке Android приложений зачастую приходится прибегать к помощи adb для оптимизации своей работы и экономии времени. Adb является единственным инструментом, позволяющим, к примеру, миновать длинную цепочку activity и запустить сразу нужную или отправить broadcast сообщение без ожидания выполнения каких-либо внешних условий. Использование adb влечет за собой все неудобства применения терминала для формирования длинных команд, что и привело меня к написанию плагина для ускорения и упрощения работы с adb при отправке intent на устройства.
Найти информацию и подробные описания, как разработать плагин к IntelliJ IDEA, не проблема, поэтому в конце статьи я уделю несколько абзацев данной теме, показав особенности, с которыми я столкнулся. Сейчас же хочется описать результат своей работы.

Итак, Android Intent Sender завязан на следующие команды adb shell:

  • Broadcast – использование команды позволяет отправлять broadcast сообщения на клиент
  • StartActivity – запуск activity
  • StartService – запуск сервиса

При отправке данных команд, можно приложить intent, снабженный всеми необходимыми параметрами (action, data, category, component, mime type, flag) и экстра данными.

Внешний вид плагина было решено оформить в виде отдельного рабочего окна с необходимыми UI элементами. Доступ к плагину осуществляется через меню View -> Tool Windows, иконку слева-снизу экрана и из правой панели:

plugin general image

Окно плагина разделено на следующие основные секции: окно выбора устройства / эмулятора, секция параметров intent, кнопки отправки команд. В плагине реализовано автоматическое обновление устройств при их подключении и отключении. В секции параметров intent помимо полей для ввода расположена кнопка истории (правее заглавия секции), пикер классов текущего проекта рядом с полем ввода компонента, пикер флагов и кнопка добавления экстра данных.

Пикер классов текущего проекта значительно упрощает выбор компонента (к примеру, запускаемая activity). По нажатию на кнопку откроется окно с классами проекта с возможностью поиска и переключения на дерево проекта. При выборе класса автоматически будет подставлен в нужное место необходимый разделитель “/”для границы package (например com.weezlabs.blabla/.MainActivity):

class picker image

Пикер флагов позволяет выбрать один или несколько флагов, добавляемых к intent:

flags picker image

Для отправки команды на устройство используются кнопки “Send Broadcast”, “Start Activity”, “Start Service”.

Для запуска activity и сервиса через adb в общем случае необходимо, чтобы в manifest для данного компонента стоял флаг android:exported=”true”. Однако, как оказалось, есть решение, позволяющее запустить компонент, у которого флаг exported стоит в значении false. Для этого в плагин добавлено поле “Add user”, в которое автоматически подставляется package id при выборе компонента из пикера. К сожалению, на ряде устройств данная функциональность может не работать (похоже, что это касается большинства устройств от Samsung).

Рассмотрим некоторые случаи возможного использования плагина:

  1. Тестирование broadcast receiver
    Тут все просто: указываем необходимые параметры и отправляем intent. К примеру, у нас есть receiver с кодом в onReceive:
    @Override
    public void onReceive(Context context, Intent intent) {
            String action = intent.getAction();
            if (action.equals("simple_intent_action")){
                    Log.d("SimpleReceiver", "Got intent with action: " + action + " and string extra: " + intent.getStringExtra("string_extra"));
            }
    }
    
    

    Receiver регистрируем, указав соответствующий IntentFilter:
    IntentFilter simpleFilter = new IntentFilter("simple_intent_action");
    
    

    Тогда для отправки broadcast сообщения в плагине в поле action указываем «simple_intent_action» и, нажав на кнопку «Add extra», добавляем extra с ключом “string_extra” и любым текстом. Нажимаем “Send Broadcast” и видим в логах нечто, вроде:
    Got intent with action: simple_intent_action and string extra: Awesome string
  2. Запуск activity, сервиса
    Предположим, нам необходимо запустить MessageActivity, при этом MessageActivity при создании забирает id отображаемого сообщения типа long из intent. Ключ в экстра данных для id пускай будет «messageId».

    Для запуска данной активити в плагине в пикере компонентов выберем MessageActivity, и убедимся, что автоматически добавился user. Далее добавим экстра объект типа long с ключом “messageId” и нужным значением. Все готово, нажимаем “Start Activity” и смотрим на запустившуюся activity с отображением указанного сообщения.

    Если MessageActivity в методе onCreate записывает логи:

    Log.d(LOG_TAG, "Message id: " + getIntent().getLongExtra("messageId", -1));
    
    

    то мы увидим в логах:
    Message data: 234
  3. Тестирование собственной схемы данных
    К примеру, нам необходимо внедрить автоматический запуск приложения по клику определенных ссылок, вида http://myhost.com/... / Для этого в manifest мы укажем для нужной activity следующий intent filter:
    <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="http" android:host="myhost.com"/>
    </intent-filter>
    
    

    В onCreate метод вызываемой activity добавим логирование:
    Log.d("TAG", "Intent data: " + getIntent().getDataString());
    
    

    Тогда, для тестирования, в плагине указываем action: android.intent.action.VIEW, data: myhost.com/some_data и нажимаем Start Activity. На устройстве выбираем наше приложение и смотрим логи:
    Intent data: http://ift.tt/1FiCvdk
    Тоже самое мы увидим, если отправим себе смс с текстом “Тест myhost.com/some_data” и нажмем на ссылку
  4. Тестирование Google Cloud Messaging (GCM) сервиса
    Предположим, у нас есть некий класс GcmBroadcastReceiver который отвечает за получение GCM сообщений. Регистрация подобных компонентов в manifest проходит с указанием для receiver разрешения android:permission="com.google.android.c2dm.permission.SEND" .
    К сожалению, на время тестирования, данное разрешение необходимо убрать, иначе intent из плагина не обработается. Фильтр в manifest будет подобным следующему:
    <intent-filter>
                    <action android:name="com.google.android.c2dm.intent.RECEIVE" />
                    <category android:name="com.your.package" />
    </intent-filter>
    
    

    Предположим, что в теле GCM intent мы ожидаем json с некоторыми данными, передаваемый строкой с ключом “message”.
    Сформируем intent в плагине, для чего укажем action com.google.android.c2dm.intent.RECEIVE и категорию com.your.package. Добавим экстра поле типа String c ключом “message_type” и значением “gcm” и поле типа String с ключом “message” и передаваемым json внутри.

    Теперь, по нажатию “Send broadcast”, приложение получит и обработает broadcast, как оно обрабатывало бы реальное GCM сообщение.


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

Относительно непосредственно процесса разработки плагина могу сказать, что сложность определяется сложностью реализации самой идеи, вложенной в плагин. Подготовка и непосредственно настройка окружения проблем не вызывает, в интернете имеется достаточно много информации. Приведу вкратце необходимые шаги по настройке окружения:

  • Ищем ветку IDEA, используемую для AndroidStudio. Для этого ищем строку в AndroidStudioApplicationInfo.xml, подобную apiVersion=«135.1286» и забираем из нее номер ветки – в моем случае 135. AndroidStudioApplicationInfo.xml смотрим по ссылке: http://ift.tt/1FiCvdm
    Естественно, ссылка не жестко рекомендуемая и вы можете перейти в нужную версию AndroidStudio, урезав ссылку до http://ift.tt/1FiCxlg и далее, погуляв по папкам, легко можно найти нужный AndroidStudioApplicationInfo.xml
  • Качаем IntelliJ Community Edition согласно описанию и переключаемся на ветку, найденную ранее (у меня — 135)
  • Создаем SDK для разработки плагина
  • Предпоследний шаг: добавляем в SDK в секцию classpath зависимости android плагина:
    image
  • Добавляем в plugin.xml зависимость <depends>org.jetbrains.android</depends>

Далее создаем новый проект типа IntelliJ Platform Plugin и можно спокойно начинать писать плагин, используя IntelliJ Platform SDK Documentation, Open API and Plugin Development community, многочисленные статьи на хабре и примеры на github.

Плагин мной было решено реализовать на базе toolWindow компонента с использованием swing фрэймворка для UI элементов. Как человеку, ни разу не столкнувшемуся с работой с UI в Java и со swing в частности, пришлось слегка адаптироваться под его особенности, однако особых трудностей не возникло. Возможно, помогло сходство с работой над UI в Android.

Дополнительно отмечу, для работы с adb, крайне полезной оказалась библиотека ddmlib, входящая в состав Android SDK и расположенная по пути ${SDK_HOME}/sdk/tools/lib. В данной директории находится достаточно много других jar-ов, так что рекомендую просмотреть, вдруг какая-либо библиотека упростит решение поставленных задач. Также обращаю внимание на пакет org.jetbrains.android, значительно помогающий при работе с Android частью. Так, при его помощи был получен путь к директории Android SDK и вытащен package из manifest. На всякий случай в плагине предусмотрен также поиск пути к Android SDK в переменной окружения ANDROID_HOME и возможность ручного задания пути, если на предыдущих шагах найти SDK не удалось. При работе с adb в плагине проводится парсинг ответа консоли, благодаря чему удается в некоторых случаях понять, что отправка не удалась и выдать соответствующую ошибку.

Для хранения истории команд используется json, хранимый в настройках плагина.

В связи с разработкой плагина в Windows среде, при его запуске на OS X возник ряд случаев вылета с NoSuchMethodException. Причина – различие Java для Windows и для OS X. Пришлось беспокоить коллег, работающих на OS X и проводить отладку с их помощью.

После завершения тестирования и отладки плагина, была подана заявка на добавление его в репозиторий плагинов jetbrains.Первая поданная версия быстро прошла модерацию и была доступна для скачивания не более чем через час после подачи заявки.

Заключение:


К моему удивлению, написание плагина не вызвало никаких значительных технических сложностей. Быть может, помогло желание сделать открытый полезный инструмент, переключение с Android разработки или просто пришедшее вдохновение, однако так или иначе разработка плагина дала хорошую моральную встряску и зарядила на некоторое время дополнительным позитивным настроем. Хоть и плагин уже самостоятелен и вполне работоспособен, работа над плагином еще не завершена, есть что улучшить и есть идеи для других полезных плагинов. Todo лист для плагина:
  • Добавить возможность инициирования сборки и запуска проекта с указанием запускаемой activity и ее параметров
  • Попытаться обойти временное удаление разрешения у GCM receiver
  • Реализовать поддержку передачи в intent Parcelable объектов
  • Решить проблему запуска не exported компонентов на устройствах от Samsung

Плагин на github: http://ift.tt/1FiCxlk
В качестве баг трекера можно смело использовать github issues: http://ift.tt/1FiCxlm.

Буду рад любым предложениям по улучшению плагина или вопросам по его реализации, а также пополнению списка не очевидных способов использования.

This entry passed through the Full-Text RSS service - if this is your content and you're reading it on someone else's site, please read the FAQ at http://ift.tt/jcXqJW.

Комментариев нет:

Отправить комментарий