Как стать автором
Обновить

Комментарии 81

Спасибо. Статья интересная. Особенно по поводу статусбара.
Однако, утверждение, что все приложения должны быть Single Activity- спорное. Этот подход хорош, когда у Вас типичное «конвейерное приложение» а-ля веб-клиента. Если писать что-то по-сложнее или добавить к существующему Single Activity-приложению действительно тяжелый, с точки зрения ресурсов устройства, функционал- с этого момента могут возникнуть проблемы.
Надо ли говорить, что Single Activity-приложение, чаще всего, представляет собой изобилие рукотворных утечек. Потому работа с таким подходом, если всё делать по совести, вызывает не меньше сложностей в сравнении с multiple activity.
Потому важно иметь права как на болид F1, так и на старый добрый вездеход. И понимать, на чем ехать в конкретном случае. Иногда комбинировать.
Иначе это- просто мода. По типу «asynctask- зло» или «dagger нужен везде». Каждому инструменту- своё применение.
Надо ли говорить, что Single Activity-приложение, чаще всего, представляет собой изобилие рукотворных утечек

Надо :) Почему риск утечек памяти (если вы о них) в Single Activity выше, чем в любом другом подходе?

Согласен, надо. Не хотел приводить пример потому, что коммент был и без того слишком развернутый.
Если просто- activity, которые есть в backstack, android может выгружать из памяти при ее нехватке, оставляя в памяти только ту, на которою Вы смотрите (и ближайшие к ним), а при возврате восстановить из saved state. Таким образом ресурсы, которые выделены на Ваше приложение, со стороны UI (ну или view/presentation-слоя, смотря что используете) не залочены.
В случае подхода с single activity в backstack все фрагменты держатся в памяти. И то, на что Вы ссылаетесь жесткими ссылками из фрагментов- тоже. Да, в простых случаях это- спички. Но когда в стэке лежит что-то тяжелое, да еще и много инстансов (например, карты, или другие тяжелые элементы/api)- то потребление памяти сильно подскакивает, а вместе с ним падает оперативность и отзывчивость приложения из-за частых вызовов GC и нагрузки в принципе.
Да, Вы можете сказать, что «вот на моем флагмане (или мощном B-бренде) всё хорошо». Однако, часто, бОльшая часть ЦА не располагает достаточно мощными устройствами. И современные low-end устройства с 2-3гб оперативы всё равно испытывают нехватку, и там этот вопрос стоит острее.
Да, конечно, это можно решить\обойти в рамках Single Activity- это вопрос навыков. Однако, по моему опыту, такие решения весьма не тривиальны и многие просто на это забивают.
Если просто- activity, которые есть в backstack, android может выгружать из памяти при ее нехватке, оставляя в памяти только ту, на которою Вы смотрите

Не может. Я об этом специально написал дальше в статье.
Только процесс может быть выгружен из памяти при нехватке ресурсов.

Это будет полноценное отдельное приложение. Там ничего не останется от общего стека и общих расшаренных моделей. Поэтому и разработка этого Activity будет не разработкой экрана, но полноценного приложения. Если мне не изменят память, то там и новый инстанс Application создастся. Я могу ошибаться, такой подход совсем не рядом с обычным стеком экранов, и применяется исключительно для специальных кейсов (когда надо выделить больше памяти на процесс).


А про вендоров — это еще одна из причин делать все в одном Activity, меньше полагаешься на систему, больше работаешь внутри со своими классами.

Не совсем так, всё зависит от того, где у Вас data source. Да, процесс отдельный, но взаимодействовать с ним проще, чем кажется.
Это будет полноценное отдельное приложение.
Когда будет отдельный проект- то тогда и будет отдельное полноценное приложение. До тех пор у Вас есть логика, которую можно переиспользовать

А откуда эта информация, что отдельное activity из бекстека не может быть выгружено? Этот контракт где-то описан? Я, конечно, глубоко не копал, но при использовании sgs3 даже на глаз было, что activity в различных приложениях при возврате к ним из бекстека иногда пересоздаются.

вызывает не меньше сложностей в сравнении с multiple activity.

сложность это вопрос образованности и опыта, а я попытался доказать, что в "классическом" подходе есть фатальные проблемы, которые не решить сколь угодно сложным кодом

