Итак, начнем.
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:
Потом создают интерфейс(Я использую 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)
dakatso
01.09.2017 20:06По поводу Butterknife
1) Вьюхи приходится располагать в самой большой области видимости
2) Annotation processing не позволяет использовать private модификатор
3) Необходимо расставлять аннотации@Nullable
если вьюха не должна инициализироваться.
Имея все это, лучше ужfindViewById
, с 26 api даже кастовать не нужно.vlad2711 Автор
01.09.2017 20:36В этом вы правы. Но эти недостатки не так серьезны как кажется на первый взгляд, как сказано в этой статье:
«Предвидя возможные вопросы о нарушении одного из важнейших принципов ООП, а именно — инкапсуляции, отвечу: конечно нарушает. Но сильно ли это может повлиять на ваше приложение? Ведь никто в здравом уме не будет напрямую обращаться к полю класса, а именно views, и менять его состояние. Конечно, могут быть разные ситуации, но это очень плохая практика — напрямую обращаться к полю класса. Для этого есть геттеры сеттеры.»
Тем более butterknife способен не только на инициализацию вьюх.
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 beforeMultiDex.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`
vlad2711 Автор
02.09.2017 10:04Retrofit де-факто стандарт при разработке, а ButterKnife и Dagger2 чуть ли не на каждом углу упоминаются.
Но в заголовке так и написано: must have для андроид разработчика. То есть самые используемые библиотеки при разработке, которые есть практически в любом приложении. И я лишь указал базовые штуки, что бы можно было сразу получить результат тем кто только начал знакомство с этими библиотеками, ведь если бы я начал описывать все на что способна каждая библиотека то пост вышел бы слишком длинным, и мало кому хватило бы выдержки дочитать до конца. Возможно позже я набросаю статьи про каждую библиотеку отдельно или вставлю ссылки по которым сам изучал.
А за Multidex — спасибо. Я впервые познакомился с Multidex когда получил Exception, и нашел это на stackoverflow. Я обновлю информацию.
- Указать в манифесте у
anyd3v
02.09.2017 10:111) Вьюхи приходится располагать в самой большой области видимости
Можно объявлять поля/методы как protected при использовании ButterKnife
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).
dakatso
04.09.2017 07:241) Вьюхи приходится располагать в самой большой области видимости
Под областью видимости я имел в виду скоуп переменной, то есть где-то в коде мне нужно инициализировать вью и задать ей лисенер, но для этого мне необходимо объявлять ее полем класса.
asmrnv777
02.09.2017 11:53+1Зачем нынче нужен ButterKnife, если есть нативный Data Binding, который еще сильнее сокращает код?
Nakosika
02.09.2017 17:06Датабайндинг не всем подходит. Замедляет компиляцию, утекает логику во вьюхи, в принципе билдскрипт становится неоправданно сложнее, что мешает использовать хот релоад и тп.
А вообще, проблема с поиском вьюх сильно надумана. Она никогда больше пяти процентов времени не занимала, соответственно все эти паттерны можно коту под хвост с чистой совестью. Чистота кода важнее.
petrovichtim
02.09.2017 19:211 — не нужен
2 — норм
3 — не нужен
4 — не нуженrraderio
02.09.2017 22:47А что вы используете?
petrovichtim
03.09.2017 05:03-1MVP для архитектуры и то не часто, а там где много классов. Причем без фрагментов, на активити и вьюхах. Сторонние библиотеки стараюсь не тянуть в проект, от этого и мультидекс не нужен.
vlad2711 Автор
03.09.2017 08:43Аргументируйте. Если насчет butterKnife есть хоть какая-то дискуссия, то retrofit считается самой лучшей библиотекой для создания запросов. А MultiDex вообще не заменим. Насчет MVP — это действительно крутая вещь!
petrovichtim
03.09.2017 09:04-1Масляничный нож дает вагон оверхеда и взамен по сути ничего, ретрофит я использую сам почти везде, мульти декс нужен если вам лень писать код самому и вы затащили 100500 сторонних библиотек в проект.
asmrnv777
04.09.2017 19:32Добавляете несколько Support и Google Play Services библиотек — Profit, 65К достигнуты! :)
vlad2711 Автор
04.09.2017 19:45У меня было точно также. Клиент хотел сделать вроде простенькую приложуху, думал обойдусь без multidex, но клиент захотел геолокацию и тут Google Play Services пробили лимит:)
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 импортировать ничего не нужноvlad2711 Автор
03.09.2017 09:36Насчет того, что первую версию делала компания Square я знал, но где-то читал что гугл сделал на основе первой версии вторую, но все же информацию обновлю. Библиотекой Picasso пользовался, и странно как не добавил. Насчет apt — спасибо.
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 для них генерировать.
vlad2711 Автор
04.09.2017 17:48У меня на студию установлен плагин butterknife Zelezny можно также генерить
jawaharlalnehru
05.09.2017 13:59Вот блин, точно, плагин называется Android ButterKnife Zelezny. Injections — это про другое. Спутал, поскольку у меня оба стоят
Yazon2006
04.09.2017 18:04Это не маст хев список. Все перечисленные библиотеки очень даже опциональны, за исключением, возможно, Retrofit. ButterKnife вообще перестал использовать с переходом на котлин, чему очень рад. Multidex до 21 апи — зло, которого можно постараться уникнуть, используя например Proguard, который, кстати, must have. Имхо, в этом списке должен быть Glide (или Picasso как альтернатива).
vlad2711 Автор
04.09.2017 18:06Вы правы, про Picasso и Glide я как-то позабыл, насчет того можно ли на котлине и без butterKmife я ничего сказать не могу
Yazon2006
04.09.2017 18:09Оооо, это блаженное ощущение, когда используешь android kotlin extensions. Очень рекомендую! kotlinlang.org/docs/tutorials/android-plugin.html
Lietto
05.09.2017 10:30Must Have не мультидекс. Must Have — это ProGuard с самого момента старта проекта и постоянное его дополнение в процессе.
Sie
Dagger2 — пожалуй самая поллезная библиотека из этого списка.
Ретрофит — хоршо подходит если надо быстро получтиь данные из сети, наверно быстрее всегоп озволят это сделать
nolane
Dagger не нужен. Целая библиотека для инициализации полей. В мое время для этого хватало присваивания.
vlad2711 Автор
Пускай это прозвучит грубо, но: начнем с того когда было ваше время? Никто никого не заставляет использовать dagger2, и можно по-прежнему просто присваивать — это не запрещено, ведь вряд ли кто-то будет использовать dagger для
int k = 0;
это по-прежнему делают присваиванием. Но люди сами пришли к тому что им нужна такая библиотека. Большинство проектов которые выходят за грань проектов типа нажал на кнопку и поменялся текст используют dagger ведь в некоторых приложениях десятки, а то и сотни классов, и держать в голове, в конце концов код сильно нагромождается присваиванием и вы сами не поймете что и где и зачемnolane
Аргумент типа "все так делают", конечно, просто супер.
"код сильно нагромождается присваиванием"
Не хотел бы я видеть такой проект, в котором инициализация полей занимает так много места по сравнению с остальным кодом.
Я считаю, что даггер любят только те, кто считают, что это синоним DI. Но ведь это не так. DI можно и нужно придерживаться и без даггера. Он, наоборот, ухудшает читаемость кода и к тому же усложняет процесс сборки, не принося никакой пользы.
Про сабж: https://soundcloud.com/leonid-bogolubov/android-dev-2#t=24:00
vlad2711 Автор
Аргумента «типа все так делают» не было. Dagger вносит в код структуру, которой нету при обычном присваивании.
Какая разница где проходит инициализация по всему коду или в одном участке? Если вы вынесите инициализацию каждого поля в отдельную функцию(что делают не редко), то поймете что кода не намного меньше.
И если следовать вашей логике, тогда функции это бесполезный хлам, который никому не нужен, ведь они занимают так много места по сравнению с другим кодом. Но это звучит абсурдно, так как без функций ни одна программа не работает, ведь даже если и есть хоть малейшая вероятность сделать это — потом ни один здравомыслящий человек не полезет туда, так как там нету никакой структуры, и все абсолютно нечитабельно. Тоже самое можно сказать и про классы. Если всунуть весь код с огромной программы в один класс, то мы сильно сократим общее количество строк(вы только представьте себе как это будет «прекрасно»). Но так никто не делает, все хотят видеть структуру(снова будете говорить про аргумент типа «все так делают»?). И Dagger только вносит дополнительную структуру точно также как это делают функции и классы.
nolane
Ничего не понял из того, что вы сказали.
В код структуру вносит программист, а не библиотека.
Вы это вообще к чему?
Последний параграф не смог распарсить.
vlad2711 Автор
Вы правы, это делает программист, но ему для этого нужно что-то использовать — инструменты вроде классов, функций и всякого прочего, и библиотека тоже может быть этим самым инструментом
bromzh
Мда, такие себе спецы там общаются…
Первый человек говорит: "Даггер — одно из средств реализаций DI, делать Даггер только потому что ты хочешь делать DI — это значит не понимать, как ещё можно сделать DI. Самый тупой вариант — это сделать фабрики. По-сути — это тоже самое, что делает даггер. Делаете локальное поле, и вместо new Service() присваиваете ServiceFactory.create(...)".
Другой человек говорит, что "во многих проектах гугла используется самописная реализация DI, Map-like. DI на хэшмапе пишется очень-очень быстро..."
Оба "спеца" сами путают DI и IoC, ведь DI — это одна из реализаций IoC. Первый человек рассказал про ещё один вариант реализации IoC — Factory Pattern, второй — про ещё одну реализацию — ServiceLocator.
Да, всё это разновидности IoC, но DI принципиально отличается от других реализаций.