Чрезмерно подробное руководство по малоиспользуемым библиотекам Android

Опубликовано: 2022-03-11

Любой опытный разработчик скажет вам, что его лучший код — это не тот код, который он написал. Это код, который они взяли из чужой работы.

Да, мы, разработчики, занимаемся новаторскими решениями проблем, но многие из проблем, с которыми мы сталкиваемся, уже решены, а решения упакованы в библиотеки, доступные каждому. Зачем изобретать велосипед, когда бесплатные колеса есть везде?

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

Но там, где SDK не хватает, сообщество Android создало несколько первоклассных библиотек, которые могут сэкономить вам массу работы по кодированию, заменив ее тщательно настроенными, проверенными и протестированными реализациями. Я не говорю об очевидных библиотеках — библиотеке поддержки Android, библиотеке поддержки дизайна Android, Gson. Я имею в виду инструменты, о которых вы, возможно, не знаете. И даже если вы это сделаете, вы, вероятно, еще не используете их.

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

Я много лет разрабатываю, наставляю и руковожу командами Android, изучил и использовал десятки внешних инструментов и библиотек. (Я даже был известен тем, что читал код их реализации и обсуждал их внутренности с разработчиком.) Многие очень эффективно помогали мне выполнять работу, но правда в том, что большинство из них не помогали.

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

Выбор правильной библиотеки Android

При выборе библиотеки я обращаю внимание на четыре ключевые особенности:

  • Он обеспечивает последовательное и качественное решение реальной и нетривиальной проблемы.
  • Он использует максимально простой API.
  • Это не требует каких-либо изменений в моей общей архитектуре.
  • У него большая пользовательская база и, желательно, активное сообщество разработчиков.

Первые три функции являются нарушителями условий сделки. Если их нет, я иду дальше или начинаю кодить вручную.

Android-библиотеки

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

  • Две библиотеки для внедрения зависимостей, привязки макета к Java, фиктивные объекты.
  • Модель обмена сообщениями в приложении/подписке.
  • Безопасный, эффективный, самовосстанавливающийся уровень связи HTTP.
  • Манипуляции с изображениями: загрузка, кэширование, изменение размера и загрузка в оперативную память.
  • Потоковое видео в реальном времени.
  • Обнаружение утечки памяти.

ButterKnife: лучший инструмент для внедрения зависимостей

Это лучшая библиотека внедрения зависимостей для Android. Простой, надежный, сверхбыстрый (без рефлексии!), и способный избавиться от большого количества стандартного кода вашего приложения.

Кто-то может возразить, что ButterKnife — это то, чем в первую очередь должно было быть сопоставление макета Android с Java.

Внедрение зависимостей Android Butterknife

Исчезла необходимость напрямую связывать каждое из ваших представлений с помощью вызова findViewById() ; вместо этого есть аннотированное представление, которое дает вам прямой доступ к коду. ButterKnife также устраняет необходимость в стандартных событиях пользовательского интерфейса, таких как onClick , onTouch и т. д., и заменяет их автоматически внедряемым кодом.

Но хватит болтовни, давайте посмотрим код.

Привязка поля просмотра:

 class MyButterKnifeActivity extends Activity { @BindView(R.id.name) TextView name; @BindView(R.id.address) TextView address; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.simple_activity); ButterKnife.bind(this); // MUST BE CALLED BEFORE ACCESSING UI FIELDS name.setText(“etc etc”); } }