Согласен. Но, разработка в принципе состоит из проблем и их решений.
Если покопаться, такие проблемы есть и с single activity. И потому, что оба этих подхода имеют проблемы разного характера, они оба имеют право на существование в продакшене. Что из этого выбирать- вопрос опыта разработчика.
Однако, согласитесь, что заголовок и некоторые доводы из статьи весьма провокационны. Вы сравниваете умение применять повсеместно single activity с правами F1, что, всё-таки, расходится с реальностью. Я прочитал статью с удовольствием, но такие моменты смущают.
Лично я (как, думаю, и многие) вижу это так- «Либо ты пишешь только single activity, либо ты- плохой разраб»
И ладно, если в это верят отдельные разрабы- это вопрос доверчивости и опыта каждого. Но, читая подобные статьи на хабре, в это начинает верить техническое руководство. А это- уже проблема…
такие проблемы есть и с single activity

Так я прошу их привести и обсудить! Прошу конструктивный диалог) Я же хочу о них узнать, чтобы подстелить себе соломку.

Если писать что-то по-сложнее...

Пока это просто неподкрепленное фактами утверждение. Пример: приложение Uber с 1700+ градл модулями в проде написано целиком в одном Activity. Как вы считаете это тоже "типичное «конвейерное приложение»"?


Приведите пример, который на нескольких Activity решается бесспорно лучше, и мы детально обсудим)

приложение Uber с 1700+ градл модулями
На мой скромный взгляд, Uber не является хорошим примером приложения. Как и многие от крупных брендов.
Примеры- сервисы синхронизации, аутентикаторы, камеры различной насыщенности с кучей точек входа, приложения с широким функционалом шаринга, чатики с быстрым ответом, навороченные радио и плееры… В общем примеров много, и перечислил далеко не все. В основном те приложения, которые часто контактируют с пользователем посредством точек входа ОС, нежели напрямую

Для этого вы должны были обратить особое внимание на самую последнюю часть в статье!

Всё верно, там приведен пример отдельного запуска activity и дальнейшего переиспользования кода. А когда таких активити будет много? А когда там будет работа с различными payload? А если другую (уже имеющуюся) активити проще вызвать сразу, а не городить новый стек? А если захочется еще и в recents сохранять?
Какой же это тогда single activity?
Поймите правильно, подавляющее большинство задач можно реализовать обоими путями, но не надо позиционировать свой подход как «истинно верный»…
На мой взгляд, надо брать то, что дает меньше сопротивления в каждом конкретном случае
Какой же это тогда single activity?

Вот поэтому я очень просил в статье внимательно читать последний абзац.
Это надо уметь сохранить single-activity.
"дает меньше сопротивления" — и перечитав недостатки нескольких Activity в приложении надо все писать в одном. И принять подход как "«истинно верный»" :-)

Вы, похоже, половины из моего комментария не прочитали…
Вы написали:
узнать высоту клавиатуры (это реально!)

а в рамках вашей статьи не показали пример

Потому что статья не об этом, но там специально выделено: Всем изучать WindowInsets!
Когда появляется клавиатура, то система сообщает ее размер через применение WindowInsets к Activity, именно так и можно узнать ее высоту.

Как узнать высоту клавиатуры, если приложение запущено в split screen?)

А если клавиатура не налезет на наше Activity, то ее знать и не надо)
И в таком случае система и не кинет нам WindowInsets

Спасибо за статью!
Пара вопросов:


1) Темы фрагментам задаёте в onCreateView, создавая ContextThemeWrapper? Что-то вроде:


override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View {
    val contextThemeWrapper = ContextThemeWrapper(activity, R.style.Theme_MyApp_Dark)
    val themedInflater = inflater.cloneInContext(contextThemeWrapper)
    return themedInflater.inflate(R.layout.fragment_smoething_dark, container, false)
}

?


2) Передача данных с экрана на экран. Допустим, у нас в приложении есть экран, который умеет сканировать паспорт, и возвращать объект с паспортными данными. Если это Activity, то всё понятно, у нас одна точка выхода: onActivityResult(). Перегрузили, распарсили, передали куда надо.
Как правильно сделать это через фрагмент? Можете чуть подробнее рассказать о подходе к реализации "реактивного подхода", который использует ваша команда?

1) Нет, просто в разметке
2) два варианта:


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

А как можно обобщить когда множество мелких/и не очень объектов передаются между разными фрагментами? Сделать некий SharedFragmentExchangeData модель и везде её инжектить?
Отвечу на первый вопрос.
Можно повесить тему непосредственно, на root элемент в layout'е. И это будет даже лучше, чем указывать это в манифесте, т.к. всё что касается отображения конкретного окна будет в одном месте.
Более интересные вопрос что делать с штуками типа windowBackground. Тут видимо придётся либо использовать один background на всё приложение, либо в каждом фрагменте указывать фон явно.

