Как выйти в поиск в яндекс
Перейти к содержимому

Как выйти в поиск в яндекс

  • автор:

15 супер полезных примеров команды find в Linux

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

Она используется для поиска файлов и папок через командную строку Linux.

Команда find — одна из самых мощных и широко применимых команд. При этом она крайне объёмная и насчитывает более 50 опций, в которых легко запутаться, особенно в сочетании с командами exec или xargs.

Если вы сисадмин или разработчик, избежать команды find при работе с командной строкой не получится. Так что давайте научимся её не бояться и пользоваться её возможностями в полной мере.

Для этого разберём самые распространённые случаи практического применения команды find. Но для начала покажу вам синтаксис и принцип работы с командой.

Команда find в Linux

Общий синтаксис команды find выглядит так:

find [directory to search] [options] [expression]

Всё, что в квадратных скобках, указывать необязательно. А значит, выполнить команду find можно вообще без опций и параметров. Она выдаст список всех файлов и папок в текущем расположении. Мало полезного, да?

Так что давайте взглянем на параметры подробнее:

  • directory to search (папка поиска) — это расположение, с которого вы хотите начать поиск. Поиск по умолчанию рекурсивный и начинается с текущего расположения.
  • options (опции) содержит указание типа поиска: по имени, типу файла, времени изменения и так далее — тут может быть более 50 вариантов.
  • expression (выражение) содержит поисковый запрос. Если вы ищете файл по имени, параметр expression должен содержать имя файла. Если ищете файлы с именем, соответствующим заданному шаблону, поисковое выражение — это шаблон.
find . -type f -name myfile

Такая команда выполнит поиск файла (именно файла, не папки) с именем myfile в текущей папке и подпапках. Опция -type f сужает поиск до файлов. Точка ( . ) указывает на текущую папку.

Рассмотрим несколько примеров применения команды find.

Поиск файлов и папок по имени

Так выполняется поиск файлов и папок по имени:

find . -name SEARCH_NAME

Поскольку тип объекта не указан, команда выполняет поиск и файлов, и папок.

Пример ниже — поиск файлов и папок с именем «mystuff»:

abhishek@LHB:~/Examples$ find -name mystuff ./new/mystuff ./mystuff

Поиск только файлов или только папок

Если нужно искать только файлы, на помощь придёт опция type -f:

find . -type f -name SEARCH_NAME

Тип и имя можно указывать в любом порядке. Возьмём пример выше и ограничим круг поиска файлами:

abhishek@LHB:~/Examples$ find -type f -name mystuff ./mystuff

Если нужно найти папку, укажите тип type -d:

find . -type d -name SEARCH_NAME

Вот пример нашего поиска уже по папкам:

abhishek@LHB:~/Examples$ find -type d -name mystuff ./new/mystuff

Поиск без учёта регистра

Команда find по умолчанию учитывает регистр. Чтобы выполнить поиск по имени файла без учёта регистра, надо ввести опцию -iname вместо -name .

find . -type f -iname SEARCH_NAME

С поиском по папкам ( type -d ) это тоже работает.

abhishek@LHB:~/Examples$ find -iname mystuff ./new/mystuff ./MyStuff ./mystuff

Скриншот последних трёх примеров:

image

Поиск файлов по расширению (важно)

Одно из самых популярных применений команды find — поиск файлов определённого типа, то есть по заданному расширению.

Скажем, вы хотите найти все файлы С++ в текущих папках. Файлы С++ имеют расширение .cpp, и вот как их можно найти:

find . -type f -name "*.cpp"

С такими опциями команда find найдёт только файлы ( -type f ) с именами, оканчивающимися на .cpp .

abhishek@LHB:~$ find . -type f -name "*.cpp" ./file.cpp ./.cargo/registry/src/github.com-1ecc6299db9ec823/libz-sys-1.1.3/src/zlib/contrib/iostream2/zstream_test.cpp ./.cargo/registry/src/github.com-1ecc6299db9ec823/libz-sys-1.1.3/src/zlib/contrib/iostream/test.cpp ./.cargo/registry/src/github.com-1ecc6299db9ec823/libz-sys-1.1.3/src/zlib/contrib/iostream/zfstream.cpp

При работе с командой find всегда заключайте поисковое выражение в двойные кавычки.

С чем связана рекомендация заключать поисковый запрос в двойные или одинарные кавычки? Дело в том, что без кавычек оболочка будет работать с символом * как с джокером и выполнит подстановку.

Вот что будет, если ввести запрос без кавычек:

find . -type f -name *.cpp

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

Это сработает, если такой файл всего один, но если их несколько, оболочка пожалуется на некорректный синтаксис.

image

В нашем случае файл .cpp всего один, и после подстановки команда выглядит так: find . -type f -name file.cpp . Она работает, поскольку file.cpp — корректный поисковый запрос.

А вот файлов .txt в той же папке два, и когда команда расширяется до find . -type f -name another.txt new.txt , выводится предупреждение, потому что поисковых запросов больше одного.

Именно поэтому сам поисковый запрос всегда следует заключать в двойные кавычки.

Поиск нескольких файлов с несколькими расширениями (или условием)

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

Вместо того чтобы прогонять команду find несколько раз, введите её один раз с опцией -o, которая работает как логическое условие «или»:

find . -type f -name "*.cpp" -o -name "*.txt" 
abhishek@LHB:~/Examples$ find . -type f -name "*.txt" -o -name "*.cpp" ./new.txt ./file.cpp ./new/new.txt ./new/dir2/another.txt ./new/dir1/new.txt ./another.txt

Поиск файлов в заданной папке