Привязка ресурсов:

 class ExampleActivity extends Activity { @BindString(R.string.username) String username; @BindDrawable(R.drawable.graphic) Drawable graphic; @BindColor(R.color.bg_color) int bgColor; @BindDimen(R.dimen.lower_padding) Float lowerPadding; // and no need for getResources().getString()/getDrawable/getColor() }

Привязка событий пользовательского интерфейса:

 @OnClick(R.id.my_button) public void clickHandler(View view) { // onClick logic goes here }

AndroidAnnotations: внедрение внедрения зависимостей на новый уровень

AndroidAnnotations занимает второе место после ButterKnife, когда дело доходит до внедрения зависимостей, и использует несколько иной подход: автоматически генерируемые классы, которые, как только вы освоите их, становятся чрезвычайно простыми. Еще более полезным является то, что он позволяет внедрять зависимости «на основе имени». Например, @ViewById ListView myUserList; указывает библиотеке назначить этому полю объект layoutListView с тем же именем.

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

Но возможности внедрения AndroidAnnotations идут еще дальше. Вы можете внедрить в активность как состояние, так и макет.

Реализация аннотаций Android:

 @NoTitle @Fullscreen @EActivity(R.layout.my_layout) public class MyActivity extends Activity { @ViewById ListView customerList; // auto-binded to R.id.customerList @App MyApplication app; // auto-binded to app object @AminationRes Animation fadeoutAnimation; @UiThread void updateUI() { // main thread action } }

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

 new Handler(Looper.getMainLooper()).post(new Runnable() { logic goes here } ); // NO ANNOTATIONS

В AndroidAnnotations все, что вам нужно сделать, это аннотировать вашу функцию с помощью @UiThread, и теперь она всегда будет выполняться:

 @UiThread void updateUI() {..} // WITH ANNOTATIONS

Обратите внимание, что эта аннотация применяется к стандартным классам компонентов Android (действия, службы и т. д.). Но что произойдет, если я также хочу аннотировать свои собственные классы?

Здесь AndroidAnnotations предлагает новую концепцию EBean . Все, что вам нужно сделать, это пометить свой класс как таковой с помощью @EBean , и все готово:

 @EBean public class MyNonComponentClass { @SystemService NotificationManager notifManager; @Bean MyOtherClass dependency; @UiThread void updateUI() { // main thread work goes here } }

EventBus: межкомпонентная коммуникация стала проще

Библиотека EventBus превращает проблему, которая годами преследовала разработчиков Android, в прогулку по парку. Взаимодействие между компонентами еще никогда не было таким простым — используйте простую модель публикации/подписки для связи между любыми двумя частями вашей системы.

Анимация EventBus

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

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

Использование EventBus простое.

а. Создайте классы событий. Лучше всего работать с POJO:

 class NewUserEvent { String fullname; String address; String role; // add getters and setters }

б. Создайте в классе методы обработки событий — любой класс, на который вы хотите подписаться на эти события:

 class MySubscriber { @Subscribe public void newUserHandler(NewUserEvent event) { // handle NewUserEvent } @Subscribe public void newUserHandler(AnotherEvent event) { // handle AnotherEvent } }

Но эй, любой малоопытный разработчик Android остановится и спросит в этот момент: какова модель многопоточности этих обработчиков? И могу ли я заставить обработчик работать вне основного потока, если, скажем, он включает доступ к компоненту пользовательского интерфейса? Хороший вопрос…

По умолчанию все методы обработчика выполняются в рабочем потоке, взятом из пула потоков, который выделяется и поддерживается самой EventBus. Если вам нужен метод обработчика для запуска в основном потоке, разверните аннотации подписки следующим образом:

 @Subscribe(threadMode = ThreadMode.MAIN) public void runOnMainThreadHandler(AnotherEvent event) { … }

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

в. Управляйте жизненным циклом регистрации EventBus вашего класса подписчика, то есть когда он подключается и когда отключается от шины? Разумным потоком регистрации для деятельности будет:

 class MySubscriberActivity extends Activity { @Override public void onStart() { super.onStart(); EventBus.getDefault().register(this); // START RECEIVING EVENTS HERE } @Override public void onStop() { EventBus.getDefault().unregister(this); // NO MORE EVENTS super.onStop(); } }

Вышеизложенное, конечно, просто пример. Вы можете выполнить (не)регистрацию в любом месте по вашему выбору.

д. И, наконец, на самом деле запустить событие:

 EventBus.getDefault().post(new MyEvent(“I'm here”));

Об использовании EventBus нужно знать гораздо больше: многоадресная рассылка событий (поведение по умолчанию), использование закрепленных событий, потоки доставки, приоритеты и многое другое. Но вышеизложенного достаточно, чтобы начать работу с этой простой, но мощной технологией.

OkHttp: Android HttpClient на стероидах

Именно так должен был быть написан Android HttpClient. Очень просто, очень умно. Библиотека OkHttp внутренне заботится о цикле повторных попыток, автоматическом сжатии полезной нагрузки, поддержке Http/2, пуле соединений и кэшировании ответов, чтобы вы могли избежать ненужного доступа к сети.

Анимация библиотеки OkHttp

Использование OkHttp не составляет труда.

HTTP-ПОСТ:

 OkHttpClient client = new OkHttpClient(); MediaType JSON = MediaType.parse("application/json; charset=utf-8"); RequestBody body = RequestBody.create(JSON, json_str); Request request = new Request.Builder() .url(url) .post(body) .build(); Response response = client.newCall(request).execute(); return response.body().string();

ПОЛУЧИТЬ HTTP:

 OkHttpClient client = new OkHttpClient(); Request request = new Request.Builder() .url(urls[0]) .build(); Response responses = client.newCall(request).execute(); String jsonData = responses.body().string();

OkHttp также поддерживает такие полезные функции, как асинхронная сеть, запрос перенаправления маршрута, запрос локального кеша и многое другое. Не стесняйтесь использовать их там, где это необходимо. Большинство разработчиков используют OkHttp как более разумную замену стандартному HTTP-клиенту Android, HttpURLConnection. На самом деле весь этот проект начинался как частный форк для HttpURLConnection.

Мне нравится его надежность — он сразу дополняет ваш сетевой уровень.

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

Пикассо: есть веская причина, по которой Google тоже использует его!

Picasso — это самый простой и надежный способ управления загрузкой, кэшированием, изменением размера и кадрированием изображений.

Это утверждение:

 Picasso.with(context).load(url).resize(50,50).centerCrop().into(imageView)

Сделает это для вас:

  • Подключиться к удаленному URL-адресу.
  • Загрузите изображение.
  • Сохраните его в локальном кэше LRU, которым он также будет управлять для вас.
  • Измените размер исходного изображения перед его загрузкой в ​​память.
  • Запустите все вышеперечисленное в пуле потоков, управляемом Picasso.
  • Используйте изображение с измененным размером, чтобы заполнить ваш imageView.
  • Перед любыми будущими запусками проверьте локальный кеш, чтобы убедиться, что сетевое обращение туда и обратно действительно необходимо.

Создание вышеуказанного набора задач потребует много часов работы даже от главного разработчика. И это при условии, что вы все помните. Что делать, если вы забыли, скажем, часть изменения размера?

Анимация библиотеки Android Picasso

Что ж, на среднем устройстве Android приложение получает не более 50–60 мегабайт оперативной памяти, а соотношение пикселей к байтам для большинства устройств Android равно 4. Это означает попытку загрузить 13-мегапиксельное изображение с SD-карты. потребуется 52 мегабайта оперативной памяти. Другими словами, ваше приложение немедленно выйдет из строя.

Это только один пример силы Пикассо. Одна из первых вещей, которую я делаю при оптимизации/отладке устаревшего проекта с интенсивным использованием мультимедиа, — переключаю загрузку всех изображений на Picasso. Вы будете удивлены, как этот простой шаг повлияет на качество приложения.

Одно из самых убедительных свидетельств мощи этой библиотеки: многие из собственных примеров кода Google для Android за последние два года используют Picasso для загрузки изображений.

ActiveAndroid: ORM без накладных расходов на производительность

ORM, сокращение от объектно-реляционного отображения, стало популярным во времена J2EE. Это позволяет вам хранить ваши POJO в базе данных и извлекать их из базы данных без необходимости преобразовывать их в отдельные поля.

ActiveAndroid ORM

Это полезно? Очень даже так, поскольку это позволяет вам написать большую часть вашего приложения без кодирования каких-либо операторов SQL.

Это также очень эффективно. В старые времена платформы ORM в значительной степени полагались на отражение и были печально известны своей медлительностью. Современные платформы, в том числе ActiveAndroid, намного быстрее и в большинстве практических случаев не будут страдать от накладных расходов по производительности по сравнению с кодированием на чистом SQL.

Никаких ручных операторов SQL, никаких потерь производительности!

Применение:

а. Инициализируйте в объекте приложения, расширив пользовательский класс приложения:

 public class MyApplication extends extends com.activeandroid.app.Application { … }

б. Создайте POJO, полученный для класса модели, с классами для каждой записи, которую вы планируете хранить в базе данных. Каждый такой POJO может находиться в своей собственной таблице. Аннотации следует использовать для указания имени полей БД для каждого хранимого элемента:

 @Table(name = "Categories") public class UserDetails extends Model { @Column(name = "Name") public String name; @Column(name = "Address") public String address; @Column(name = "Age") public int age; }

Если вы хотите установить индекс для члена, используйте следующую аннотацию:

 @Column(name = "ID", index = true) public String userID;

в. Чтобы библиотека не перебирала все самое классное время запуска, что является поведением по умолчанию, настоятельно рекомендуется указать все классы вашей модели в следующем разделе манифеста:

 <meta-data android:name="AA_MODELS" android:value=“com.myapp.MyModelA, com.myapp.MyModelB" />

Примечание. Классы моделей, не указанные в этом списке, не будут распознаваться ActiveAndroid.

д. Запись в базу данных:

 UserDetails usr = new UserDetails(); usr.save(); // RUNS ON A BACKGROUND THREAD

Если требуется несколько операций записи, более эффективным способом было бы объединить их в одну транзакцию:

 ActiveAndroid.beginTransaction(); try { for (UserDetails u: userList) item.save(); ActiveAndroid.setTransactionSuccessful(); } finally { ActiveAndroid.endTransaction(); }

е. Чтение POJO из базы данных:

 new Select() .from(UserDetails.class) .where("name = ?", usr.getName()) .orderBy("Age") .executeSingle();

ORM был обязательным инструментом, когда я работал серверным разработчиком. У него был несколько поздний вход в домен Android. Но, наконец, вот оно: программирование баз данных настолько просто, насколько это возможно. Наслаждайся этим.

LibStreaming: безболезненная потоковая передача видео

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

Анимация библиотеки libStreaming

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

В двух словах, это упрощает потоковое видео.

Чтобы использовать его для H.264 и AAC, вам нужно сделать следующее:

а. Инициализируйте объект сеанса в методе onCreate вашего основного действия. Объекты сеанса представляют потоковую передачу мультимедиа узлу:

 protected void onCreate(Bundle savedInstanceState) { mSession = SessionBuilder.getInstance() .setCallback(this) .setSurfaceView(mSurfaceView) .setPreviewOrientation(90) .setContext(getApplicationContext()) .setAudioEncoder(SessionBuilder.AUDIO_NONE) .setAudioQuality(new AudioQuality(16000, 32000)) .setVideoEncoder(SessionBuilder.VIDEO_H264) .setVideoQuality(new VideoQuality(320,240,20,500000)) .build(); mSurfaceView.getHolder().addCallback(this); }

б. Собственно начать сеанс:

 mSession.setDestination(destination_server_url); mSession.start();

в. Остановите сеанс, когда закончите:

 mSession.stop();

Теперь, пожалуйста, не поймите меня неправильно. Потоковая передача в реальном времени по своей природе беспорядочна, и libStreaming не избавляет от этой сложности. Тем не менее, он действительно хорошо скрывает это от вас большую часть времени. В некоторых случаях вам придется столкнуться со сложностью, например, при выборе политики одноранговой сигнализации, выборе кодирования камеры (обычно вы хотите использовать MediaCodec/surface-to-buffer) или при работе с пакетированием.

Тем не менее, вы обнаружите, что хорошие ребята, создавшие libStreaming, сделали все возможное, плавно объединив эти сложности в простой в использовании API.

LibStreaming поддерживает большинство кодировщиков, используемых приложениями Android, включая H.264, H.263, AAC и AMR.

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

LeakCanary: обнаружение утечек памяти в строке кода

Начнем с мотивации этой библиотеки: утечки памяти . Приложения для Android подвержены им, особенно если вы не очень осторожны с кодированием. На самом деле создать утечку памяти очень просто. Все, что вам нужно сделать, это сохранить ссылку на активность вне ее контекста. На самом деле, даже сохранение ссылки на отдельный объект представления вне контекста его активности создаст утечку.

Почему? Потому что представление — фактически все представления — внутренне хранят контекстную ссылку на содержащую его активность. Пока ссылка на представление сохраняется, содержащаяся в нем активность — вместе с тем, что находится внутри нее, включая рисуемые объекты, иерархию представлений и ресурсы — не может быть утилизирована сборщиком мусора.

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

Утечки, конечно, не уникальны для Android, но, поскольку это мобильная система с ограниченными ресурсами памяти, воздействие более непосредственное.

Утечки ссылки на одно ресурсоемкое действие иногда бывает достаточно, чтобы приложение вышло из строя с исключением «Недостаточно памяти ».

Библиотека LeakCanary

Как вы можете защититься от них? Начните, конечно, со строгих практик кодирования . Но не все из нас являются опытными разработчиками Android, и даже опытные разработчики иногда забывают правила.

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

Использование инструмента памяти DDMS — отличный способ со временем узнать, есть ли утечка в вашем приложении. Вы должны обязательно использовать его. Однако он не скажет вам, что вызывает утечку.

На помощь приходит утечка Canary. Это лучший из существующих детекторов утечек памяти, и он обеспечивает автоматическое обнаружение утечек для всех ваших действий — одной-двумя строками кода.

Чтобы использовать его, просто инициализируйте leakCanary с помощью объекта вашего приложения onCreate() :

 public class MyApp extends Application { @Override public void onCreate() { super.onCreate(); LeakCanary.install(this); // more initialisations } }

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

LeakCanary достигает этого волшебства, автоматически внедряя объект с именем ActivityRefWatcher во все ваши действия и отслеживая количество их ссылок после onDestroy() . Счетчик ссылок > 0 для уничтоженной активности может означать только утечку.

Важно: Обнаружение утечек работает только для приложений в режиме отладки. Никогда не тестируйте на наличие утечек (ну, не с помощью LeakCanary) APK в режиме выпуска.

Но что, если я хочу проверить другие части моей системы на наличие утечек? Здесь LeakCanary предлагает объект с именем refWatcher, который фактически является возвращаемым значением вызова инициализации:

 refWatcher = LeakCanary.install(this);

Его можно использовать для наблюдения за ценностями, которые вскоре будут восстановлены. Точнее, ценности, которые, я думаю, скоро будут возвращены. Для этого звоните:

 refWatcher.watch(my_soon_to_be_reclaimed_obj);

Библиотека сообщит вам, если этот объект не был освобожден вскоре после вызова наблюдения.

Я нигде не смог найти значение этого «короткого времени», но это, наверное, не так важно. С LeakCanary все просто работает. Бесценный.

Резюме

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

Подводя итог, вот что моя подборка библиотек для Android может сделать для вас:

  • ButterKnife — Автоматически внедряемый код поможет вам избавиться от большей части шаблонного кода вашего приложения. Это лучшая инъекция кода для Android. Нужно ли мне сказать больше?

  • AndroidAnnotations — используйте молниеносно быстрые автоматически сгенерированные классы и внедрение кода на основе имен, чтобы сэкономить время без потери производительности по сравнению с логикой, написанной вручную.

  • EventBus — разделение компонентов для более надежного кода, взаимодействие между компонентами никогда не было таким простым.

  • OkHttp — умная замена HttpURLConnection с поддержкой асинхронной сети, запросом перенаправления маршрута, запросом локального кэша и многим другим.

  • Picasso — оптимизированная обработка изображений, которая настолько хороша, что теперь используется Google. Это значительно экономит время в крупных медиа-проектах и ​​некоторых устаревших проектах.

  • ActiveAndroid — ORM упрощается без снижения производительности.

  • LibStreaming — потоковое видео в реальном времени, используемое основными потоковыми приложениями.

Это единственные библиотеки Android, которые стоят вашего времени? Конечно нет. Но я обещаю вам следующее: использование любого из них в вашем следующем проекте сделает вас намного лучшим разработчиком. Если вы хотите увидеть их в действии, загляните на мой GitHub.

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

Связанный: Android 7.0 для разработчиков: новые функции, повышение производительности и другие вещи, о которых вы не будете заботиться