про windowBackground я отдельно сказал в разделе про Splash Screen

Ну вот, получается, что если мы хотим Single-Activity, то получаем overdraw на каждом экране, т.к. нам нужно нарисовать фон ещё и поверх splash'а. Не смертельно, но всё же.

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

Может я вас не понял, но вы сами говорите, что у вас есть splash, который задаётся как windowBackground. У вас splash и является фоном для всех фрагментов? Или вы его в своей активити/фрагменте в рантайме подменяете, как только всё загрузилось?

да, подменяем сразу после старта Activity

Если нужно темировать не только верстку, а, например, и диалоги, то все же придется использовать врапер.

внутри диалога есть возможность указать любую тему

Тоже интересно посмотреть на грамотные примеры такой передачи данных между экранами. Просто, если говорить о всяких MVP/MVVP, то я вижу это как создание временного объекта при запуске нового фрагмента с которым будет работать Rx. Одно дело сделать один раз, другое дело как-то обобщить на всё приложение.
узнать правильную высоту статус бара (а не хардкодить 51dp)

А разве не 24dp все хардкодят? 51 — как-то много для статусбара :)


Вообще, кейс очень даже реальный. Успели с ним столкнуться. Несмотря на то, что на material.io написано, что высота statusbar должна быть 24 dp, по-факту, она может быть какой угодно. Особенно это заметно на девайсах с "челкой".

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

Больше похоже на высоту navigation bar.
Don't keep activities

Этот флаг для тестирования действительно позволяет найти некоторые баги в приложении, но поведение, которое он воспроизводит, НИКОГДА не встречается в реальности!


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

Don't keep Activities воспроизводит следующее поведение:
  • Свернули приложение
  • Запустили другое ресурсоемкое и/или дали телефону уйти в сон
  • Развернули свое приложение


Поднимется верхнее Activity стэка, при этом нижние будут задестроены.

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

Именно! Это то, о чем я и говорю) Процесс в вашем кейсе тоже будет пересоздан!


Только вместе с процессом могут умирать Activity, никогда не может быть такого, что вы находитесь в приложении на некотором экране, а в этот момент другие экраны (Activity) выгружаются. И именно такое поведение создает Don't keep Activities. Посмотрите внимательно в статью.


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


Другой случай — это поворот приложения. Там Activity будут лениво пересоздаваться при возврате по стеку назад.

для инициализации нативных библиотек, камеры, VR и т.д.

Это еще одна вариация самого последней части в статье. Я для этого пишу отдельное "приложение", то есть Activity никак не связанное с основным приложением. Там нет общего слоя данных, общих стилей, общих моделек — ничего. И общаюсь с ним только через Intent.
Но этот кейс редкий и поэтому такой особенный.

terrakok
android:windowSoftInputMode=«adjustResize» — установили
Если вы используете для обработки прозрачного статус-бара подход из прошлого раздела, то обнаружите досадную ошибку: если фрагмент успешно «подлезал» под статус бар, то при появлении клавиатуры он сожмется сверху и снизу, так как и статус бар и клавиатура внутри системы работают через SystemWindows.

т.е на анимации, что вы показали при сворачивании клавиатуры заголовок не переместился в первоначальное положение:
для решения проблемы надо переопределить onApplyWindowInsets и вызывать dispatchApplyWindowInsets у child?

да, через onApplyWindowInsets реализуется любое поведение. То есть вы можете просто не передавать инсеты сверху, и фрагмент останется под статус баром

Спасибо за супер статью :)

Перевести-то вы GitFox на single-activity перевели, вот только приложение лишилось анимаций и появились баги с бэкстеком вроде белого экрана при нажатии кнопки назад на первом экране. И это как бы намекает, что подход не идеален.

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

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

То что приложение без анимаций переходов между экранами работает хуже чем с анимациями — это лично ваше представление о качестве работы приложения.


GitFox — это не иллюстрация подхода, это мой личный проект "для души". Просто там я это использую.

У меня была проблема, которую не получилось нормально решить в рамках одной активити: по нажатию на превью картинки, должна проиграться анимация перехода картинки на фулскрин, а по свайпу картинка улетает обратно. Такое поведение есть в настройках телеграмма, когда нажимаешь на свою аватарку. Дело в том, что транзишн на фрагментах не работает, если вызывать метод add, только replace или remove, а поскольку нам нужно, чтобы под полноэкранной картинкой оставался прошлый фрагмент, нам такой вариант не подходит. Есть вариант сделать превьюху в чайлд фрагменте, внутри основного и реплейсить на фуллскрин франмент, тогда анимация работать будет, но если превьюха находится внутри CollapsingToolbarLayout, то контейнер с превьюхой не получиться сделать во весь экран и реплейс будет происходить не так, как мы задумали. В итоге не придумал ничего лучше кроме как использовать отдельную активити для фулскрин галереи. Думаю, в данном случае это нормально, но интересно, можно ли эту задачу решить без дополнительной активити и без создания анимаций вручную.