Все приведённые примеры иллюстрируют поиск в текущей папке, потому что команда включает в себя точку ( . ).

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

abhishek@LHB:~/Examples$ find ./new -name mystuff ./new/mystuff

Поиск файлов в нескольких папках

Если нужные вам файлы могут находиться в нескольких папках, можно выполнить поиск во всех этих расположениях за один раз. Просто укажите все пути к папкам при введении команды find:

find ./location1 /second/location -type f -name "pattern"

Поиск пустых файлов и папок

Опция -empty позволяет использовать команду find для поиска пустых файлов и папок.

Найти таковые в текущей папке можно следующим образом:

find . -empty

Можно указать тип объектов, чтобы искать только файлы или только папки:

find . -empty -type f

Кроме того, можно в таком режиме искать файлы по имени:

find . -empty -type f -name "*.cpp"

image

Поиск крупных и мелких файлов (поиск по размеру файла)

Команда find поможет найти крупные или мелкие файлы, если выполнить поиск по размеру. Но это работает только для файлов, не для папок.

Используется опция -size с аргументом +N для файлов размером более N и -N для файлов размером менее N.

А вот как можно найти файлы точного заданного размера (50 КБ):

find . -size 50k

Так выполняется поиск файлов размером более 1 ГБ в текущей папке:

find . -size +1G

А так — файлов, не превышающих 20 байт:

find . -size -20c

Для поиска файлов размером более 100 МБ, но менее 2ГБ, введите:

find . -size +100M -size -2G

Поиск по размеру тоже можно сочетать с поиском по имени файла. Таким образом, найти в корневом каталоге все файлы размером более 500 МБ с именем, оканчивающимся на .log, можно так:

find / -size +500M -name "*.log"
  • c – байты
  • k – килобайты
  • M – мегабайты
  • G – гигабайты

Поиск недавно изменённых файлов (поиск по времени изменения или создания)

  • Mtime – время последнего изменения файла
  • Ctime – время создания файла
  • Atime – время последнего доступа к файлу

Найти все файлы, претерпевшие изменения за последние трое суток (3*24ч), можно так:

find . -type f -mtime -3

А все файлы, созданные пять и более дней назад, ищутся так:

find . -type f -ctime +5

Понимаю, что 24 часа — большой срок. Что если нужно выявить файлы, изменённые всего пару минут назад? Для этого предусмотрены опции mmin , amin и cmin .

Так выглядит команда поиска всех файлов, изменённых за последние пять минут:

find . -type f -mmin -5

image

Можно не только указать имя файла, но и ограничить временной промежуток с двух сторон. Команда ниже выполнит поиск всех файлов .java, изменённых не ранее 30 и не позднее 20 минут назад.

find . -type f -mmin +20 -mmin -30 -name "*.java"

Поиск файлов с определёнными настройками доступа

Надеюсь, вы имеете представление о разрешениях файлов в Linux.

Команда find позволяет выполнить поиск файлов по разрешению и режиму доступа.

find -perm mode

Поищем в текущей папке, к примеру, все файлы с режимом доступа 777:

find . -perm 777

А так можно найти все файлы с правами на чтение и запись для всех типов пользователей (только точное совпадение; файлы с правами на выполнение для всех не отобразятся):

find . -perm a=r+w

Поиск файлов по владельцу

Можно также найти файлы, принадлежащие определённому пользователю.

Вот как обнаружить в текущей папке все файлы пользователя Джона:

find . -type f -user John

Эта опция сочетается с другими, будь то размер или время и имя файла:

find . -type f -user John -name "*.cpp"

Отключение рекурсивного поиска для поиска только в текущей папке

По умолчанию команда find выполняет поиск во всех подпапках текущего расположения. Если это не требуется, можно ограничить глубину поиска значением «1». Так вы ограничитесь поиском в текущей папке, не залезая в подпапки.

find . -maxdepth 1 -type f -name "*.txt"

image

Исключение папки из поиска

Если нет необходимости производить поиск в той или иной папке, можно исключить её с помощью опций path, prune и логического «или».

find . -path "./directory_exclude/*" -prune -o -name SEARCH_NAME

Будьте внимательны: путь к папке должен оканчиваться на *, затем идёт -prune и только потом -o .

Попросту говоря, при поиске с опцией prune папка, указанная с помощью path, игнорируется. Prune всегда сопровождается флагом -o (логическое «или»), чтобы папки, которые не были исключены, просматривались на наличие искомого объекта.

Дальнейшая работа с результатами команды find: exec и xargs

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

Например, как найти файлы с именем, соответствующим определённому шаблону, и переименовать их за одно действие? Или выявить и удалить пустые файлы?

Вам уже известно, что в Linux можно использовать перенаправление ввода-вывода, чтобы объединить результаты одной команды с вводом другой. Но с результатами команды find это не сработает — по крайней мере, не напрямую.

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

  • Применить exec
  • Применить xargs

Использование find и exec

Допустим, вам нужен подробный список (ls -l) файлов, найденных командой find. Вот как его получить:

find . -type f -name "*.txt" -exec ls -l <> +

Результат будет таким:

abhishek@LHB:~/Examples$ find . -type f -name "*.txt" -exec ls -l <> + -rw-rw-r-- 1 abhishek abhishek 39 Oct 13 19:30 ./another.txt -rw-rw-r-- 1 abhishek abhishek 35 Oct 13 15:36 ./new/dir1/new.txt -rw-rw-r-- 1 abhishek abhishek 35 Oct 13 15:36 ./new/dir2/another.txt -rw-rw-r-- 1 abhishek abhishek 35 Oct 13 18:51 ./new/mystuff/new.txt -rwxrwxrwx 1 abhishek abhishek 35 Oct 13 15:37 ./new/new.txt -rw-rw-r-- 1 abhishek abhishek 35 Oct 13 18:16 ./new.txt

