Warning: Undefined array key 0 in /var/www/tgoop/function.php on line 65

Warning: Trying to access array offset on value of type null in /var/www/tgoop/function.php on line 65
107 - Telegram Web
Telegram Web
Довольно часто в коде приходится работать с условиями. Представьте такое условие:

if (!isEmpty && !hasProduct && !(!isFeatureEnabled || !userAuthorized)) {
doSmth()
} else {
doElse()
}

Какие проблемы у этого кода?

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

val canWeDoSmth = !isEmpty && !hasProduct && (!isFeatureEnabled && !userAuthorized)

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

val canWeDoSmth = !(isEmpty || hasProduct || (isFeatureEnabled && userAuthorized))

Можете проверить логика осталась без изменений. Согласитесь такое условие читать намного проще, несмотря на то, что мы не избавились от всех отрицаний. Как такое получилось?

Есть такая теорема в дискретной математике, называемая теорема де Моргана. Определение у нее следующее: "Отрицание конъюнкции есть дизъюнкция отрицаний, а отрицание дизъюнкции есть конъюнкция отрицаний". Звучит страшно, однако концепция супер простая.

Конъюнкция это операция "И" (в коде &&), дизъюнкция – операция "ИЛИ" (в коде ||), ну и отрицание это операция "НЕ" (в коде !). Дальше все просто:

!(A && B) = !A || !B
!(A || B) = !A && !B

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

Вот и все, применить это правило на наше условие и мы получаем вместо кучи отрицаний всего одно. Простой метод который позволяет избавляться от лишних отрицаний, довольно эффективен в разработке. Если следующий раз когда будете писать условие вам покажется что оно получается сложным, вспомните про это метод.
👍401
Как система ловит ANR?
👍12
Недавно я задался вопросом, а каким образом система понимает, что приложение зависло? Для начала вспомним что такое ANR.

ANR (Application Not Responding) или до словно приложение не отвечает, вы возможно даже сами у себя на устройстве ловили такой диалог. Диалог этот системный, система его показывает в своем отдельном процессе и повлиять мы на него не можем.

В доке гугла написано, что система выкидывает ANR в двух случаях:
👉 приложение активно и UI поток или BroadcastReceiver не отвечает на события в течении 5 сек
👉 приложение в фоне то и BroadcastReceiver завис на примерно 60 сек

Каким же образом система понимает что приложение зависло и нужно показать диалог? Чтобы это понять нужно представлять как работает связка сущностей Looper + Handler + MessageQueue писал про это тут. Надеюсь вы прочитали или просто вспомнили, как эта штука работает. Дальше все просто у нас есть отдельный поток, который делает примерно следующее:

fun run() {
while(true) {
tick += ANR_TIMEOUT
uiHandler.post(specialRunnalbe)
thread.sleep(ANR_TIMEOUT)

if(tick != 0) {
showANR()
}
}
}

Бесконечный поток который постоянно отправляет в Looper главного потока specialRunnalbe. Этот Runnable обнуляет переменную tick. Если все хорошо и Looper главного потока не забит и нет блокирующих вызовов, то очередь до specialRunnalbe дойдет довольно быстро. Если же есть зависание, то Runnable не выполнится и система кинет ANR.
🔥17👍98
Всем привет!

Я планировал сделать этот пост когда на канале будет 100 подписчиков. Немного опоздал, так как это произошло быстрее, чем я рассчитывал🎉.

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

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

Хотелось бы в паре слов рассказать что это за канал.

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

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

Ну и стандартное, буду очень признателен, если вы расскажете про этот канал своим друзьям/знакомым/коллегам. Всем мир🕊
👍46🔥94
Фрагменты, фрагменты, фрагменты.
👍3
Скорее всего все и так уже примерно знают что такое фрагмент. Все слышали про историю с появлением планшетов, разделение UI и бла бла бла. Грубо говоря можно представить, что фрагмент это просто такая сложная View со своим состоянием и жизненным циклом очень похожим на жизненный цикл Activity.

Как и View фрагмент не может существовать сам по себе, он может существовать только в рамках Activity или другого фрагмента (который естесна живет в Activity). Жизненный цикл фрагмента сильно завязан на ЖЦ Activity, если у Activity был вызван onStop, значит и у всех фрагментов тоже был вызван onStop. Не может быть такого чтобы у Activity статус pause, а фрагмента stop все абсолютно синхронно.

У Activity может быть много фрагментов. Они могут отображаться на экране разделяя его пополам, могут быть наложены друг на друга, короче как угодно. В этом плане фрагменты гораздо гибче Activity которая может быть только одна на экране. Из-за такого удобства и возникают подходы вроде Single Activity или Ribs о которых я тоже скоро сделаю посты.

Все это и так можно было прочитать в доке, ничего нового я вам тут не расскажу. Однако при всей своей кажущейся простоте есть нюансы работы с ними. Например: фрагмент для оптимизации, может убить View, но сам фрагмент при этом не умрет, или что фрагмент может жить, но не быть привязан к Activity. Помимо этого если Activity создает сама система, с фрагментами история немного другая. Из-за всех этих нюансов возникают вопросы:

👉 Как правильно показать первый фрагмент?
👉 Как правильно передать данные в этот фрагмент?
👉 Почему так много методов чтобы закомитить транзакцию фрагмента?
👉 Что такое Back Stack и зачем про это знать?
👉 Что будет если показать фрагмент когда Activity в статусе onStop?
👉 Может ли быть фрагмент без View?

Все эти моменты важны в работе, на некоторых можно подорваться, про них спрашивают на собесах и про них и поговорим в следующих постах.
👍218
Как правильно показать первый фрагмент? Много текста, но я чуть шизу не поймал, пока пытался придумать как показать этот процесс😄
👍12😁51🤔1
Как я уже говорил Fragment это в некотором смысле усложненная View. Однако добавить фрагмент на Activity мы не можем просто вызвать view.addView(SomeFragment()). Fragment гораздо сложнее и для добавления фрагмент в Activity мы используем Fragment Manager.

Fragment Manager – как понятно из названия полностью отвечает за работу с фрагментами. Он умеет восстанавливать фрагменты, сохранять их стейт, передавать события ЖЦ от Activity, показывать новые, все что связано с фрагментами.

У Activity есть свой Fragment Manager, и у фрагмента тоже есть свой Fragment Manager. Фрагменту нужен свой fragment manager, потому как мы можем и внутри фрагмента показывать другие фрагменты, например DialogFragment.

Значится есть два стула основных способа как показать первый фрагмент:

👉 Через верстку
👉 Через код

С методом через верстку все довольно очевидно. Просто в наш layout через специальный тег встраиваем наш фрагмент. Плюс в том, что Activity сама полностью позаботится о том как правильно запустить фрагмент и как правильно восстановить его состояние при пересоздании. Недостаток этого способа в том, что можно показать только ограниченное количество фрагментов и вы не управляете этим процессом. Чаще всего такой способ используется для какой-нибудь статики.

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

override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)

if (savedInstanceState == null) {
supportFragmentManager.beginTransaction()
.replace(R.id.container, MainFragment())
.commit()
}
}

Как я уже упомянул, Fragment Manager умеет восстанавливать фрагменты при смерти Activity. Отсюда вытекает вот это условие с savedInstanceState == null. Потому как если Activity пересоздается, то Fragment Manager сам восстановит фрагмент и нам не нужно делать эту транзакцию.

Это важно потому как, если мы заново сами создадим фрагмент, то потерям сохранённое состояние фрагмента. Пользователь вводил какие-то данные в EditText, потом случайно перевернул телефон, а мы эти данные потеряли. Спасибо за такое он явно не скажет.
👍37🔥1
В комментариях в прошлом посту меня попросили прикладывать участки кода в виде картинок, так как на телефоне они читаются максимально всрато. К сожалению телеграм в канале не позволяет вставлять картинки в середину поста, и в целом это правильно потому что канал в телеграмме это не медиум и не про это.

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

Поэтому ловите, мини статейку про то, как правильно передавать данные во фрагмент.
👍30
Почему так много методов чтобы закоммитить транзакцию?
👍15🔥5
Вспомним что такое вообще транзакция. Само по себе понятие транзакции это некоторый набор действий который либо выполняется полностью, либо не выполняется. Это свойство назвается атомарность.

Фрагменты мы добавляем/удаляем/меняем через транзакции. В одной транзакции можем заменить текущий фрагмент и одновременно показать поверх него новый. Все эти действия произойдут одновременно. Как и полагается транзакции эта цепочка действий либо выполняется полностью, либо мы упадем. Это отличается от работы с Activity, где это было бы три отдельных действия.

Далее вы могли заметить что есть два метода чтобы закоммитить транзакцию: commit и commitNow. В чем разница? В том, что когда делаем commit, мы эту транзакцию отправляем в Looper главного потока, а когда делаем commitNow транзакция происходит вот прямо сейчас синхронно.

Чтобы это понять есть простая аналогия. Когда вызываем просто commit, транзакция встает в очередь. Когда вызываем commitNow эта транзакция превращается в чела который: "Мне просто спросить".

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

Также есть такой метод как executePendingTransaction, который просто выполняет все транзакции посланные в Looper. Метод блокирующий, это значит, что при его вызове, все транзакции которые были посланы в Looper и находятся в очереди, забьют на эту очередь и выполнятся сейчас. Отсюда выходит, что если вызвать сначала commit, а потом следующей строкой executePendingTransaction, то это аналогично простому вызову commitNow.
👍285
Что такое Back Stack?
👍21
Для начала вернемся к транзакциям. Помимо атомарности у транзакций есть еще одно свойство. Грубо говоря это свойство можно назвать откатываемостью. Другими словами, допустим совершается транзакция, в которой есть набор действий меняющие состояние. Откатываемость позволяет полностью откатить эту транзакцию, что значит вернуть состояние системы в изначальное. Зная это свойство идем дальше.

Что такое Back Stack? Это просто стэк в котором лежат сохранённые транзакции, стэк позволяет их откатывать. Чтобы понять, как это работает, рассмотрим как закрываются Activity. Когда нажимаем на кнопку назад то верхняя Activity убивается и мы возвращаемся к предыдущей. Сохранение транзакций в Back Stack позволяет по нажатию на эту кнопку откатывать транзакции.

Работает это так, при нажатии на кнопку назад Activity сначала у Fragment Manager узнает если что-то в Back Stack. Если есть тогда откатывается последняя транзакция добавленная в Back Stack. Это происходит до тех пор, пока в Back Stack не будет пуст. После этого уже закрывается сама Activity.

Давайте на примере. Вы добавили первый фрагмент FirstFragment() в Activity. Затем в эту же Activity мы добавляем еще один фрагмент SecondFragment(). Однако транзакцию по запуску SecondFragment добавляем в Back Stack. Теперь когда нажимаем на кнопку назад, у нас сначала откатывается транзакция SecondFragment() т.е этот фрагмент удаляется, а после второго нажатия уже закроется вся Activity, так как первый фрагмент мы не добавляли в Back Stack.

Чтобы добавить транзакцию в Back Stack нужно использовать метод addToBackStack(). У функции есть один параметр типа String, это tag транзакции. В большинстве случаев этот tag не используется и просто передается null. Однако можно туда передать строку, чтобы потом можно было откатить все транзакции до транзакции с этим tag. Причем нужно держать в голове, что отказывается вся транзакция. Это значит что если вы в одной транзакции меняете какой-то фрагмент и одновременно показываете что-то поверх, при откате этой транзакции верхний фрагмент удалится, а замена фрагментов произойдет в обратном порядке.

Как уже упоминал выше, Activity сама откатывает транзакции по нажатию на кнопку назад. Помимо это можно откатывать транзакции через специальный метод popBackStack(). Есть два варианта этого метода обычный без параметров и второй с параметром типа String это тот tag транзакции, чтобы откатить все транзакции до этой.

Важный момент. Нельзя вызывать commitNow() с addToBackStack(), потому как транзакция сразу же упадет. Сделано это из-за очевидной проблемы. Когда вызываем commitNow(), эта транзакция минует очередь, а в очереди могут быть транзакции которые меняют Back Stack. Разруливать такое довольно сложно, поэтому очевидное решение просто падать в таком случае.
👍323🔥1
Что будет если показать фрагмент когда Activity в статусе onStop?
🤔14👍3
В предыдущих постах я упоминал что ЖЦ фрагмента полностью завязан на ЖЦ Activity. Однако интересные вещи происходят на стыке этих двух сущностей. Один из интересных вопросов который может возникнуть – что произойдет если закоммитить транзакцию фрагмента в тот момент когда у Activity уже был вызван onStop?

Fragment Manager умеет сохранять состояние фрагментов и делает он это в тот момент, когда Activity сохраняет свое состояние. Но каким образом сохранять состояние когда методы сохранения состояния Activity уже вызваны?

Для этого существует такая ошибка как State Loss Exception которая позволяет понять, что мы закомитили транзакцию после вызова onSaveInstanceState у Activity. Такая ситуация может возникнуть, когда вы показываете результирующий экран после какого-нибудь запроса к серверу.

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

Когда пользователь сворачивает приложение и возвращается назад, он ожидает увидеть экран на котором был. При использовании commitAllowingStateLoss() мы пытаемся показать новый экран когда пользователь уже не в приложении. Во-первых, это плохой UX, во-вторых, это лотерея, потому как я уже сказал выше гарантий никаких нет.

Поэтому лучше всего никогда не использовать commitAllowingStateLoss(), есть куча инструментов тот же Cicerone который позволяет навигироваться между экранами, даже если пользователь свернул приложение. commitAllowingStateLoss() лучше использовать только для экранов, который пользователю не критично пропустить вроде какого-нибудь банера и только в том случае когда вы уверены, что другого выбора нет.
👍322
Как может быть фрагмент без View?
🔥18
Вот это по мне так самый интересный пост за всю эту серию.Есть одна ошибка которую совершают все при работе с фрагментами. Причем, под все я не сильно утрирую, за всю мою карьеру был только один проект, в котором про эту ошибку не забыли.

Начнем с простого, когда создаем фрагмент мы переопределяем метод onCreateView. В этом методе можно просто вернуть null. Это самый простой кейс как может быть Fragment без View. В этом случае немного изменяется ЖЦ фрагмента. В частности не вызывается метод onViewCreated, потому как View не создалась. Может возникнуть вопрос, а зачем это нужно? Для этого окунемся в историю.

Раньше в стародавние времена, не было такой штуки как ViewModel, которая умела переживать смерть Activity. Большие объекты сохраняли либо через статику, которая несет свои сложности, либо через retain фрагмент.

У фрагментов есть такая настройка setRetainInstance, которая позволяет указать нужно ли пересоздавать фрагмент при смерти Activity. Если выставляем retainInstance = true, то при пересоздании Activity фрагмент умирать не будет. Это позволяло использовать такие фрагменты, как сейчас, используются ViewModel. Однако есть важное ограничение. Нельзя делать такие фрагменты с View это может привести к утечкам памяти.

У View всегда есть ссылка на Activity (сейчас не рассматриваем кейс с виджетами), и соответственно у Activity всегда есть ссылка на View которую мы видим на экране. Когда умирает Activity, умирает и View которая к ней привязана. Фрагмент как мы помним это в некотором смысле сложная View. Если в retain фрагменте сохранить ссылку на View в поле этого фрагмента, то при перевороте у нас будет жить ссылка на старую Activity которая умерла: Fragment -> View -> Activity.

Из-за такой возможности легко сделать утечку памяти, а также из-за смешивания View и презентационной логики были сделаны ViewModel. Сейчас настройка retainInstance deprecated и очень не рекомендуется к использованию. retainInstance ушел в прошлое, правда иногда спрашивают на собесах.

Однако это еще не все интересные кейсы где может быть фрагмент без View.

Неочевидно поведение проявляется при транзакции с заменой фрагмента. Начнем с примера, у вас есть FirstFragment в Activity, затем мы делаем транзакцию с заменой фрагмента FirstFragment на SecondFragment. Что при этом происходит с FirstFragment?

Вот тут интересная часть, у него удаляется View, однако сам фрагмент какое-то время продолжает жить. Это сделано для оптимизации. Если нажмем на кнопку назад, то нам нужно будет создать заново фрагмент FirstFragment, а это накладные расходы т.к рефлексия и вот это все. Теперь вдумайтесь, обычный фрагмент, может в некоторых кейсах вести себя очень похоже на retain фрагмент. Да разумеется, он уничтожится при перевороте и тогда уже нет проблем. Однако не факт что этот переворот случится. Это именно тот момент когда можно подорваться и вот каким образом.

Самая распространённая ошибка это сохранение Adapter во фрагменте. Когда вы используете RecycleView вы обязаны создать Adapter чтобы с ним работать. Часто этот Adapter сохраняют в поле Fragment, потому как пересоздавать его каждый раз когда обновляются данные такая себе затея и это правильно. Однако в таком случае важно в onDestroyView зачищать ссылку на этот Adapter у RecyclerView:

recycler.adapter = null


Интересный факт о котором не пишет Гугл – Adapter держит ссылку на RecyclerView. И по факту мы можем оказаться в ситуации когда у нас фрагмент, который ведет себя как retain фрагмент, да еще и с ссылкой на View. Он конечно удалится со временем, однако не понятно когда, да и удалится ли вообще, до того момента, как умрет Activity.

Поэтому просто возьмите за правило – не сохраняйте ссылки на View в поля фрагментов, или очищайте их в onDestroyView, что делает ViewBindingPropertyDelegate. Помимо это затирайте ссылку на Adapter у RecyclerView.
🔥39👍18🤔32
Что не так с Fragment Factory?

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

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

Какую проблему решает FragmentFactory? Единственная проблема которую она решает это внедрение зависимостей во фрагмент через конструктор. Для зависимостей все проекты используют какую-либо DI библиотеку. Вне зависимости от того, существующее это решение для DI или самописное у нас во фрагменте будет завязка на этот DI. Если это Dagger, то будет аннотация inject, если это Koin, то специальные extension функции и т.д. FragmentFactory позволяет внедрять зависимости через конструктор, что означает что сам фрагмент ничего не будет знать про DI.

Это безусловно плюс этого решения. Один из кейсов когда это может понадобиться это когда вы свой фрагмент предоставляете как либу. И чтобы пользователи вашей либы не тянули зависимости на ваш DI можно использовать FragmentFactory. Это обяжет пользователей вашей либы создавать FragmentFactory, но зато предоставит возможность им выбирать с помощью чего внедрять зависимости. Вот один из вариантов через Dagger.

Из минусов этой библиотеки, проблема с передачей аргументов. Посмотрев внимательнее вы заметите, что динамическая информация вроде id по-прежнему передается через bundle. В этом и заключается неудобство. С обычным созданием фрагмента мы делаем метод newInstance который четко говорит какие данные нужны для показа фрагмента. Это четкий контракт который не позволить создать фрагмент без нужных данных. 

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

Помимо этого возникает трудность интеграции с инструментами навигации. Например, тот же Cicerone, обязывает нас передавать именно объект фрагмента, уже с аргументами чтобы его запустить.  Еще из минусов вытекает, что если у вас много фрагментов, то FragmentFactory становится единой точкой у которой есть риск разрастись. FragmentFactory также обязана знать про все зависимости, что может добавить геморроя. 

Конечно проблемы которые я перечислил решаемы, за исключением аргументов. Однако нужно ли вам такое решение, которое помогает решить одну проблему, но создает целый ворох других. Причем решает она проблему, которую уже давно решили. FragmentFactory прикольное решение, однако для очень узкого круга задач.
👍25
Разница между IO и Сomputation
👍10
На данный момент в индустрии есть два варианта как мы работаем с асинхронными задачами. Мы либо используем Rx, либо Coroutines. Разумеется, существуют проекты, где эти технологии не используются, но это либо мелкие либы, либо просто мелкие проекты. На большом проекте без использования библиотеки просто невозможно будет выжить, сложность будет расти экспоненциально.

Что в Rx что в Coroutines есть абстракция, которая позволяет указать на каких потоках нужно выполнять операции. У Rx эта абстракция называется Sсheduler, у Coroutines это Dispatcher. Обе эти абстракции работают практически одинаково, поэтому просто держите в голове что все, что сказано про Sсheduler применимо и к Dispatcher.

Обычно у нас несколько вариантов Sсheduler для разных типов задач. Для похода в сеть или базу данных мы используем IO, а для расчетов используем Computation. Возникает вопрос, а в чем разница? Почему один Sсheduler хорош для одних задач, а другой для других?

Чтобы понять в чем разница, нужно разобраться как работают Sсheduler. Sсheduler это лишь обертка над Executor. Executor сделали для решения одной интересной задачи. Поток очень тяжелая штука по памяти, примерно 1mb, если каждый раз создавать поток, когда нам нужно сделать асинхронную задачу это может привести к OutOfMemory.

В основе Executor лежит простая идея. Если подумать, то очевидно что нам не нужно каждый раз создавать новый поток на каждую задачу. Достаточно переиспользовать имеющиеся, и просто раздавать им задачи. Чем-то такая система напоминает Looper и MessageQueue. Идем дальше, действительно ли много потоков позволят быстрее выполнять программу? На самом деле нет, после некоторого количества потоков, программа наоборот будет работать медленнее.

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

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

Вернемся к нашим задачам. Когда мы идем в базу или сеть мы точно будем ждать пока придут данные, как вы понимаете сеть в рамках выполнения программы довольно медленная штука. Поток в этом случае блокируется и просто ждет данные с сокета, ну или с файла есть речь о базе данных. В таком случае если у нас мало потоков, и много запросов в сеть, мы тупо будем долго ждать, т.к запросы будут идти практически последовательно. Поэтому в IO Sсheduler, а точнее сказать в Executor этого Sсheduler много потоков, из-за блокирующих задач.

Касательно задачи расчета, мы никого не ждем, ни сети ни файловой системы, нам просто нужно быстро что-то посчитать. В этому случае нужно минимизировать переключение контекста между потоками. Именно поэтому в Сomputation Sсheduler наоборот мало потоков. Если точнее количество потоков в Сomputation равно количеству ядер процессора. Это позволяет минимизировать переключение контекста и тем самым ускорить выполнение расчета.

Можно также вспомнить что есть MainThread Sсheduler, который позволяет выполнять задачи на потоке UI. Все, что делает этот Sсheduler, тупо отправляет все задачи в Looper главного потока.

В этом и заключается разница между Sсheduler. Никакой магии и особых потоков. Все максимально просто, разница лишь в количестве потоков.
🔥41👍11
2025/07/13 01:50:11
Back to Top
HTML Embed Code: