Экран "Недавние" (также называемый экраном обзора, списком недавних задач или экраном недавних приложений) – это системный интерфейс, в котором перечислены недавно запущенные действия и задачи. Пользователь может просматривать список, выбирать задачу, которую нужно возобновить, или удалять задачи из списка, проводя по ним пальцем.
На экране "Недавние" используется модель, ориентированная на документы, которая была представлена в Android 5.0 (уровень API 21). В ней несколько экземпляров одного и того же действия, содержащего разные документы, могут отображаться как задачи на экране "Недавние". Например, в Google Диске может быть задача для каждого из нескольких документов Google. Каждый документ отображается как задача на экране "Недавние":
Другой распространенный пример – когда пользователь нажимает Поделиться > Gmail в браузере. Откроется экран Написать приложения Gmail. Если нажать кнопку "Недавние", Chrome и Gmail будут показаны как отдельные задачи.
Обычно система сама определяет, как задачи и действия отображаются на экране "Недавние". Вам не нужно изменять это поведение. Однако вы можете настроить, как и когда действия будут появляться на экране "Недавние".
Класс ActivityManager.AppTask позволяет управлять задачами, а флаги класса Intent – указывать, когда действие добавляется на экран "Недавние" или удаляется с него. Кроме того, атрибуты <activity> позволяют задать поведение в манифесте.
Как добавить задачи на экран "Недавние"
Использование флагов класса Intent для добавления задачи позволяет лучше контролировать, когда и как документ открывается или повторно открывается на экране "Недавние". При использовании атрибутов <activity> можно выбрать, открывать ли документ в новой задаче или использовать для него существующую.
Как добавить задачу с помощью флага Intent
При создании нового документа для объекта activity вы вызываете метод startActivity(), передавая ему намерение, которое запускает объект activity. Чтобы вставить логический разрыв, в результате которого система будет обрабатывать ваше действие как новую задачу на экране "Недавние", передайте флаг FLAG_ACTIVITY_NEW_DOCUMENT в методе addFlags() объекта Intent, который запускает действие.
Если при создании нового документа вы установите флаг FLAG_ACTIVITY_MULTIPLE_TASK, система всегда будет создавать новую задачу с целевым действием в качестве корневого. Этот параметр позволяет открывать один и тот же документ в нескольких задачах. В следующем коде показано, как основное действие делает это и запускает новое Activity из вашей composable-функции:
private fun newDocumentIntent(context: Context): Intent = Intent(context, NewDocumentActivity::class.java).apply { addFlags(Intent.FLAG_ACTIVITY_NEW_DOCUMENT or Intent.FLAG_ACTIVITY_RETAIN_IN_RECENTS) putExtra(KEY_EXTRA_NEW_DOCUMENT_COUNTER, documentCounter++) } @Composable fun CreateDocumentButton() { val context = LocalContext.current Button( onClick = { val intent = newDocumentIntent(context) // Add FLAG_ACTIVITY_MULTIPLE_TASK if needed based on state context.startActivity(intent) } ) { Text("Create New Document") } }
Когда основное действие запускает новое действие, система ищет среди существующих задач ту, намерение которой соответствует имени компонента намерения и данным намерения для действия. Если задача не найдена или намерение содержит флаг FLAG_ACTIVITY_MULTIPLE_TASK, создается новая задача с действием в качестве корневого элемента.
Если система находит задачу, намерение которой совпадает с названием компонента намерения и данными намерения, она выводит эту задачу на передний план и передает новое намерение в onNewIntent().
Новое действие получает намерение и создает новый документ на экране "Недавние", как показано в следующем примере:
class DocumentCentricActivity : ComponentActivity() { private var documentState by mutableStateOf( DocumentState( count = 0, textResId = R.string.hello_new_document_counter ) ) override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) val initialCount = intent.getIntExtra(KEY_EXTRA_NEW_DOCUMENT_COUNTER, 0) documentState = documentState.copy(count = initialCount) setContent { MaterialTheme { DocumentScreen( count = documentState.count, textResId = documentState.textResId ) } } } override fun onNewIntent(newIntent: Intent) { super.onNewIntent(newIntent) // If FLAG_ACTIVITY_MULTIPLE_TASK has not been used, this Activity is reused. documentState = documentState.copy( textResId = R.string.reusing_document_counter ) } data class DocumentState(val count: Int, @StringRes val textResId: Int) companion object { const val KEY_EXTRA_NEW_DOCUMENT_COUNTER = "KEY_EXTRA_NEW_DOCUMENT_COUNTER" } } @Composable fun DocumentScreen(count: Int, @StringRes textResId: Int) { Column( modifier = Modifier .fillMaxSize() .padding(16.dp), verticalArrangement = Arrangement.Center ) { // UI reacts to whichever string resource ID was passed down Text(text = stringResource(id = textResId)) Spacer(modifier = Modifier.height(8.dp)) Text(text = "Counter: $count") } }
В приведенном выше коде Activity обрабатывает маршрутизацию на уровне ОС (onCreate и onNewIntent), а функция @Composable отвечает только за отрисовку пользовательского интерфейса на основе предоставленного состояния.
Как добавить задачу с помощью атрибута действия
В манифесте действия также можно указать, что оно всегда запускается в новой задаче. Для этого используется атрибут android:documentLaunchMode тега <activity>.
У этого атрибута есть четыре значения, которые определяют, что происходит, когда пользователь открывает документ в приложении:
intoExisting- В действии повторно используется существующая задача для документа. Это то же самое, что установить флаг
FLAG_ACTIVITY_NEW_DOCUMENTбез флагаFLAG_ACTIVITY_MULTIPLE_TASK, как описано в разделе Использование флага Intent для добавления задачи. always- Действие создает новую задачу для документа, даже если он уже открыт. Использование этого значения аналогично установке флагов
FLAG_ACTIVITY_NEW_DOCUMENTиFLAG_ACTIVITY_MULTIPLE_TASK. none- Действие не создает новую задачу для документа. На экране "Недавние" действие обрабатывается по умолчанию. В нем отображается одна задача для приложения, которая возобновляется с того действия, которое пользователь вызывал последним.
never- Действие не создает новую задачу для документа. Если задать это значение, оно переопределит поведение флагов
FLAG_ACTIVITY_NEW_DOCUMENTиFLAG_ACTIVITY_MULTIPLE_TASK. Если в намерении задан один из этих флагов, а на экране "Недавние" отображается одна задача для приложения, оно возобновляет работу с того действия, которое пользователь вызывал последним.
Удалить задачи
По умолчанию задача документа автоматически удаляется из списка недавних приложений, когда ее действие завершается. Это поведение можно переопределить с помощью класса ActivityManager.AppTask, флага Intent или атрибута <activity>.
Вы можете полностью исключить задачу из списка недавних, задав для атрибута <activity> android:excludeFromRecents значение true.
Вы можете задать максимальное количество задач, которые приложение может включать в экран "Недавние", установив для атрибута <activity> android:maxRecents целочисленное значение. Когда максимальное количество задач будет достигнуто, задача, которая использовалась раньше всех, исчезнет с экрана "Недавние". По умолчанию используется значение 16, а максимальное значение – 50 (25 на устройствах с небольшим объемом памяти). Значения меньше 1 недопустимы.
Как удалять задачи с помощью класса AppTask
В действии, которое создает новую задачу на экране "Недавние", вы можете указать, когда нужно удалить задачу и завершить все связанные с ней действия, вызвав метод finishAndRemoveTask():
@Composable fun RemoveTaskButton() { val context = LocalContext.current Button( onClick = { // It is good practice to remove a document from the overview stack if not needed anymore. (context as? Activity)?.finishAndRemoveTask() } ) { Text("Remove from Recents") } }
Хранить завершенные задачи
Если вы хотите сохранить задачу на экране "Недавние", даже если ее действие завершено, передайте флаг FLAG_ACTIVITY_RETAIN_IN_RECENTS в методе addFlags() намерения, которое запускает действие.
private fun newDocumentIntent() = Intent(this, NewDocumentActivity::class.java).apply { addFlags(Intent.FLAG_ACTIVITY_NEW_DOCUMENT or android.content.Intent.FLAG_ACTIVITY_RETAIN_IN_RECENTS) putExtra(KEY_EXTRA_NEW_DOCUMENT_COUNTER, getAndIncrement()) }
Чтобы добиться того же эффекта, задайте для атрибута <activity> android:autoRemoveFromRecents значение false. Значение по умолчанию – true для действий с документами и false для обычных действий. Этот атрибут переопределяет флаг FLAG_ACTIVITY_RETAIN_IN_RECENTS.
Включить отправку URL из списка недавних (только для Pixel)
На устройствах Pixel с Android 12 или более поздней версией пользователи могут делиться ссылками на недавно просмотренный веб-контент прямо с экрана "Недавние". После просмотра контента в приложении пользователь может перейти на экран "Недавние", найти приложение, в котором он просматривал контент, и нажать кнопку ссылки, чтобы скопировать URL или поделиться им.
Чтобы включить ссылки на недавние действия, разработчику приложения нужно предоставить веб-интерфейс и переопределить метод onProvideAssistContent(), как показано в примере ниже.
class MainActivity : ComponentActivity() { // Track the current URL as state so the UI can update it during navigation private var currentWebUri by mutableStateOf("https://example.com/home") override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContent { AppTheme { // Pass a lambda to your Compose UI so it can update the URL state // as the user navigates through your app. MainScreen( onPageChanged = { newUrl -> currentWebUri = newUrl } ) } } } override fun onProvideAssistContent(outContent: AssistContent) { super.onProvideAssistContent(outContent) // The system calls this when the user enters the Recents screen. // Provide the active URI tracked by the Compose state. outContent.webUri = Uri.parse(currentWebUri) } }