Pull to refresh
2
0
Константин @ConstOrVar

Разработчик мобильных приложений

Send message

Подход, который вы описали, применяем в части проектов. Но не все проекты хотят завязываться на data binding от Google (по разным причинам). Вы правы, что это упростит привязку состояния к определенным элементам отображения. Но к сожалению, есть вещи, ради которых имеет смысл встраиваться в жизненный цикл родительского компонента. Классический пример (есть и другие) — компонент получения местоположения пользователя: пока activity/fragment в состоянии STARTED, нужно подписываться на датчики; если это делать при создании view, то скажется на расходе батареи.
Хочу подчеркнуть, что рассматриваю подход, разобранный в статье, как лишь один из инструментов в арсенале разработчика. Если он избыточен в большинстве случаев, то применять его не стоит. Но есть экраны, где он поможет структурировать код и сделать более читаемым. Поэтому и решил поделиться с сообществом)

Если вы про data binding, который предоставляет Google из коробки, то, насколько я знаю, он не позволяет настроить запуск действий в определенные моменты жизненного цикла (а такое иногда требуется, хоть и не очень часто). Поправьте, если не прав.
Если вы про data binding как концепцию в целом, то тут все зависит от реализации — возможно, где-то по умолчанию есть такая возможность. Буду благодарен за пример.

В статье указаны даты проведения


Mobius 2019 Moscow есть конкретные даты: 8-9 декабря

но на сайте конференции заявлены 7-8 декабря 2019. Подскажите, пожалуйста, какие же даты правильные?

Добрый день. Спасибо за статью!
Есть небольшие замечания по коду, напрямую не относящиеся к содержанию статьи.
В методе mapToNewsViewData(news: List<News>) на каждой итерации создается объект Regex и DateFormat, хотя по сути они для всех одинаковы. В дополнение к этому заполнение итогового списка выглядело бы лучше (читабильнее) при использовании stream подхода. Например, так:


class NewsMapper {
    companion object {
        private val dateFormat = SimpleDateFormat("yyyy-MM-dd HH:mm", Locale("ru"))
        private val regex = "\\.".toRegex()
    }

    fun mapToNewsViewData(news: List<News>): List<NewsViewData> {
        return news.asSequence()
            .map {
                    val textSplits = it.text.split(regex)              
                    NewsViewData(
                        id = it.date.toString(),
                        title = textSplits[0],
                        description = textSplits[1].trim(),
                        date = dateFormat.format(it.date)
                    )
            }
            .toList()
    }
} 
Спасибо за статью. На мой взгляд, один из важных момент при работе с Rx — помнить про подписки и не забывать отписываться от них. В приведенных примерах, как-то про это забыли. У Вас в данном случае создается бесконечный Observable, подписка на который нигде не сохраняется, но от которого нужно отписаться в onDestroy(). Хотелось бы, чтобы пользователи, которые будут читать статью, видели «картину в целом» и следовали хорошим практикам.
Не нешел в статье на сколько по времени рассчитан курс. Уточните, пожалуйста.
Спасибо за статью. В Вашем примере Observable для SearchView будет не совсем корректно работать. Вы на него подписываетесь в onCreate(), но при срабатывании onQueryTextSubmit у Вас произойдет отписка, так как вызовется onComplete. Получается, что повторный поиск не будет работать. Чтобы повторный поиск работал, нужно избавиться от subject.onComplete();
Спасибо за Ваш отзыв. Один такой комментарий уже оправдывает публикацию данной статьи. Рад, что моя идея приглянулась Вам.
Дело тут вовсе не в контексте. Давайте на простом примере. В приложении создан класс, ответственный за общение с сервером. Как правило, чтобы не создавать несколько инстансов такого класса используют либо синглтон, либо создают его в Application (руками или с помощью компонента даггера) и соответственно добираются к инстансу этого класса через Application. В примере выше как раз описывался примерно такой случай.
Поделитесь, пожалуйста, каким образом Вы обходитесь без Апп-синглтона при разработке приложения?
Согласен, что ссылку, сохраненную в onAttach(), нужно чистить в onDetach(), чтобы не было утечки памяти. И в реальном коде это было. Я не стал это копировать сюда, так как это напрямую не относилось к теме статьи.
«Каноническое» означает основанное на вере, что так должно быть. Но разве разработка не подразумевает поиск новых подходов?! Я лишь поделился подходом, а применять его или нет — на усмотрение каждого конкретного разработчика. В любом случае, спасибо за конструктивную критику.
ИМХО, EventBus — не панацея. Я хотел сделать компонент как можно более независимым от разных библиотек, чтобы его можно было переиспользовать в других проектах (где этих библиотек может не быть и добавление их не одобрят).
К тому же, хоть EventBus добавляет гибкости во взаимодействии между компонентами, он также накладывает больше ответственности на разработчиков — чтобы приложение не превратилось в запутанный клубок из событий. Лично я предпочитаю не злоупотреблять рассылкой событий и использовать EventBus только при острой необходимости.
Давайте по порядку. Начнем с сериализации: согласен, что при таком подходе появляются накладные расходы на сериализацию, но, на мой взгляд, сохранение и восстановление одного объекта (не обладающего внутренним состоянием) не приведет к падению производительности. Можно вместо Serializable использовать Pacrelable, если Вы хотите более быстрой сериализации. К тому же, если Android SDK предоставляет возможность сохранять в Bundle сериализуемые объекты, то почему бы нам этим не воспользоваться, а не прикрываться фразами типа «медленная сериализация».

Теперь по поводу «самописного DI». Если Вы внимательно читали статью, то я нигде не призывал отказываться от DI фреймворков, а наоборот предлагаю использовать подход, описанный в публикации, совместно c ними. Давайте возьмем для примера Dagger до появления функционала AndroidInjection, приходилось писать подобное (код с официального сайта):
public class FrombulationActivity extends Activity {
  @Inject Frombulator frombulator;

  @Override
  public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    // DO THIS FIRST. Otherwise frombulator might be null!
    ((SomeApplicationBaseType) getContext().getApplicationContext())
        .getApplicationComponent()
        .newActivityComponentBuilder()
        .activity(this)
        .build()
        .inject(this);
    // ... now you can write the exciting code
  }
}

Я лишь предлагал спрятать создание активити компонента. Примерно так.
interface Provider {
    Builder getBuilder(FrombulationActivity activity);
}

class ProviderImpl implements Provider {
    @Override
    Builder getActivityComponent(FrombulationActivity activity) {
        return ((SomeApplicationBaseType) getContext().getApplicationContext())
            .getApplicationComponent()
            .newActivityComponentBuilder()
            .activity(activity)
            .build();
    }
}

public class FrombulationActivity extends Activity {
  @Inject Frombulator frombulator;

  @Override
  public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    // DO THIS FIRST. Otherwise frombulator might be null!
    Provider provider = getIntent().getExtras().getSerializable(PROVIDER);
    provider.getActivityComponent(this)
        .inject(this);
    // ... now you can write the exciting code
  }
}

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

Information

Rating
Does not participate
Location
Россия
Date of birth
Registered
Activity