Как вы правильно заметили: проблема из-за "транзишн на фрагментах не работает, если вызывать метод add". Это косяк стандартных анимаций.
У себя мы просто сделали сами эту анимацию, а галерея открывается в фулскрин диалоге, так как отдельное Activity несет все проблемы из статьи.

А без фрагментов трудно сделать?

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

Conductor вам в помощь

Удивительно, что кто-то еще не пишет приложения в Single-Activity. В любой момент может потребоваться поместить экран в NavigationDrawer, ViewPager или BottomBar. В этом случае без кучи фрагментов в одной Activity не обойтись.

У меня ViewPager без фрагментов

Интересно получается, что мейнстрим пошел по пути кроссплатформенных фреймворков (например Qt), когда все внутренние страницы приложения живут внутри одной системной Activity. Чем меньше зависимость от поведения системы, тем меньше головной боли, однако каждый пилит свои реализации и user experience в одинаковых задачах отличается. И столько библиотечных возможностей пылится…

С фрагентами конечно проще, но в целом писать одно мучение.


Будем реалистами, зайдем на гитлаб GitFox и посмотрим статистику, сотни комитов, несколько авторов. Установим приложение (на первом же экране кнопка back ведет на пустое место, ошибка в токене никак не сообщается юзеру, просто слетает форма и все), по сути это клиент для части гитлабовского рест апи. На каком-нибудь реакте или ионике или том же новом флаттере это все заняло бы максимум неделю времени: сделать апи клиент, закодить n экранов/списков, без лишних размышлений о потоках, активити, лайфсайклах, листвью, темах, стилях и тп.

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

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

Замечательная статья, спасибо!

Немного не понятен один вопрос. Предположим мы берем приложуху с не залоченным экраном, а нормальное полностью адаптивное приложене, обрабатывающие повороты и разные размеры и все что только нужно. Вы предлагаете использовать SinglActivity и увязывать ее жизненный цикл с жизненным циклом приложения. Если я правильно Вас понял, то такой подход возможен только при android:configChanges=«orientation», в противном случае наша активити при повороте будет пересоздаваться.
Резюмируя — для адаптивных приложении Вы советуете использовать android:configChanges=«orientation»?

Нет, вы неправильно поняли. Прошу, перечитайте еще раз. Там прямо про поворот экрана отмечено "Вот верное решение" и так далее

override fun onResume() {
   super.onResume()
   activity?.requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_SENSOR
}

override fun onPause() {
   super.onPause()
   activity?.requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_PORTRAIT
}


Это предложение справедливо для приложения которое в зависимости от экрана либо обрабатывает либо нет поворот.
А я все же говорю, что мы априори обрабатываем повороты всех экранов всегда.
т.е. мы все же берем как описано у Вас в манифесте

<activity android:name=".AppActivity"
android:configChanges=«orientation» />

нам же не нужно устанавливать в моем случае флаги SCREEN_ORIENTATION_PORTRAIT и SCREEN_ORIENTATION_SENSOR

Зачем тогда вообще блокировать поворот? Обрабатываете и все.
Не понимаю..

Я так и говорю. Обрабатываем повороты, но если Вы предлагаете увязать жизненный цикл активити к циклу приложения то не обойтись без android:configChanges=«orientation» (я так понял другого варианта нет?), что нам не мешает обрабатывать повороты, но придется в сложных случаях обрабатывать колбэк onConfigurationChanged()

Просто сейчас реализую Single Activity App со всеми поворотами, но в моем проекте она все же пересоздается при поворотах т.е. параметра android:configChanges в манифесте нет.

А, я понял, хороший вопрос. Ответ такой: в Activity есть системный флаг isFinishing(), который говорит о том срабатывают коллбеки ЖЦ из-за завершения экрана или это просто поворот (пересоздание).

Да, спасибо!
Буду разбираться.
а как насчет retained фрагмента? Будет работать «из коробки» и без магических флагов типа isFinishing.

retained фрагменты как работали, так и работают. мы их правда не используем, достаточно Toothpick с простым управлением DI-скоупами