Многие забывают ввести <> + в конце команды exec. Но это необходимо — как и пробел между скобками <> и плюсом +.

Фигурные скобки ссылаются на результат выполнения команды find. Их содержимое может иметь следующий вид: . Символ + используется как конец команды exec.

Есть ещё один вариант оформления exec:

find . -type f -name "*.txt" -exec ls -l <> \;

В данном случае плюс заменён на точку с запятой. Дополнительная косая черта означает, что точка с запятой не является специальным символом.

Преимущество сочетания <> + заключается в меньшем количестве команд ( ls -l file1 file2 file3 ), тогда как комбинация <> \; запустит цепочку ls -l file1 , ls -l file2 и так далее.

Однако сочетание <> \; даёт возможность использовать <> несколько раз в одном и том же выражении exec. Так, приведённая ниже команда переименует все обнаруженные файлы с расширением .old.

find . -type f -name "*.txt" -exec mv <> <>.old \;

Использование команды xargs

Многие пользователи Linux сталкиваются с необходимостью перенаправления ввода-вывода довольно часто. Но команда exec с цепочкой символов <> + кажется им слишком сложной.

И тут на помощь приходит xargs. Нужно просто перенаправить вывод команды find в команду xargs через конвейер.

find . -type f -name "*.txt" | xargs ls -l

image

Синтаксис куда проще, верно? К тому же команда xargs тоже весьма эффективна. Подробнее о ней — в статье по ссылке.

Сочетание команд find и grep

Теперь вы умеете совмещать команду find с xargs и exec, и пора перейти на следующий уровень — объединить find и grep.

Для сисадминов и разработчиков комбинация команд find и grep — одна из самых распространённых и вместе с тем самых полезных.

Команда find находит файлы с именем, соответствующим шаблону, а затем команда grep выполняет поиск по их содержимому.

Например, вам нужно найти все файлы .txt, в которых есть имя «Alice». Объединить команды find и grep можно так:

find . -type f -name "*.txt" -exec grep -i alice <> +

А можно с помощью xargs:

find . -type f -name "*.txt" | xargs grep -i alice

image

Конечно, пример элементарный, но если команда grep вам знакома, можете использовать её на своё усмотрение.

И это далеко не все возможности команды find.

Перечислить все опции и примеры использования команды find практически невозможно. Её возможностям нет границ, но если вы освоите её принципы, она окажется очень кстати во многих ситуациях. Решающий фактор — как сочетается логика действия разных опций и команд.

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

НЛО прилетело и оставило здесь промокоды для читателей нашего блога:

— 15% на все тарифы VDS (кроме тарифа Прогрев) — HABRFIRSTVDS .

Доступно до 31 декабря 2021 г.

Что такое ТОП поисковой выдачи, как в него попасть и удержаться

Топ поисковой выдачи — это сайты (как правило, первые 10), которые прежде всего показываются пользователю по его поисковому запросу, простыми словами — это первая страница выдачи с результатами.

Поисковая выдача делится на рекламную и органическую. В рекламную входят сайты, которые рекламируются в «Яндекс.Директе», в органическую — сайты, которые, на взгляд поисковых роботов, дают лучшие ответы на заданный пользователем вопрос.

1 — сайты из рекламной выдачи. 2 — сайты из органической выдачи

Когда говорят «попасть в ТОП выдачи», имеют в виду попасть именно в 10-ку органической выдачи. Почему большинство сайтов так туда стремятся?

  • Максимум трафика . Как правило, при поиске ответа на вопрос пользователи ограничиваются первой страницей выдачи. Лишь самые пытливые люди проходят на 2-3 страницу: все сливки трафика получают сайты из первой десятки.
  • Больше конверсий . Чем больше пользователей приходят на сайт из поисковой выдачи, тем статистически выше вероятность конверсионных действий. Условно говоря, если конверсия составляет 1 %, то из 100 посетителей совершит конверсионное действие 1 человек, а из 10 000 посетителей — уже 100. Даже без роста процента конверсии такой вариант выглядит привлекательнее.
  • Больше продаж . Часто в качестве конверсии определяется заполнение формы заказа или оформление покупки. При повышении трафика из поисковой выдачи продаж будет больше.
  • Прочнее репутация компании. Люди склонны больше доверять компании, сайт которой находится в топе поиска «Яндекса» или Google. А если сайт часто мелькает в поиске, растет и узнаваемость компании.

Читайте также:
Продвижение молодого сайта с нуля — как выйти в ТОП и не слить бюджет на SEO

Как попасть в ТОП-10 поисковой выдачи

Результаты поиска формируется сложными алгоритмами поисковых систем: их общее количество оценивается в несколько сотен. Алгоритмы постоянно совершенствуются; старые уходят в прошлое, появляются новые. Поэтому, чтобы попасть в ТОП, разработчики сайтов, SEO-специалисты и копирайтеры постоянно проводят доработку сайта и его отдельных страниц.

Чтобы вывести сайт в первую 10-ку, надо учитывать:

Релевантность информации на сайте поисковому запросу . Если пользователь вбивает в поисковую строку запрос «купить лежанку для кошки», он должен попасть на страницу, где будет большой ассортимент, удобный функционал выбора и возможность оформить заказ.

В конце 90-х и начале 2000-х годов ключевые слова «купить телевизор» или «купить плитку в ванную» могли вписывать в тексты очень далекой тематики — например, о правилах выбора детской обуви или о выращивании томатов.

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

