ПодписчикиСколько подписчиков у канала сейчас. Волна — число подписчиков в каждой точке периода. Изменение — к началу периода.550▲ 0,4 %+2 за период
ПросмотрыСколько сейчас просмотров у постов, вышедших за период. Это не просмотры за день: сколько посмотрели вчера, MAX не сообщает. Процент — изменение в среднем за день: нынешний период ещё идёт (сегодня — неполный день), поэтому его сравниваем с прошлым по темпу, а не по сумме. «Было» — прошлый такой же период целиком.0было 0
РеакцииСколько реакций у постов, вышедших за период. Процент — изменение в среднем за день: нынешний период ещё идёт (сегодня — неполный день), поэтому его сравниваем с прошлым по темпу, а не по сумме. «Было» — прошлый такой же период целиком.0было 0
УпоминанияСколько раз канал упомянули другие каналы: ссылки и репосты, без рекламных постов. Чем крупнее круг, тем больше упоминаний в этой точке периода. Процент — изменение в среднем за день: нынешний период ещё идёт (сегодня — неполный день), поэтому его сравниваем с прошлым по темпу, а не по сумме. «Было» — прошлый такой же период целиком.61▼ 19,7 %было 76по подписке
РепостыСколько раз другие каналы сделали репост постов этого канала (без рекламных постов). Репосты входят и в число упоминаний. Процент — изменение в среднем за день: нынешний период ещё идёт (сегодня — неполный день), поэтому его сравниваем с прошлым по темпу, а не по сумме. «Было» — прошлый такой же период целиком.28▲ 12,1 %было 25после входа
Средний охват постаСколько просмотров в среднем набирает один пост. Считаем по постам старше двух суток: у свежих просмотры ещё растут. Точки, где есть свежие посты, — пунктиром и полыми кружками: охват всех постов на сейчас, он ещё вырастет.5 478▲ 1,0 %было 5 421после входа
ERRКакая доля подписчиков видит средний пост: средний охват ÷ подписчики × 100 %. Линия — ERR в каждой точке периода, пунктир — ERR прошлого такого же периода. Точки с постами моложе двух суток — полые кружки на пунктирном конце линии.28,9 %▼ 0,2 п. п.пунктир — было 29,0 %после входа
Вовлечённость ERКак часто читатели реагируют на посты: реакции ÷ просмотры × 100 %. Засечка на полосе — прошлый период.2,84 %▼ 0,2 п. п.
Постов в деньСколько публикаций в среднем выходит за день. Клетка — точка периода: чем темнее, тем больше постов в день. Процент — изменение в среднем за день: нынешний период ещё идёт (сегодня — неполный день), поэтому его сравниваем с прошлым по темпу, а не по сумме. «Было» — прошлый такой же период целиком.4,3▼ 11,0 %было 4,8после входа
Доля рекламыКакая доля публикаций за период похожа на рекламу: маркировка erid, рекламные ссылки и другие признаки. Шкала — рекламная нагрузка по порогам индекса качества: до 10 % — низкая, до 25 % — умеренная, до 50 % — высокая, дальше — очень высокая. Рост доли отмечается красным.3,1 %▲ 0,3 п. п.
ДинамикаСверху — число подписчиков на конец каждой точки периода, снизу — прирост за точку: зелёный — подписчиков стало больше, красный — меньше. Над графиком прироста — самый большой прирост и самая большая убыль. Сколько подписалось и отписалось по отдельности, MAX не сообщает.
Рост аудитории
+2подписчиков за период
+1 самый большой прирост · 25 окт 2026
Оценка каналаОценка канала от 0 до 100 из пяти частей: аудитория, честность роста, рекламная нагрузка, доверие и достоверность расчёта. Полоса — сколько баллов часть набрала из своего максимума. Индекс пересчитывается каждую ночь; каналы сравниваются с похожими — той же категории и размера.
ОхватыСколько сейчас просмотров у постов, вышедших в каждой точке периода (а не просмотры за день). Подписан рекорд; точка с сегодняшним днём бледнее — день ещё идёт. Средний охват поста — по постам старше двух суток.
Просмотры и охват
702 763просмотров за период▼ 10,5 %
Средний охват поста — 5 478 просмотров (посты старше двух суток).
ВовлечённостьER — реакции, делённые на просмотры, в процентах — у постов каждой точки периода. Пунктир — ER за весь период. Изменение — в процентных пунктах к прошлому такому же периоду.
ЦитируемостьИндекс цитируемости (ИЦ) — насколько часто и какими каналами упоминают этот канал за последние 30 дней, без рекламных постов. Уровень — место среди похожих каналов. Линия — ИЦ в каждой точке периода (в неделе и месяце — средний за её дни).
Индекс цитируемости пока не рассчитан: он появится после ночного расчёта, когда канал упомянут другие каналы.
РитмСколько постов в среднем выходит в каждый час каждого дня недели, время московское: чем темнее клетка, тем больше публикаций. «Лучшее время» — часы, в которые у постов старше двух суток самый высокий средний охват.
Когда и сколько публикует
ПнВтСрЧтПтСбВс
036912151821
Лучшее время:09:0018:00часы, в которые у постов самый высокий средний охват
ФорматыИз чего состоит лента. У каждого поста один формат — по старшинству: репост, видео, фото, ссылка, текст. Полоса и проценты — доля постов формата; справа — средний охват одного поста формата (по постам старше двух суток), полоска — сравнение с самым просматриваемым форматом.
ПубликацииКаждая точка — пост за период: по горизонтали — дата выхода, по вертикали — просмотры, размер точки — реакции, цвет — формат. Так видно хиты, провалы и то, как меняется охват. У крупных каналов показаны 100 самых просматриваемых постов и равномерная выборка остальных — не больше 1 500 точек.
Каждый пост — точка
Выше — больше просмотров, крупнее — больше реакций. Нажмите на точку, чтобы открыть пост.
ВидеоФотоДругоеразмер — реакцииПриближение — кнопками «+» и «−», щипком или Ctrl + колесо мыши; приближенный график сдвигается перетаскиванием
СводкаИтоги выбранного периода: сколько вышло публикаций, сколько у них сейчас просмотров и реакций и средний охват поста. Просмотры — текущие у постов, вышедших в эти дни, а не просмотры за день. Изменение — к такому же отрезку перед периодом, если за него есть статистика.
Итоги периода
Нажмите на показатель — ниже он появится по точкам периода
Просмотрысегодня — день ещё идётТочки с постами моложе двух суток — бледные с пунктиром: их просмотры ещё растут.
РекламаКакая доля публикаций за период похожа на рекламу: маркировка erid, рекламные ссылки и другие признаки. Детектор осторожный, поэтому настоящая доля обычно выше. Шкала — рекламная нагрузка по порогам индекса качества. Ниже — средний охват рекламного и обычного поста и кого рекламирует канал (сайты, на которые ведут ссылки из рекламы).
Что работаетПосты периода с наибольшим числом просмотров. Полоса — просмотры относительно первого места. Превью — картинка поста, у поста без картинки — аватар канала. Просмотры — сколько у поста сейчас.
РитмСколько постов вышло в каждый день периода (бледные столбики) и среднее за 7 дней (линия): видно, разгоняется канал или затихает. Сегодняшний день ещё идёт, поэтому среднее для него считается по вчерашний день.
Публикации по дням
Бледные столбики — дни, линия — среднее за 7 дней: видно, разгоняется канал или затихает
Публикации за деньСреднее за 7 дней
Данные обновлены в 23:35 по Москве. Просмотры — текущие у постов, вышедших в эти дни; средний охват — по постам старше двух суток.
Как считается индекс качества
🚧 Ваш Mutex тормозит корутины. Как перестать лочить и начать жить
Вы пишете многопоточный код. У вас есть общий ресурс (например, in-memory кэш сессий). Вы знаете, что synchronized в корутинах - это зло (блокирует системный поток). Вы берете Mutex и оборачиваете код в mutex.withLock { ... }.
Поздравляю, вы сделали лучше. Но под большим contention (высокой конкуренцией) ваш код всё равно будет страдать.
В чем проблема Mutex? Да, он не блокирует поток. Но withLock - это suspend функция. Когда 100 корутин ломятся в один лок, 1 получает доступ, а 99 - саспендятся. Саспенд - это не бесплатно. Это сохранение стейт-машины, аллокации Continuation, а потом дорогой диспетчеризированный возврат (resume) каждой корутины к жизни.
🛠 Как от этого уходят Сеньоры? (2 пути)
Путь 1: Lock-free (Для простых стейтов) Если у вас счетчик или простая ссылка на объект, забудьте про локи. Используйте атомики (CAS-операции — Compare-And-Swap). Они выполняются на уровне железа процессора.
// ❌ Медленно: val mutex = Mutex() var count = 0 // ... mutex.withLock { count++ }
// ✅ Летает: val count = AtomicInteger(0) // ... count.incrementAndGet()
// 🧠 Для Kotlin-объектов: val state = MutableStateFlow(InitialState) // Под капотом использует CAS-цикл без локов! state.update { it.copy(...) }
Путь 2: Thread Confinement (Для сложной логики) Если логика внутри лока большая (нужно сходить в мапу, проверить список, обновить 3 переменные) - CAS не спасет.
Вместо того чтобы обносить данные забором из локов, спрячьте данные в один поток. Вспоминаем наш прошлый пост про limitedParallelism(1) - это официально рекомендуемая JetBrains замена устаревшим Actors.
class SessionManager { // Создаем свой "однопоточный" диспетчер private val stateContext = Dispatchers.Default.limitedParallelism(1)
// Переменная вообще без потокобезопасных оберток! private val cache = mutableMapOf<String, Session>()
suspend fun updateSession(id: String, data: Session) { // Все операции с кэшем встают в аккуратную очередь // на выполнение в один поток. Никаких race conditions. withContext(stateContext) { val existing = cache[id] cache[id] = merge(existing, data) } } }
Почему Confinement (ограничение потока) лучше?
1. Вы избавляетесь от Deadlocks по определению. 2. Процессору гораздо проще прогнать очередь инструкций на одном ядре (работает L1/L2 кэш процессора), чем прыгать между потоками, пытающимися захватить Mutex. 3. Код становится линейным и предсказуемым.
Вывод: Mutex хорош, когда коллизии редки. Если ресурс "горячий" (в него долбятся постоянно) - изолируйте стейт в StateFlow (если можно) или в отдельный limitedParallelism(1) контекст (если логика сложная).
🛑 Как задушить свое приложение, используя Dispatchers.IO
Мы привыкли думать, что Dispatchers.IO - это бездонная бочка. Закинул туда сетевой запрос или чтение файла, и корутины сами всё разрулят.
Но у Dispatchers.IO есть жесткий лимит - 64 потока (или количество ядер процессора, если их больше).
Сценарий катастрофы: Вам нужно скачать 100 картинок или сделать батч-запросы к очень мееееедленному стороннему API. Вы запускаете 100 корутин на IO. Первые 64 запроса занимают все доступные потоки пула и зависают в ожидании ответа от сервера (I/O block).
В этот момент пользователь нажимает кнопку "Сохранить профиль". Метод сохранения идет в локальную БД (Room/Realm), которая тоже использует Dispatchers.IO. Результат: Запрос в БД не выполняется. Он встает в очередь и ждет, пока хотя бы одна из 64 картинок скачается и освободит системный поток. Пользователь видит бесконечный лоадер. Приложение кажется "зависшим", хотя ANR нет.
🛠 Как это лечат Джуны? Создают свой пул: Executors.newFixedThreadPool(100).asCoroutineDispatcher(). Почему это плохо: Вы плодите тяжеловесные системные потоки в обход общего механизма корутин. Это жрет память и ломает шаринг ресурсов.
👑 Как это лечат Сеньоры?
Используют limitedParallelism. У этого метода есть две суперсилы.
Суперсила 1: Расширение лимита
// Да, этот вызов легально расширяет лимит для конкретной задачи! val ImageDownloadDispatcher = Dispatchers.IO.limitedParallelism(100) Документация гласит: вызов limitedParallelism на Dispatchers.IO создает независимый пул, который может превышать глобальный лимит в 64 потока, при этом используя общий elastic thread pool под капотом. Картинки будут качаться в 100 потоков, не блокируя остальные задачи на дефолтном IO.
Суперсила 2: Изоляция узких бутылочных горлышек (Bottlenecks) Допустим, у вашей базы данных (SQLite/Room) пул коннектов равен 4. Если вы отправите 20 корутин писать в БД на Dispatchers.IO, 4 будут писать, а 16 - тупо заблокируют потоки IO, ожидая свободного коннекта к БД.
Делаем так:
val DbDispatcher = Dispatchers.IO.limitedParallelism(4) Теперь, если вы запустите 20 корутин на DbDispatcher, только 4 потока будут заняты. Остальные 16 корутин будут приостановлены (suspended), не блокируя системные потоки. Ваш основной IO диспетчер останется свободным для сети и файлов.
Чек-лист: Выделяйте кастомные диспетчеры через limitedParallelism для:
1. Массовых долгих сетевых операций. 2. Работы с БД (по размеру connection pool). 3. Работы с Legacy SDK, которые не поддерживают асинхронность.
Мы так привыкли к Dependency Injection (Dagger/Hilt/Koin), что перестали замечать, как он раздувает наш код. Сценарий из жизни: вам нужно залогировать событие глубоко в слое бизнес-логики. Вы берете интерфейс Logger и прокидываете его через конструкторы пяти промежуточных классов, хотя он нужен только в одном методе на самом дне.
В Kotlin 2.1+ (KEEP-367) активно допиливают киллер-фичу, которая меняет правила игры - Context Parameters.
Историческая справка: Если вы помните экспериментальные Context Receivers (синтаксис context(Logger)), забудьте их. JetBrains официально задепрекейтили их в версии 2.0.20, заменив на более строгие и читаемые Context Parameters.
Как это выглядит теперь?
Допустим, у нас есть TransactionScope и Logger. Мы не хотим инжектить их в классы-прослойки, мы хотим потребовать их наличие в функции неявно.
// Новая магия: context(...) перед функцией с явными именами переменных context(logger: Logger, tx: TransactionScope) fun deleteUser(userId: String) { logger.log("Deleting user $userId")
tx.execute { // ... логика удаления в БД } }
В чем отличие от обычных параметров? При вызове функции deleteUser вам не нужно передавать эти параметры руками! Компилятор сам найдет их в текущем скоупе (scope) и подставит под капотом.
class UserUseCase( private val logger: Logger, private val tx: TransactionScope ) { fun execute() { // Вводим зависимости в область видимости with(logger) { with(tx) { // Вызываем функцию БЕЗ передачи логгера и транзакции! // Компилятор сам слинкует их из контекста. deleteUser("user_123") } } } }
(На практике вы настроите скоуп один раз на самом верхнем уровне — например, в базовой ViewModel или HTTP-хендлере Ktor — и вся вложенная цепочка вызовов получит к ним доступ).
Почему это круто для архитектуры?
1. Чистые сигнатуры: Бизнес-функции принимают только бизнес-аргументы (например, userId: String). Вся техническая инфраструктура (логирование, аналитика, права доступа, транзакции) выносится за скобки. 2. Читаемость (в отличие от старых Receivers): Раньше методы из контекста вызывались неявно (просто log()), и в большом классе было невозможно понять, откуда пришел этот метод. Теперь у нас есть жесткое имя: logger.log(). 3. Идеально для Cross-cutting concerns: Это элегантное решение для сквозного функционала, который не должен загрязнять доменную модель.
⚠️ Правило Сеньора: Context Parameters - это не замена конструкторному DI. Если классу нужен UserRepository для выполнения всех его основных задач, передавайте его в конструктор. Используйте context(...) только для тех сущностей, которые меняются в зависимости от среды выполнения (call chain), или для утилитарных инструментов (например, AnalyticsScope).
🌪 Ваша дата в опасности: flatMapConcat vs Merge vs Latest
Вам нужно взять поток ID-шников и для каждого скачать детали из сети. Задача банальная:
val detailsFlow = idsFlow.flatMap??? { id -> repository.fetchDetailsAsFlow(id) }
IDE услужливо подсказывает три варианта: Concat, Merge, Latest. И вот тут кроется разница между "работает как часы", "тормозит" и "тихо теряет данные".
Давайте разберем на пальцах.
1. flatMapConcat (Очередь в поликлинику)
• Как работает: Строго последовательно. Ждет, пока полностью завершится внутренний Flow для первого ID, и только потом переходит ко второму. • Плюс: Сохраняет идеальный порядок элементов. Безопасно для транзакций/БД. • Минус (Bottleneck): Это ОЧЕНЬ медленно. Если у вас 10 картинок, и каждая качается по секунде - весь процесс займет 10 секунд. Никакой параллельности.
2. flatMapMerge (Броуновское движение)
• Как работает: Запускает сбор внутренних Flow параллельно. Не ждет окончания предыдущего. • Плюс: Максимальная скорость. 10 картинок скачаются (почти) за 1 секунду. • Минус: Порядок ответов непредсказуем. Если вам важно, чтобы элементы пришли в том же порядке, что и ушли — вы получите race condition. • 🧠 Секрет Сеньора: У него есть параметр concurrency (по дефолту 16). Если поставить flatMapMerge(concurrency = 1), он превратится в flatMapConcat.
3. flatMapLatest (Амнезия)
• Как работает: Как только в верхний (внешний) поток прилетает новый элемент, он отменяет выполнение предыдущего внутреннего потока. • Плюс: Идеально для UI (например, строка поиска). Юзер ввел "A" -> пошел запрос. Ввел "AB" -> запрос для "A" отменился (вылетел CancellationException), пошел запрос для "AB". Экономит трафик и ресурсы. • Фатальный Минус: НИКОГДА не используйте это для сохранения данных или платежей. Если вы отправите два события на сохранение подряд, первое будет убито на полпути.
📝 Чек-лист для код-ревью: ✅ Поиск с автокомплитом? -> flatMapLatest ✅ Скачивание списка файлов, порядок не важен? -> flatMapMerge ✅ Строгая последовательная обработка платежей или логов? -> flatMapConcat
Необходимые cookie нужны для работы и безопасности сайта: вход, защита форм, язык, тема и другие. Аналитические cookie Яндекс Метрики и рекламные cookie Рекламной сети Яндекса включаются только с вашего согласия. Нажимая «Принять все», вы даёте согласие на обработку данных cookie-файлов; подробнее — в Политике обработки персональных данных.