Наверное retain фрагменты не подойдут, если у нас clear arcitecture or mvvm.
По сути retain позволяет сохранять данные во фрагменте но все равно пересоздает UI. А если мы храним данные в другом слое необходимости в этом нет.

Если только мы не привязываем один хостовой фрвгмент к ЖЦ приложения для обработки чего либо ранее бывшего в application))(но я так не делал)

Вообще странно, что по умолчанию GOOGL не сделал активити android:configChanges=«orientation» а фрагмент retain, в противном случае это похоже уже на костыль против порочной логики пересоздания. Хотя я могу заблуждаться
А если вам не надо сохранять данные или запускать/останавливать сервисы etc., то зачем привязывать ЖЦ activity к ЖЦ приложения?

Вообще странно, что по умолчанию GOOGL не сделал активити android:configChanges=«orientation» а фрагмент retain
Да нет в этом ничего странного. configChanges=«orientation» не стоит по дефолту, т.к. могут быть разные layout'ы для разных ориентаций. А фрагменты не retained, потому что содержат UI и должны пересоздаваться по такими же правилам, как и activity.

Не понимаю, как при configChanges=«orientation» вы хотите не пересоздать Activity, а только повернуть верстку? Мы обязаны вызвать super метод, а он пересоздаст все.

У нас тут какое-то недопонимание, я не понял что вы сейчас хотели сказать. При повороте окна + configChanges=«orientation» как раз ничего не пересоздаётся.
А отвечал я комментатору выше, почему configChanges=«orientation» не стоит по-умолчанию.
вы правы, нужно добавить дополнительные флаги что бы активити не пересоздавалось:
android:configChanges=«orientation|screenSize»

удивительно что вы это пропустили.

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


Дополнил статью.

Я согласен что пересоздание активити нужно учитывать, но в случае поворота экрана я на 100% предпочитаю не пересоздавать его, это удобнее и разработчику и пользователю (меньше тормозит апп, меньше мест где разработчик может допустить баг:) )
конечно такое поведение может не подойти некоторым приложениям где для разных ориентаций разные макеты, но при желании смену вьюшек\их расположения можно наколхозить и в onConfigurationChanged

Так, а чем тогда вам android:screenOrientation="portrait" не подходит? Если верстка не меняется, то и поворачивать не надо

Верстка не меняется — имеется ввиду что никакие layoutParams не меняются, не добавляются вьюшки, не меняется иерархия.

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

пример: в приложениях Telegram/WhatsApp на экране списка чатов как раз этот случай — иерархия\layoutParams не меняются, а вьюшки растягиваются при повороте экрана.

Да, но тут стоит учесть, что во многих приложениях используется Toolbar, а его высота берется системой из ?attr/actionBarSize, которая, в свою очередь, достается из системных параметров, зависящих от ориентации. Поэтому Toolbar в ландшафтной ориентации узкий.
Если сделать android:configChanges=«orientation|screenSize», то пересоздания не будет

Подобные моменты при желании можно обработать

Добавлю свои 5 коп.


Передача результата между экранами

Еще вариант: гугл рекомендует в AAC для этого использовать AAC ViewModel.

НЛО прилетело и опубликовало эту надпись здесь
Допустим есть single activity app — пусть приложение-чат, при запуске поднимается сокет, получается список чатов, можно заходить в чат и тд.

Один из функционалов — это встроенный крутой просмотрщик картинок из чатов.

Вы находитесь сейчас НЕ в приложении, а, например, в браузере, приложение свернуто, сокета нет и тд. Теперь приходит пуш нотификация, что пользователь прислал вам картинку. Вы хотите по открыть свой крутой просмоторщик картинок прямо из пуша — благо там есть вся информация — url картинки, кто прислал. При этом по беку надо вернуться именно туда, откуда пришли — в браузер.

Вызывать single activity, поднимать сокет, прокидывать на экран, и кастомно обрабатывать back? Или все-таки просмоторщик картинок завернуть в отдельную активити, которую и вызвать из pending intent при клике на пуш?

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

Самое противное при single activity это если есть большое кол-во фрагментов, получается большая стейтмашина. Но эту проблему может решить рано или поздно google, например с navigation component в AAC.

Этот случай я описал подробно в последнем абзаце.
Зачем вам открывать сокет, если вы открыли просмотрщик из нотификации и хотите сразу выйти по кнопке назад?
Сделайте фрагмент-просмотрщик. В основном приложении открывайте его в активити где все остальные экраны.
А для запуска извне открывайте SpecialActivity, которое будет переиспользовать ваш фрагмент-просмотрщик для внешних вызовов.

Зарегистрируйтесь на Хабре, чтобы оставить комментарий