Читайте также:
Релевантность: что это такое, критерии релевантного запроса

Семантическое ядро . Чтобы ресурс попал в ТОП-10, важно грамотно составить семантическое ядро — список запросов, по которым пользователь может искать товар, услугу или тематическую информацию. Если совсем просто, семантическим ядром называется набор ключевых фраз, который наилучшим образом раскрывает тематику сайта и делает его хорошо видимым для поисковых роботов. Чем точнее составлено семантическое ядро, тем лучше будет ранжироваться сайт, и тем более теплый трафик может прийти из поисковой системы.

Читайте также:
16 источников бесплатного трафика для вашего сайта

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

Читайте также:
Что такое контент простыми словами

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

. на которых последнее, о чем думали создатели — это удобство пользователей. Такому сайту точно не место в первой 10-ке

Расширение структуры сайта . В идеале под каждый поисковый запрос в теме сайта или под каждую группу очень близких запросов должна быть создана отдельная страница. Например, под запрос «продвижение интернет-магазина» одна, а под запрос «как продвигать свой блог в интернете» — другая. Объединять такие запросы нерационально, хотя общая тема у них одна — продвижение сайтов. Разветвленная структура сайта поможет охватить больше ключевых фраз, вывести сайт по большему количеству запросов, и в итоге привести больше трафика. Запросы постоянно меняются, появляются новые: работа над структурой фактически бесконечна.

Регулярное обновление контента . Поисковые алгоритмы ценят сайты, информация на которых постоянно расширяется и обновляется. Чем чаще на сайте появляется качественный и актуальный контент, тем выше будут его шансы. Обновлениями могут быть и новые страницы, и обновления ленты новостей, и обновления статей — дополнение их актуальной информацией, и переработка товарных карточек. Так, в онлайн-курсах от «Яндекса» есть информация, которую можно считать свидетельством важности обновления контента:

Есть сайты, новый контент которых попадает в поиск очень быстро. К ним относятся крупные популярные ресурсы с часто обновляемой информацией — например, сайты СМИ.

Скорость загрузки сайта . Это тоже важный показатель для ранжирования, так как большинство пользователей не готовы ждать загрузки сайта дольше 2-3 секунд. А поисковые системы заинтересованы показывать пользователю сайты, которые отвечают их ожиданиям, в том числе и с точки зрения скорости загрузки. Поэтому для сайта так важны надежный движок, продуманная логичная структура, грамотный код и отсутствие лишних скриптов.

Вот что пишет об этом «Яндекс»:

Высокая скорость загрузки сайта напрямую влияет и на показатель отказов, и на конверсию сайта. По данным исследований, 70 % людей не готовы ждать загрузки мобильного сайта более 2-х секунд.

Читайте также:
Что такое показатель отказов и как его снизить

Измерить скорость загрузки сайта помогут специализированные сервисы, в том числе российский аналитический сервис PR-CY.

Адаптивная верстка. Современные сайты просто обязаны корректно отображаться на экранах мобильных гаджетов — доля мобильного трафика растет с каждым годом, и в 2021 году в Рунете достигла рекордных 83 %.

Практически любой сайт получает больше половины трафика с мобильных устройств

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

Читайте также:
Мобильные версии сайтов 2023: интересные тенденции и неожиданные выводы

Внешнее продвижение . Под внешним продвижением сайта подразумевается размещение ссылок на внешних ресурсах. Например, в карточке компании на «Яндексе», в тематических каталогах, в социальных сетях. Качественное внешнее продвижение придает вес сайту с точки зрения поисковых систем, и они могут повысить его в ранжировании.

Сколько времени сайт может находиться в ТОП-10

Есть две новости для разработчиков и SEO-специалистов, плохая и хорошая:

  • Плохая новость — невозможно один раз вывести сайт в ТОП-10 по какому-то запросу и успокоиться. Алгоритмы поисковой системы меняются быстро, ведь в интернете ежедневно появляются новые сайты. Когда появляется новый хорошо оптимизированный сайт с качественным контентом, хорошими поведенческими факторами, он довольно быстро может попасть в первую 10-ку и изменить картину выдачи. Взлеты и падения сайтов могут происходить чуть ли не ежедневно.
  • Хорошая новость — нет запретов или определений того, сколько времени один и тот же сайт может находиться в ТОП-10 «Яндекса» или Google. При качественном SEO-продвижении, регулярном обновлении контента сайта и отсутствия технических ошибок, замедляющих работу ресурса, он может сохранять лидирующие позиции неограниченный период времени.

Присоединяйтесь к нашему Telegram-каналу!

  • Теперь Вы можете читать последние новости из мира интернет-маркетинга в мессенджере Telegram на своём мобильном телефоне.
  • Для этого вам необходимо подписаться на наш канал.

Чек-лист: готов ли ваш сайт выйти в ТОП

Вот короткий чек-лист, который поможет определить, готов ли ваш ресурс к продвижению в поиске:

  • Информация каждой страницы сайта релевантна поисковому запросу, по которому ее будут находить пользователи.
  • Семантическое ядро составлено, расширено, очищено и разбито на кластеры.
  • Страница сайта наполнены качественным, полезным, структурированным контентом.
  • Продумано удобство для пользователей — удобное меню, «хлебные крошки», быстрая загрузка сайта.
  • Ведется постоянная работа над расширением структуры сайта и обновлением его контента.
  • Сайт корректно отображается и на десктопных, и на мобильных устройствах.
  • Проводится работа по внешнему продвижению сайта.

