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

Итак, начнем.

Butterknife


Библиотека была разработана компанией Square и сразу же прижилась у разработчиков. ButterKnife был создан на замену findViewById для того чтобы уменьшить и без того раздутые activity:

View someView = (View) findViewById(R.id.someView)

Вы только посмотрите какая длинная строка! А если это поле класса — то это целых две строки:

View someView; //Первая строка
...
@Override 
 protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        someView = (View) findViewById(R.id.someView); //Вторая строка
}

А теперь перейдем к ButterKnife. Вот простой пример кода с ButterKnife:

    @BindView(R.id.someView1) View view1;
    @BindView(R.id.someView2) View view2;
    @BindView(R.id.someView3) View view3;
    ...
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        ButterKnife.bind(this); //Важная строка
    }

Все очень просто: с помощью аннотации @BindView мы говорим какая вьюха нам нужна, и потом главное не забыть про ButterKnife.bind(this);(Так делать нужно в активити, для других мест вроде holder или fragment это делают немного по-другому. Смотрите здесь.

Как добавить к проекту?

Gradle(app module):

  compile 'com.jakewharton:butterknife:8.5.1'
  apt 'com.jakewharton:butterknife-compiler:8.5.1'
или annotationProcessor 'com.jakewharton:butterknife-compiler:8.5.1' с jack

Retrofit 2


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

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

Добавить в gradle(app module):

    compile 'com.google.code.gson:gson:2.7'
    compile 'com.squareup.retrofit2:retrofit:2.1.0'
    compile 'com.squareup.retrofit2:converter-gson:2.1.0'
//если использовать  rxJava то еще и 
    compile 'io.reactivex.rxjava2:rxjava:2.0.1'
    compile 'io.reactivex.rxjava2:rxandroid:2.0.1'
    compile 'com.jakewharton.retrofit:retrofit2-rxjava2-adapter:1.0.0'

Сначала берут сайт для конвертации с json в pojo вроде этого и копируют все классы которые сгенерировались в AndroidStudio:

image

Потом создают интерфейс(Я использую retrofit вместе с RxJava):

public interface IWeatherProvider{
    @GET("/premium/v1/weather.ashx")
    Observable<Model> getWeather(@QueryMap Map<String, String> map);
}

В QueryMap бросают query параметры(например api key). Хотя можно вместо @QueryMap использовать просто Query но тогда для каждого параметра надо прописать свой query.

После этого:

Retrofit retrofit =  Retrofit.Builder()
                .addConverterFactory(GsonConverterFactory.create(gson))
                .addCallAdapterFactory(RxJava2CallAdapterFactory.create())
                .baseUrl(Constants.WEATHER_BASE_URL)
                .build();
...
retrofit.create(IWeatherProvider);

Потом нужно за полнить параметры данными:


        HashMap<String, String> mapJson = new HashMap<>();
        mapJson.put("key", Constants.WEATHER_API_KEY);
        mapJson.put("q", latitude + "," + longitude);
        mapJson.put("num_of_days", "14");
        mapJson.put("date", "today");
        mapJson.put("format", "json");

И вызвать iweatherProvider.getWeather(mapJson);

И наконец:

new CompositeDisposable().add(weatherProvider
                .observeOn(Schedulers.newThread())
                .subscribeOn(Schedulers.io())
                .subscribe(onNext, onError));

В случае если вы не используете RxJava замените в интерфейсе Observable на Call и сделайте

iweatherProvider.getWeather(mapJson).enqueue(callback);

Dagger 2


Создан гуглом. Помогает реализовать паттерн Dependency Injection. С помощью Dagger'а можно круто структурировать проект, что очень хорошо сказывается на читабельности кода и помогает в тестировании(кстати использовать retrofit вместе с dagger'ом само наслаждение). Смысл в том что все классы хотят работать уже с готовыми данными, им нужно лишь получить их, и что-то с ними сделать. Но кто-то должен предоставлять данные. Эту роль на себя берет dagger. Основа Dagger это компоненты, вот один из них:

@Component(modules = {WeatherInfoTaskModule.class})
public interface RetrofitComponent {
    void inject(MainActivity activity);
}

Все компоненты состоят из модулей, в одном компоненте может быть несколько модулей, вот пример модуля:

@Module
public class WeatherInfoTaskModule {

    @Provides
    Gson provideGson() {
        Log.d(TAG, "gson");
        return new GsonBuilder().create();
    }

    @Provides
    IWeatherProvider provideWeather(Retrofit retrofit) {
        Log.d(TAG, "weather");

        return retrofit.create(IWeatherProvider.class);
    }

    @Provides
    Retrofit provideRetrofit(Gson gson) {
        Log.d(TAG, "retrofit");

        return new Retrofit.Builder()
                .addConverterFactory(GsonConverterFactory.create(gson))
                .addCallAdapterFactory(RxJava2CallAdapterFactory.create())
                .baseUrl(Constants.WEATHER_BASE_URL)
                .build();
    }
}

Внутри модулей находятся функции с аннотацией Provides, как следует из аннотации они поставляют данные. В основном эти функции делят на модули по смыслу(к примеру геолокация). И здесь возникает логичный вопрос, ну как это использовать?

Очень просто. В вашей активити помечаете поля которые должны получить данные аннотацией Inject( @Inject IWeatherProvider mWeatherProvider;). Потом создаете свой Application класс:

public class App extends Application {
    private static RetrofitComponent component;
    @Override
    public void onCreate() {
        super.onCreate();
        component = DaggerRetrofitComponent.create();
    }

    public RetrofitComponent getComponent() {
        return component;
    }
}

В MainActivity в OnCreate сделайте следущее:

 
 @Inject IWeatherProvider mWeatherProvider;
...
@Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        ((App) getApplicationContext()).getComponent().inject(this);
        }

P.S. Не забудьте добавить App класс в манифест.

В gradle(app):

buildscript {
    repositories {
        mavenCentral()
    }
    dependencies {
        classpath 'com.neenbedankt.gradle.plugins:android-apt:1.8'
    }
}
apply plugin: 'com.neenbedankt.android-apt'
apply plugin: 'android-apt'
dependencies{
    compile 'com.google.dagger:dagger:2.7'
    apt 'com.google.dagger:dagger-compiler:2.7'
}

в gradle(project module):

        classpath 'com.neenbedankt.gradle.plugins:android-apt:1.8'

Но что если одной из функций на вход тоже нужны данные? В таком случае dagger пробегается по компоненту в поисках функции которая возвращает необходимый класс или тип данных, и когда находит — вызывает.

Что будет если я поменяю название функции? Dagger'у абсолютно все равно на название, его интересуют лишь сигнатуры, поэтому на работу это не влияет.

Студия подсвечивает класс DaggerRetrofitComponent красны. Что делать? Dagger работает на кодогенерации поэтому в таком случает стоит просто сделать rebuild.

Multidex


Рано или поздно разработчик сталкивается с проблемой в 65 тысяч методов. Но начнем сначала. Когда вы хотите установить приложение на телефон вам нужен apk файл. Основа любого apk файла dex файл. Dex файл — это ваши java классы собранные в один файл. Но у dex файла есть ограничение в 65 тысяч методов. И большая часть приложений превышает этот лимит и получает exception при сборке. Для таких случаев создан multidex. В gradle(app module) добавляете:

...
defaultConfig {
...
        multiDexEnabled true
...
 }
...
 compile 'com.android.support:multidex:1.0.1'


Благодаря int00h, я исправлю ранее ошибочную информацию.
Для подключения MultiDex есть 3 способа:

  • Указать в манифесте у application параметр
    android:name="android.support.multidex.MultiDexApplication"
  • Отнаследовать свой класс App от MultiDexApplication
  • Вызвать MultiDex.install(this);
    в методе attachBaseContext(Context) своего класса App


Пост вышел длинным, но зато я надеюсь что хот как-то объяснил. А какими библиотеками пользуетесь вы?

Комментарии (39)


  1. Sie
    01.09.2017 20:06

    Dagger2 — пожалуй самая поллезная библиотека из этого списка.

    Ретрофит — хоршо подходит если надо быстро получтиь данные из сети, наверно быстрее всегоп озволят это сделать


    1. nolane
      02.09.2017 00:43

      Dagger не нужен. Целая библиотека для инициализации полей. В мое время для этого хватало присваивания.


      1. vlad2711 Автор
        02.09.2017 11:18
        -1

        Пускай это прозвучит грубо, но: начнем с того когда было ваше время? Никто никого не заставляет использовать dagger2, и можно по-прежнему просто присваивать — это не запрещено, ведь вряд ли кто-то будет использовать dagger для int k = 0; это по-прежнему делают присваиванием. Но люди сами пришли к тому что им нужна такая библиотека. Большинство проектов которые выходят за грань проектов типа нажал на кнопку и поменялся текст используют dagger ведь в некоторых приложениях десятки, а то и сотни классов, и держать в голове, в конце концов код сильно нагромождается присваиванием и вы сами не поймете что и где и зачем


        1. nolane
          02.09.2017 15:15
          -1

          Аргумент типа "все так делают", конечно, просто супер.


          "код сильно нагромождается присваиванием"


          Не хотел бы я видеть такой проект, в котором инициализация полей занимает так много места по сравнению с остальным кодом.


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


          Про сабж: https://soundcloud.com/leonid-bogolubov/android-dev-2#t=24:00


          1. vlad2711 Автор
            03.09.2017 09:32

            Аргумента «типа все так делают» не было. Dagger вносит в код структуру, которой нету при обычном присваивании.

            Не хотел бы я видеть такой проект, в котором инициализация полей занимает так много места по сравнению с остальным кодом.


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

            И если следовать вашей логике, тогда функции это бесполезный хлам, который никому не нужен, ведь они занимают так много места по сравнению с другим кодом. Но это звучит абсурдно, так как без функций ни одна программа не работает, ведь даже если и есть хоть малейшая вероятность сделать это — потом ни один здравомыслящий человек не полезет туда, так как там нету никакой структуры, и все абсолютно нечитабельно. Тоже самое можно сказать и про классы. Если всунуть весь код с огромной программы в один класс, то мы сильно сократим общее количество строк(вы только представьте себе как это будет «прекрасно»). Но так никто не делает, все хотят видеть структуру(снова будете говорить про аргумент типа «все так делают»?). И Dagger только вносит дополнительную структуру точно также как это делают функции и классы.


            1. nolane
              03.09.2017 16:30

              Ничего не понял из того, что вы сказали.


              Dagger вносит в код структуру, которой нету при обычном присваивании.

              В код структуру вносит программист, а не библиотека.


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

              Вы это вообще к чему?


              Последний параграф не смог распарсить.


              1. vlad2711 Автор
                03.09.2017 16:46

                В код структуру вносит программист, а не библиотека.

                Вы правы, это делает программист, но ему для этого нужно что-то использовать — инструменты вроде классов, функций и всякого прочего, и библиотека тоже может быть этим самым инструментом


          1. bromzh
            05.09.2017 09:41

            Про сабж: https://soundcloud.com/leonid-bogolubov/android-dev-2#t=24:00

            Мда, такие себе спецы там общаются…
            Первый человек говорит: "Даггер — одно из средств реализаций DI, делать Даггер только потому что ты хочешь делать DI — это значит не понимать, как ещё можно сделать DI. Самый тупой вариант — это сделать фабрики. По-сути — это тоже самое, что делает даггер. Делаете локальное поле, и вместо new Service() присваиваете ServiceFactory.create(...)".
            Другой человек говорит, что "во многих проектах гугла используется самописная реализация DI, Map-like. DI на хэшмапе пишется очень-очень быстро..."


            Оба "спеца" сами путают DI и IoC, ведь DI — это одна из реализаций IoC. Первый человек рассказал про ещё один вариант реализации IoC — Factory Pattern, второй — про ещё одну реализацию — ServiceLocator.
            Да, всё это разновидности IoC, но DI принципиально отличается от других реализаций.


            DI можно и нужно придерживаться и без даггера
            Позвольте узнать, как? Не, реализаций DI много, но все они примерно одинаково выглядят. Что вы советуете использовать?


  1. dakatso
    01.09.2017 20:06

    По поводу Butterknife
    1) Вьюхи приходится располагать в самой большой области видимости
    2) Annotation processing не позволяет использовать private модификатор
    3) Необходимо расставлять аннотации @Nullable если вьюха не должна инициализироваться.

    Имея все это, лучше уж findViewById, с 26 api даже кастовать не нужно.


    1. vlad2711 Автор
      01.09.2017 20:36

      В этом вы правы. Но эти недостатки не так серьезны как кажется на первый взгляд, как сказано в этой статье:
      «Предвидя возможные вопросы о нарушении одного из важнейших принципов ООП, а именно — инкапсуляции, отвечу: конечно нарушает. Но сильно ли это может повлиять на ваше приложение? Ведь никто в здравом уме не будет напрямую обращаться к полю класса, а именно views, и менять его состояние. Конечно, могут быть разные ситуации, но это очень плохая практика — напрямую обращаться к полю класса. Для этого есть геттеры сеттеры.»

      Тем более butterknife способен не только на инициализацию вьюх.


  1. int00h
    02.09.2017 09:52
    +1

    Честно говоря, не понимаю зачем нужен этот пост и на кого он рассчитан. Retrofit де-факто стандарт при разработке, а ButterKnife и Dagger2 чуть ли не на каждом углу упоминаются. Полной сути использования данных библиотек пост не раскрывает, а в случае с MutliDex (про который вообще есть оф.документация) еще и неверная информация преподносится.


    Caution: Do not execute MultiDex.install() or any other code through reflection or JNI before MultiDex.install() is complete. Multidex tracing will not follow those calls, causing ClassNotFoundException or verify errors due to a bad class partition between DEX files.

    Для подключения MultiDex есть 3 способа:


    • Указать в манифесте у application параметр android:name="android.support.multidex.MultiDexApplication"
    • Отнаследовать свой класс App от MultiDexApplication
    • Вызвать MultiDex.install(this);
      в методе attachBaseContext(Context) своего класса App`


    1. vlad2711 Автор
      02.09.2017 10:04

      Retrofit де-факто стандарт при разработке, а ButterKnife и Dagger2 чуть ли не на каждом углу упоминаются.


      Но в заголовке так и написано: must have для андроид разработчика. То есть самые используемые библиотеки при разработке, которые есть практически в любом приложении. И я лишь указал базовые штуки, что бы можно было сразу получить результат тем кто только начал знакомство с этими библиотеками, ведь если бы я начал описывать все на что способна каждая библиотека то пост вышел бы слишком длинным, и мало кому хватило бы выдержки дочитать до конца. Возможно позже я набросаю статьи про каждую библиотеку отдельно или вставлю ссылки по которым сам изучал.

      А за Multidex — спасибо. Я впервые познакомился с Multidex когда получил Exception, и нашел это на stackoverflow. Я обновлю информацию.


  1. anyd3v
    02.09.2017 10:11

    1) Вьюхи приходится располагать в самой большой области видимости

    Можно объявлять поля/методы как protected при использовании ButterKnife


    1. vlad2711 Автор
      02.09.2017 10:22

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

      class ExampleActivity extends Activity {
        @BindView(R.id.title) TextView title;
        @BindView(R.id.subtitle) TextView subtitle;
        @BindView(R.id.footer) TextView footer;
      
        @Override public void onCreate(Bundle savedInstanceState) {
          super.onCreate(savedInstanceState);
          setContentView(R.layout.simple_activity);
          ButterKnife.bind(this);
          // TODO Use fields...
        }
      }
      это официальный пример использования библиотеки, и как видно здесь не используют protected, но никто не сказал что это запрещено(как с private).


    1. dakatso
      04.09.2017 07:24

      1) Вьюхи приходится располагать в самой большой области видимости

      Под областью видимости я имел в виду скоуп переменной, то есть где-то в коде мне нужно инициализировать вью и задать ей лисенер, но для этого мне необходимо объявлять ее полем класса.


  1. Mishkun
    02.09.2017 11:48

    В этом списке как-то пустовато. Не хватает RxJava2, AutoValue, Gson/Jackson ну и опционально Moxy.


    1. vlad2711 Автор
      03.09.2017 09:38

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


  1. asmrnv777
    02.09.2017 11:53
    +1

    Зачем нынче нужен ButterKnife, если есть нативный Data Binding, который еще сильнее сокращает код?


    1. Nakosika
      02.09.2017 17:06

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


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


  1. petrovichtim
    02.09.2017 19:21

    1 — не нужен
    2 — норм
    3 — не нужен
    4 — не нужен


    1. rraderio
      02.09.2017 22:47

      А что вы используете?


      1. petrovichtim
        03.09.2017 05:03
        -1

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


    1. vlad2711 Автор
      03.09.2017 08:43

      Аргументируйте. Если насчет butterKnife есть хоть какая-то дискуссия, то retrofit считается самой лучшей библиотекой для создания запросов. А MultiDex вообще не заменим. Насчет MVP — это действительно крутая вещь!


      1. petrovichtim
        03.09.2017 09:04
        -1

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


        1. asmrnv777
          04.09.2017 19:32

          Добавляете несколько Support и Google Play Services библиотек — Profit, 65К достигнуты! :)


          1. vlad2711 Автор
            04.09.2017 19:45

            У меня было точно также. Клиент хотел сделать вроде простенькую приложуху, думал обойдусь без multidex, но клиент захотел геолокацию и тут Google Play Services пробили лимит:)


          1. petrovichtim
            05.09.2017 04:21

            Их сейчас уменьшают, и прогвард их хорошо режет.


  1. rraderio
    02.09.2017 22:26

    не та ветка


  1. cimenatrix
    03.09.2017 09:32

    Для работы с изображениями, куда же без них

    Glide +2.9k методов,
    Picasso 850

    IcePick — генерация onsaveinstancestate

    Dagger не создан гуглом, первую версию писали ребята из Square, dagger 2 форк без использование рефлексии

    apply plugin: 'com.neenbedankt.android-apt'


    все возможности apt уже давно встроены в Gradle plugin (начиная с версии 2.2),
    используется annotationProcessor вместо apt импортировать ничего не нужно


    1. vlad2711 Автор
      03.09.2017 09:36

      Насчет того, что первую версию делала компания Square я знал, но где-то читал что гугл сделал на основе первой версии вторую, но все же информацию обновлю. Библиотекой Picasso пользовался, и странно как не добавил. Насчет apt — спасибо.


  1. Wolframius
    04.09.2017 17:47

    А кто пробовал искать View не по id, а, скажем, через getChildAt()?


  1. jawaharlalnehru
    04.09.2017 17:47

    У ButterKnife есть ещё одна удобная фишка. Если в Android Studio пойти в File -> Settings -> Plugins и там добавить плагин ButterKnife Inspections, то нажав правой кнопкой мыши на activity_main в setContentView(R.layout.activity_main); и выбрав Generate -> Generate ButterKnife Inspections, то можно выбирать вьюшки для инициализации прям из появившегося диалогового окна, и они сгенерируются автоматически. Помимо этого там можно ещё и onClick для них генерировать.


    1. vlad2711 Автор
      04.09.2017 17:48

      У меня на студию установлен плагин butterknife Zelezny можно также генерить


      1. jawaharlalnehru
        05.09.2017 13:59

        Вот блин, точно, плагин называется Android ButterKnife Zelezny. Injections — это про другое. Спутал, поскольку у меня оба стоят


  1. Yazon2006
    04.09.2017 18:04

    Это не маст хев список. Все перечисленные библиотеки очень даже опциональны, за исключением, возможно, Retrofit. ButterKnife вообще перестал использовать с переходом на котлин, чему очень рад. Multidex до 21 апи — зло, которого можно постараться уникнуть, используя например Proguard, который, кстати, must have. Имхо, в этом списке должен быть Glide (или Picasso как альтернатива).


    1. vlad2711 Автор
      04.09.2017 18:06

      Вы правы, про Picasso и Glide я как-то позабыл, насчет того можно ли на котлине и без butterKmife я ничего сказать не могу


      1. Yazon2006
        04.09.2017 18:09

        Оооо, это блаженное ощущение, когда используешь android kotlin extensions. Очень рекомендую! kotlinlang.org/docs/tutorials/android-plugin.html


        1. vlad2711 Автор
          04.09.2017 18:13

          Возможно как-нибудь позже и до него руки дойдут :)


  1. Lietto
    05.09.2017 10:30

    Must Have не мультидекс. Must Have — это ProGuard с самого момента старта проекта и постоянное его дополнение в процессе.