Если все пункты чек-листа учтены, скорее у вас высокие шансы увидеть ТОП-10 в своей тематике.

Продвижение сайта в ТОП-10

  • Оплата по дням нахождения в ТОП
  • Подбираем запросы, которые приводят реальных покупателей!

Как не выйти в Window при работе с Window?

Многие разработчики разбиваются о жизненные циклы onResume, onStart, onCreate, которые связаны с отображением UI внутри приложения, будь то Activity или Fragment. Некоторые методы работы со стремительно развивающимся андроидом приходится искать интуитивно, потому что официальная документация не всегда дает полной картины, а иногда даже вводит в заблуждение. Стоит разобраться, где заканчиваются знания и начинается интуиция.

Я Дмитрий Манько, андроид-разработчик в компании Ситимобил, попробую объяснить, что такое onResume() и почему определение от Google не совсем корректное. Разберу иерархию внутри Activity, покажу когда происходит взаимодействие и какие события для этого нужны. А ещё объясню, почему Fragment дешевле и проще Activity.

Сразу начнем с практики. Давайте рассмотрим кейс:

Есть MainActivity, который наследуется от обычного AppCompatActivity. В onCreate мы устанавливаем activity_main, переопределяем и морозим onResume.

class MainActivity : AppCompatActivity() < override fun onCreate(savedInstanceState: Bundle?) < super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) >override fun onResume() < super.onResume() Thread.sleep(TimeUnit.HOURS.toMillis(1)) // Никто никуда не спешит, поспим часик? >>

В качестве верстки для activity_main устанавливаем ConstraintLayout с текстовой кнопкой.

Используется стандартная MaterialComponents тема и телефон запущен в светлой теме.

Попытаемся выбрать правильный ответ на вопрос: «Что произойдет, если заморозить onResume при первом запуске приложения внутри Activity»?

  1. На кнопку нельзя будет нажать, на экране будет отрисована кнопка.
  2. На кнопку нельзя будет нажать, на экране будет отрисован только черный фон.
  3. Можно будет нажать на кнопку, на экране будет отрисована кнопка.
  4. На кнопку нельзя будет нажать, на экране будет отрисован только белый фон.

Этот вопрос демонстрирует, чем руководствуются разработчики, знаниями, опытом или интуицией. Например, участники конференции ответили следующим образом:

Правильный ответ

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

Большинство разработчиков ожидают получить результат слева, но ни Action Bar, ни кнопка не отрисуются, и ничего, кроме белого фона, не будет.

Официальная документация от Google

В официальной документации от Google написано:

onResume() — This is an indicator that the activity became active and ready to receive input. It is on top of an activity stack and visible to user.

Т.е. это некий указатель на то, что activity становится активной и готова принимать входящие взаимодействия: ввод, вызовы и т.д. Это находится наверху нашего Activity-стека и видно пользователю. Но на самом деле, если onResume() вызвался, а мы морозим поток, то наши View элементы еще не видны пользователю.

Поэтому давайте разбираться. Для этого нам придется спуститься в так называемые «кишки» Андроида и посмотреть, кто вызвал onResume(), кто вызвал тот метод, который вызвал onResume() и отправиться ещё выше.

onResume()

Кто вызвал onResume()

public final class ActivityThread extends ClientTransactionHandler < // Some code here public ActivityClientRecord performResumeActivity(IBinder token, boolean finalStateRequest, String reason) < // And some code here r.activity.performResume(r.startsNotResumed, reason); // And some code here >

Чтобы было проще понять, как все это между собой взаимодействует, я опущу некоторые подробности. В первую очередь нас интересует, как вызывается onResume.

r — это ActivityClientRecord, то есть обертка, которая содержит инстанс нашей Activity и дополнительную информацию о её состоянии. Она вызывает метод performResumeActivity. Он дает ответ, что тот onResume, который мы заморозили, был вызван.

Идем выше, чтобы понять, кто вызывает метод performResumeActivity

Кто вызвал performResumeActivity()

public final class ActivityThread extends ClientTransactionHandler < // Some code here @Override public void handleTopResumedActivityChanged(IBinder token, boolean onTop, String reason) < // And some code here final ActivityClientRecord r = performResumeActivity(token, finalStateRequest, reason); // And some code here if (r.window == null && !a.mFinished && willBeVisible) < r.window = r.activity.getWindow(); View decor = r.window.getDecorView(); decor.setVisibility(View.INVISIBLE); ViewManager wm = a.getWindowManager(); WindowManager.LayoutParams l = r.window.getAttributes(); a.mDecor = decor; l.type = WindowManager.LayoutParams.TYPE_BASE_APPLICATION; l.softInputMode |= forwardBit; if (r.mPreserveWindow) < a.mWindowAdded = true; r.mPreserveWindow = false; // And some code here if (r.activity.mVisibleFromClient) < r.activity.makeVisible(); >>

С помощью переменной присваивается ActivityClientRecord и в нашем путешествии появляются сущности:

  • Window
  • DecorView
  • WindowManager

Их мы разберем позднее, а сейчас нас интересует код в конце вызова метода activity makeVisible()

if (r.activity.mVisibleFromClient)

Перейдем к погружению в метод makeVisible():

public class Activity extends ContextThemeWrapper implements LayoutInflater.Factory2, Window.Callback, KeyEvent.Callback, OnCreateContextMenuListener, ComponentCallbacks2, Window.OnWindowDismissedCallback, WindowControllerCallback, AutofillManager.AutofillClient, ContentCaptureManager.ContentCaptureClient < // Some code here void makeVisible() < // Implementation here >// Some code here > 

Отметим, что имплементация находится в таком классе как Activity.

Код метода makeVisible() выглядит следующим образом:

void makeVisible() < if (!mWindowAdded) < ViewManager wm = getWindowManager(); wm.addView(mDecor, getWindow().getAttributes()); mWindowAdded = true; >mDecor.setVisibility(View.VISIBLE); >

Мы получаем WindowManager, добавляем к нему mDecor, кладем атрибуты, и делаем mDecor видимой.

Посмотрим, что происходит внутри метода addView у WindowManager.

public final class WindowManagerImpl implements WindowManager < private final WindowManagerGlobal mGlobal = WindowManagerGlobal.getInstance(); // Some code here @Override public void addView(@NonNull View view, @NonNull ViewGroup.LayoutParams params) < applyDefaultToken(params); mGlobal.addView(view, params, mContext.getDisplay(), mParentWindow); >>

У нас существует имплементация WindowManager — WindowManagerImpl. В WindowManagerImpl я не стал описывать происходящее, потому что он делегирует вызов в WindowManagerGlobal. Как видно, это применение того же метода, но уже у самого WindowManagerGlobal.

Взглянем подробнее на WindowManagerGlobal:

public final class WindowManagerGlobal < // Some code here public void addView(View view, ViewGroup.LayoutParams params, Display display, Window parentWindow) < // Some code here ViewRootImpl root; // Some code here root = new ViewRootImpl(view.getContext(), display); view.setLayoutParams(wparams); mViews.add(view); mRoots.add(root); mParams.add(wparams); // do this last because it fires off messages to start doing things try < root.setView(view, wparams, panelParentView); >catch (RuntimeException e) < // BadTokenException or InvalidDisplayException, clean up. if (index >= 0) < removeViewLocked(index, true); >throw e; > > >

В нем происходит инициализация ViewRootImpl и добавляются наши атрибуты в массив Views, массив Roots и массив Params. Самое важное — это конечное действие, которое обернуто в try/catch. Здесь у нас устанавливается метод setView нашей ViewRootImpl.

Именно в этот последний промежуток у нас root.setView() устанавливает наши View, начиная от DecorView и это позволяет проинициализировать все необходимое для старта отображения, и запустить тот самый performTraversals.

Возможно, вы знаете про этот метод, но на всякий случай напомню.

PerformTraversals прогоняет жизненный цикл наших Views. Он измеряет и рисует, начиная с корневой и заканчивая дочерними.

Почему так сложно?

Нельзя ли сделать проще?

Чтобы разобраться, давайте попытаемся дать свое определение:

onResume() — метод жизненного цикла Activity, который уведомляет, что необходимый контент был добавлен в DecorView, но будет отображен только тогда, когда ViewRootlmpI установит эту view и выполнит обход вызовов методов View для отрисовки.

Звучит сложно, поэтому и говорят, что сейчас это будет видно пользователю. Но в формулировке от компании Google я бы поправил, что не «видно пользователю», а «вот-вот будет видно», так будет корректней.

Должен признать, что к моменту выхода этой статьи, Google убрала эти двусмысленные строчки, теперь часть описания метода onResume() выглядит так:

onResume() — This is usually a hint for your activity to start interacting with the user, which is a good indicator that the activity became active and ready to receive input.

Activity

Activity — это контроллер, который обрабатывает входящие события. Но это вовсе не сущность, которая отвечает за View.

Она реагирует на какие-то события, добавляет и отображает View, а также взаимодействует через обратные методы и содержит внутри себя Window.

Так можно понять примерную иерархию: внутри Activity есть Window.

Window

Window — это обертка над DecorView.

Ее задача — передать DecorView ViewRootlmpI для отрисовки и уведомить Activity о том, что произошло событие.

DecorView

DecorView — это корневая View.

Ее можно увидеть в Layout Inspector, но давайте разберемся, что это такое.

Наш контент находится здесь. Также у нас есть navigationBar и statusBar. По сути, DecorView это обычная View, точнее наследник FlameLayout. Она находится внутри Window.

ViewRootlmpI

ViewRootlmpI — это связующий класс между Window Manager Service и DecorView. Все взаимодействия, измерения, расположение и отрисовка View проходят через него

Несмотря на название, он не является частью View-иерархии, не является View в привычном понимании Андроида. Это связующее звено, которое получает события от сервиса и передает обратно View, View Activity и т.д., и мы получаем сообщение о том, что что-то произошло.

WindowManager

WindowManager — это системная служба, управляющая отображением списка Window.

Она отвечает за анимации при закрытии приложения, вращение и другие операции с окнами.

Схема взаимодействий

Найденная на просторах интернета схема мне полностью подходила, и я решил ее не переделывать.

В схеме видно, что есть Activity или Dialog, внутри него PhoneWindow — это единственная имплементация от Window. Внутри него DecorView и View. Есть WindowManager, внутри него ViewRootlmpI и WindowManagerService — это сервис, который управляет окнами.

Почему Activity и Dialog здесь на одном уровне расскажу дальше.

Взаимодействие происходит, когда:

  • Мы сообщаем из Активити что мы установили в иерархию View что-то
  • Мы сообщаем это Window
  • Window передает это DecorView
  • DecorView передается в ViewRootlmpI
  • ViewRootlmpI — WindowManager
  • WindowManager — WindowManager service

Обратно происходят почти тоже самое, но к примеру при физическом касании экрана Manager ответственный за входящие взаимодействия сообщает о событии ViewRootlmpI, ViewRootlmpI сообщает DecorView и т.д.

Есть разные вариации этой схемы, но они отображают одно и то же.

Почему фрагмент

Я много раз слышал, что фрагмент дешевле и проще, чем Activity, но не слышал конкретных причин, кроме идеологических

Причина в том, что для Activity нужно создать DecorView, Window и т.д., то есть пройти первый цикл отрисовки и поставить параметры. Для фрагмента нужно намного меньше, достаточно добавить фрагмент в уже существующую иерархию, которую создала Activity.

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

DialogFragment

На самом деле DialogFragment создает свой DecorView, но у него совершенно другой Window, и все необходимые компоненты, для того чтобы находиться поверх окна, который содержит Activity. Этот DecorView не встроен в иерархию Activity, хотя является фрагментом. Такой способ показа диалога независим от нашей Activity, и от этого возникают эффекты тени и прочее.

Хаки с Window

Все эти знания позволяют нам, например, с помощью WindowManager делать отображение над любыми другими приложениями и меню, так у драйверского приложения есть плавающая иконка (это не иконка запуска)

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

WindowManager wm = (WindowManager)getSystemService(WINDOW_SERVICE); wm.addView (myView, myWindowLayoutParams);

Главное учитывать ограничения, которые есть на платформах, и то, что Window необходимо создавать из сервиса, чтобы он не был привязан ни к Activity, ни к фрагменту, тогда окно будет постоянно отображено.

  • android
  • мобильная разработка
  • разработка под android
  • разработка мобильных приложений
  • программирование
  • java
  • activity
  • ui
  • kotlin
  • Блог компании Конференции Олега Бунина (Онтико)
  • Блог компании Ситимобил
  • Программирование
  • Разработка мобильных приложений
  • Разработка под Android

Как я заработал 500 000 рублей, сделав доступ к ChatGPT. А потом Яндекс убил SEO и всё (почти) закончилось

Статья о том, как я почти проспал «бум» ChatGPT, но сделал к нему доступ и заработал на этом (и продолжаю зарабатывать). Сам проект сейчас идёт на спад, но об этом в конце.

Кто я

Меня зовут Ростислав, я Full-Stack разработчик. Основной мой проект — это Telegram чат для сайта КликЧат. Но в апреле я решил отвлечься на месяц и запустить SaaS с ChatGPT, о чём и расскажу далее.

Для тех, кто впервые читает меня, ссылки на другие статьи:

  • 5 советов разработчику перед стартом своего стартапа. Опыт после 750 000 рублей
  • Стартап в Соло. Часть 5. Сколько приносит проект сейчас?

Как появилась идея

В конце 2022 года к ChatGPT пришла популярность. Все о нём говорили, писали, рассказывали и клепали мемы. Я успешно проигнорировал этот момент, пропустив всё мимо ушей.

В феврале я наткнулся на бесплатного ChatGPT-бота в Telegram. Написал ему несколько сообщений, поигрался. и забыл про него. После чего прошло ещё немного времени, когда можно было бы супер удачно запустить проект с ChatGPT.

И вот уже ближе к середине марта меня осенило: у ChatGPT есть API! Следовательно, появилась идея: а что, если локализовать API и перепродавать его юр. лицам. Так, чтобы они платили мне в рублях, а я закупался у OpenAI в долларах. Разницу — в карман.

Спустя пару дней я упростил концепцию. В B2B продавать сложно. Можно проще! Буду продавать сразу конечным пользователям. Нужно только сделать сайт, настроить рекламу, прокачать SEO и SaaS готов. Можно продавать подписки. Основную ставку сделаю именно на SEO (спойлер: сработало).

Так появился сайт «ChatGPT Me» (ссылки на Хабре нельзя, но название легко гуглится). Специально выбирал с вхождением ChatGPT в домен, чтобы легче было пробиться в SEO по этому запросу.

Как проект сделан с технической стороны

С технической стороны всё относительно просто: при запросе пользователя в чат я прокидываю запрос в API обычного ChatGPT от OpenAI. Ответ показываю пользователю.

При оплате подписки пользователю начисляются токены, которые расходует ChatGPT. Для анализа расхода токенов использую токенайзер.

Доход берётся из двух составляющих:

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

Серверная часть написана на NodeJS + NestJS. Основная часть сайта разработана на NextJS, лендинг сделан на Svelte (игрался с достижением максимума в Google Page Speed). База данных PostgreSQL, Redis для кэширования и как брокер сообщений. Nginx в качестве HTTP-прокси сервера.

Первую версию разработал примерно за месяц (но в довольно медленном темпе, так как времени было мало).

Как привлёк первых клиентов

Продвижения я начал со следующих действий:

  • Закупился ссылками через SeoWizard и опубликовался во всевозможных каталогах программ.
  • Запустил рекламу в Яндекс Директе (что меня удивило — на тот момент мало кто рекламировался по запросу ChatGPT).
  • Сделал несколько статей на vc.ru, pikabu и habr. Правда их все почти сразу удалили или заминусовали (справедливо, но трафик — есть трафик).

На каждую новую регистрацию у меня стояло оповещение в Telegram. Особо на резкий успех я не надеялся и не подумал, что это плохое решение. За ночь чат с оповещениями был забит сообщениями, а на следующий день телефон блымкал каждые 20-30 минут.

Выглядело это вот так:

Сообщения о регистрациях

Регистрировалось много людей, платило мало. Пришлось отключить сообщения о регистрациях и оставить только об оплатах:

Очень приятный звук оповещений

В целом, запуск оказался довольно успешным. За первый месяц существования проекта я вложил ~30 000 рублей и заработал примерно столько же. Выйти в ноль — уже успех!

На минутку я даже почувствовал себя успешным предпринимателем.

Инсайт: накрутка поведенческих факторов

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

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

Регистрации ради бесплатных сообщений

И эти регистрации. чертовски накручивают поведенческие факторы для поисковиков! Я дал немного халявы школьникам, а они дали мне позиции в SEO. Win-Win, получается.

Как проект рос

Спустя несколько недель, сайт выкинуло вверх SEO выдачи по запросу «ChatGPT» и от рекламы я отказался. Вкладывал только в закупку ссылок и понемногу дорабатывал чат.

Вот график регистраций за все месяцы с апреля:

В мае видно резкий рост регистраций за счёт SEO

Всё это время я относился к проекту как к «временной темке», поэтому не рассчитывал заработать миллионы. Просто радовался каждому пику и ждал, когда это всё счастье закончится.

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

Сообщения за июль

В хорошие дни чат приносил по 5 000 — 7 500 рублей. Из них чистыми было ~70% от выручки! Я очень сильно надеялся, что это будет вечно (спойлер: нет).

Инсайт про подписки

В какой-то момент я заметил, что регистрации растут, подписки растут, а мой платёж OpenAI стабильно составляет ~10 000 рублей. И решил проанализировать: а сколько людей реально расходует свои токены хотя бы наполовину?

Выяснилось, что только 2.5% пользователей хоть раз израсходовали 50% доступного месячного лимита по токенам. Хотя подписок в пике было более 90 000 рублей в месяц.

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

А теперь представьте, сколько зарабатывают спортзалы с ежемесячным списанием или Яндекс Плюс (хотя Яндекс Плюсом я и правда пользуюсь).

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

Какие были проблемы

А теперь немного о сложностях. Как по мне, этот проект принёс очень легкие деньги, но всё же о моментах, где я облажался, тоже нужно упомянуть.

    Дропнул базу пользователей (epic fail!) Регистрация через сайт делается через почту. В самом начале я не сильно валидировал поля и можно было вписать две одинаковых почты разным регистром. Например, две разные с точки зрения системы почты: «rostislav@mail.ru» и «Rostislav@mail.ru». В какой-то момент клиентка не могла войти в нужный аккаунт, так как сделала почту с заглавной и строчной буквы. Я решил все-таки поправить регистр почты для регистрации, поставив всё lowercase’ом. Но конкретно эта клиентка попросила поменять ей почту вручную. Ну я и полез в базу, чтобы обновить SQL-скриптом одну почту. Написал сначала скрипт WHERE, чтобы проверить выбор только одной почты. Затем для такого же WHERE написал уже UPDATE скрипт. И, по всей видимости, где-то не закрыл скобку. Запустил. Через секунду все ~25 000 почт стали NULL. В момент у меня пошёл холодный пот по спине и я подвис на две минуты. Но бекапы были на месте, я восстановился и пошёл менять футболку на сухую. Оказалось, дропнуть базу на проде действительно можно.

Как Яндекс убил SEO

В сентябре я ожидал стремительный рост проекта, ведь открываются школы и институты. Моей ЦА снова нужен доступ к безграничной информации! «Да я же машину себе так куплю!», — в какой-то момент подумал я.Но 30-го августа мне прилетело вот такое оповещение в Яндекс Вебмастер:

Вы

Сначала я вообще не понял, что это. Потом погуглил и выяснил, что «мимикрия» — это когда сайт пытается выдавать себя за другой популярный сайт.

ChatGPT я не прикидываюсь (да и не похож), а просто почти честно использую их имя для продвижения в SEO. Странно. ��

Написал в поддержку Яндекса, что именно не так. Поддержка не помогла. Сказала, что это всё алгоритмы поиска и сами разбирайтесь. «Если всё исправите, через 30 дней сайт вернётся в выдачу».

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

Методом тыка я пытался и переписывать текст, и убирать слово «ChatGPT» из названия, и всё такое. Ничего в итоге не помогло.

С этого момента началась плавная смерть проекта.

В силу того, что я относился к проекту как к «временной темке» и ожидал чего-то такого, я погрустил пару дней и успокоился. Да и Яндекс поступил, наверное, правильно (ведь не зря такой алгоритм придуман). Тем более Яндекс работает иногда пободрее Google, да и вообще сервисы у него все (почти) классные.

Единственное, что хотелось бы в этой ситуации — это понятным языком сказать, что именно не так и помочь исправить сайт не «за 30 дней методом тыка», а оперативно.

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

Какие планы дальше

У сайта идут единичные регистрации и есть ~60 000 рублей подписок в месяц. Поэтому проект я поддерживаю, не особо активно развивая новые функции. Когда подписки совсем угаснут и сервера будут стоить дороже — закрою его. Но это, чувствую, будет года через пол, если не позже.

Выводы

За полгода я запустил проект и смог заработать на нём.
В этот раз мой опыт помог мне сделать проект прибыльным почти со старта. Я получил полезные инсайты, узнал о новых проблемах и укрепил понимание, что возможности — они везде (ведь мне говорили: — «Да зачем сайт с ChatGPT, сейчас полно ботов и вообще VPN можно включить»).

Ну а сейчас основной мой проект — это Telegram чат для сайта (если интересно, ссылка в профиле). Более надёжно и интересно, прибыль тоже есть. Поэтому все свободные силы я продолжаю вкладывать в него (ссылка в профиле или загуглите «КликЧат»). О работе над ним напишу в других статьях.

Надеюсь, мой опыт вдохновит вас начинать (или продолжать) что-то свое.

Поставьте лайк, если статья оказалось полезной или интересной, пожалуйста

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *