Activities
Activity in Android represents a single screen. Unlike iOS, where the whole app lives on a single screen, Android apps are made up of multiple activities that users can navigate between. Each activity is independent and has its own lifecycle.
However activities is not the only way to create screens in Android. You can also use → fragments, which are modular sections of an activity. Fragments allow for more flexible UI designs.
Starting an Activity
In classic Android development, you are starting an Activity via intents. With Droid framework you can do it in a more Swiftly way.
Simply initialize the activity class you want to start and call startActivity method with it:
let secondActivity = SecondActivity()
startActivity(secondActivity)
This way gives you an ablity to pass parameters to the activity via its initializer rather than using intent extras.
You also can start multiple activities at once by passing an array of activities to startActivity method:
let activity1 = FirstActivity()
let activity2 = SecondActivity()
startActivity(activity1, activity2)
And you can start activities for result using startActivityForResult method:
let thirdActivity = ThirdActivity()
startActivityForResult(thirdActivity, requestCode: 1001)
The way with intents is also supported, mostly for an ability to start activities from outside of the Droid framework.
let intent = Intent(...)
startActivity(intent)
Launcher Activity
Some activities can be designated as launcher activities. These are the entry points of your app, the first screen users see when they launch your app from the home screen or app drawer.
Normally, the main launcher activity is defined in your app's manifest file with a specific intent filter. But with Droid framework you have to define it simply in your activity class:
final class MainActivity: AppCompatActivity {
override class var intentFilter: IntentFilter? { .mainLauncher }
/// ...
}
This will automatically set up the necessary intent filter in the generated Android manifest file.
Creating an Activity
To create a new activity you need to do only two things.
Activity class
Create a new class that extends Activity (or its subclass like AppCompatActivity, depending on your theme):
import Droid
final class YourActivity: AppCompatActivity {
override var body: any Body {
TextView("Hello world!")
.width(.matchParent)
.height(.matchParent)
.gravity(.center)
}
}
Manifest Declaration
Declare the activity in your App.swift file in the Manifest:
@main
public final class App: DroidApp {
@AppBuilder public override var body: Content {
/// ...
Manifest
/// ...
.activities(
/// ...
YourActivity.self,
/// ...
)
}
}
Corresponding Kotlin code for the activity will be generated automatically during the build process.
That's it! You have created a new activity for your Android app.
Lifecycle
Activities have a well-defined lifecycle managed by the Android operating system.
It's crucial to call super for each lifecycle method you override to ensure the proper functioning of the activity.
onCreate
Called when the activity is first created.
Here it receives an ActivityContext which can later be accessed via the self.context property anytime.
override func onCreate(_ context: ActivityContext) {
super.onCreate(context)
Log.i("onCreate")
}
body
This is where you build your UI declaratively using SwiftUI-like syntax.
Is a convenient minimalistic way to define the UI declaratively.
It is called after onCreate and before buildUI.
override var body: any Body {
TextView("Hello world!")
.width(.matchParent)
.height(.matchParent)
.gravity(.center)
}
It can be used in mix with buildUI method, or alone.
buildUI
Alternative to body, could be more useful if you need to declare some variables or calculate something before building the UI.
It is called after onCreate and body.
override func buildUI() {
super.buildUI()
let customText = "Hello from buildUI!"
body {
TextView(customText)
.width(.matchParent)
.height(.matchParent)
.gravity(.center)
}
}
onStart
Called when the activity is becoming visible to the user.
Happens after onCreate() (for a new instance) or onRestart() (for a restarted one).
override func onStart() {
super.onStart()
Log.i("onStart")
}
onRestart
Called after the activity has been stopped and right before it starts again.
This method is typically followed by calls to onStart() and then onResume().
Override onRestart() when you need to restore resources or state that were released or adjusted in onStop().
override func onRestart() {
super.onRestart()
Log.i("onRestart")
}
onResume
Called when the activity is about to resume interaction with the user.
At this point, the activity is in the foreground and ready for user input.
Resume any tasks that were paused (e.g. restarting animations or refreshing data).
override func onResume() {
super.onResume()
Log.i("onResume")
}
onPause
Called when the activity is about to enter a paused state.
This means another activity is coming into the foreground, but this one is still partially visible.
Use this to pause animations, video playback, or other ongoing tasks.
override func onPause() {
super.onPause()
Log.i("onPause")
}
onStop
Called when the activity is no longer visible to the user.
This happens when a new activity covers it or the app goes to the background.
Use this to release resources that don’t need to be kept while the activity is not visible.
override func onStop() {
super.onStop()
Log.i("onStop")
}
onDestroy
Called before the activity is completely destroyed.
This may occur when:
- The activity is finishing (finish() was called), or
- The system needs to reclaim memory.
Use this to perform final cleanup, like releasing resources or saving persistent data.
override func onDestroy() {
super.onDestroy()
Log.i("onDestroy")
}
onStateNotSaved
Called when the system is about to save the activity's state, but before that state has actually been committed.
Typically invoked before onStop().
Override this to do lightweight cleanup tasks that should not be persisted in the saved instance state.
override func onStateNotSaved() {
super.onStateNotSaved()
Log.i("onStateNotSaved")
}
onAttachedToWindow
Called when the activity's window has been attached to the window manager.
This is the point where your activity can safely interact with the actual window.
Useful for performing final UI setup that depends on the window being ready.
override func onAttachedToWindow() {
super.onAttachedToWindow()
Log.i("onAttachedToWindow")
}
onBackPressed
Called when the user presses the Back button.
By default, this finishes the current activity.
Override this to implement custom back navigation logic (e.g., showing a confirmation dialog before exit).
override func onBackPressed() {
Log.i("onBackPressed")
finish() // finish activity manually
}
onActivityResult
Called when an activity you launched with startActivityForResult() finishes.
Parameters:
- requestCode: The integer request code originally supplied
- resultCode: The integer result code returned by the child activity
- intent: Optional data returned from the child activity
- componentCaller: The caller component that initiated the request
Override this to handle results from sub-activities (e.g., picking an image or capturing video).
override func onActivityResult(
requestCode: Int,
resultCode: Int,
intent: Intent?,
componentCaller: ComponentCaller?
) {
super.onActivityResult(
requestCode: requestCode,
resultCode: resultCode,
intent: intent,
componentCaller: componentCaller
)
Log.i("onActivityResult request: \(requestCode), result: \(resultCode)")
}
onMultiWindowModeChanged
Called when the activity's multi-window mode changes.
If isInMultiWindowMode is true then activity is in multi-window mode now.
Override this to adjust your UI or behavior based on whether the activity is in multi-window mode.
override func onMultiWindowModeChanged(isInMultiWindowMode: Bool) {
super.onMultiWindowModeChanged(isInMultiWindowMode: isInMultiWindowMode)
Log.i("onMultiWindowModeChanged: \(isInMultiWindowMode)")
}
onRequestPermissionsResult
Called when the user responds to a permission request.
Parameters:
- requestCode: The integer request code originally supplied to
requestPermissions() - results: An array of
ActivityPermissionResultindicating the permissions requested and whether they were granted - deviceId: The ID of the device for which the permissions were requested
Override this to handle the user's response to permission requests.
override func onRequestPermissionsResult(
requestCode: Int,
results: [ActivityPermissionResult],
deviceId: Int
) {
super.onRequestPermissionsResult(
requestCode: requestCode,
results: results,
deviceId: deviceId
)
for result in results {
Log.i("Permission: \(result.permission), granted: \(result.granted)")
}
}
Learn how to easily request → permissions
Finishing Activity
finish
Call this when your activity is done and should be closed.
finish()
finishAffinity
Call this to finish the current activity and all activities immediately below it in the current task that have the same affinity.
finishAffinity()
finishAfterTransition
Call this to finish the activity after completing any ongoing transitions.
finishAfterTransition()
finishActivity
Call this to finish another activity that you had previously started with startActivityForResult().
finishActivity(requestCode: 1001)
Configuration
className
Class name of the activity which can be overriden for the new activity types. For example, classic activity class is android/app/Activity, AppCompatActivity is androidx/appcompat/app/AppCompatActivity, but you may want to create an activity based on some custom class and then you have to override this property.
open class MyCustomActivity: AppCompatActivity {
open class override var className: JClassName { "com/domain/Activity" }
}
gradle dependencies
When you are wrapping some custom activity class from a library, you may need to add corresponding gradle dependencies to your project. You can do it by overriding this property:
open class MyCustomActivity: AppCompatActivity {
open class override var gradleDependencies: [GradleDependency] {
[ .implementation("com.domain:library:1.0.0") ]
}
}
java imports
If you are using some custom activity class, you may need to add corresponding Java imports to the generated Kotlin code. You can do it by overriding this property:
open class MyCustomActivity: AppCompatActivity {
open class override var javaImports: [String] {
[ "com.domain.library.*" ]
}
}
parent class
The default is DroidActivity().
When you are creating an activity it is based on some activity class, you may need to override the parent class declaration in the generated Kotlin code:
open class MyCustomActivity: AppCompatActivity {
open class override var parentClass: String { "DroidActivity()" }
}
allowEmbedded
Indicate that the activity can be launched as the embedded child of another activity → docs
final class YourActivity: AppCompatActivity {
override class var allowEmbedded: Bool { true }
/// ...
}
allowTaskReparenting
/// Whether or not the activity can move from the task that started it to the task it has an affinity for when that task is next brought to the front – "true" if it can move, and "false" if it must remain with the task where it started → docs
final class YourActivity: AppCompatActivity {
override class var allowTaskReparenting: Bool { true }
/// ...
}
alwaysRetainTaskState
/// Whether or not the state of the task that the activity is in will always be maintained by the system – "true" if it will be, and "false" if the system is allowed to reset the task to its initial state in certain situations → docs
The default value is "false".
final class YourActivity: AppCompatActivity {
override class var alwaysRetainTaskState: Bool { true }
/// ...
}
autoRemoveFromRecents
Whether or not tasks launched by activities with this attribute remains in the overview screen until the last activity in the task is completed. If true, the task is automatically removed from the overview screen → docs
final class YourActivity: AppCompatActivity {
override class var autoRemoveFromRecents: Bool { true }
/// ...
}
banner
A drawable resource providing an extended graphical banner for its associated item → docs
final class YourActivity: AppCompatActivity {
override class var banner: String? { "@drawable/banner" }
/// ...
}
clearTaskOnLaunch
Whether or not all activities will be removed from the task, except for the root activity, whenever it is re-launched from the home screen – "true" if the task is always stripped down to its root activity, and "false" if not → docs
The default value is "false".
final class YourActivity: AppCompatActivity {
override class var clearTaskOnLaunch: Bool { true }
/// ...
}
colorMode
Requests the activity to be displayed in wide color gamut mode on compatible devices → docs
final class YourActivity: AppCompatActivity {
override class var colorMode: ActivityColorMode { .hdr }
/// ...
}
configChanges
Lists configuration changes that the activity will handle itself.
For the rest activity will be simply restarted → docs
final class YourActivity: AppCompatActivity {
override class var configChanges: [ActivityConfigChange] {
[ .keyboard, .orientation, .screenSize, .screenLayout ]
}
/// ...
}
directBootAware
Whether or not the activity is direct-boot aware; that is, whether or not it can run before the user unlocks the device → docs
final class YourActivity: AppCompatActivity {
override class var directBootAware: Bool { true }
/// ...
}
documentLaunchMode
Specifies how a new instance of an activity should be added to a task each time it is launched → docs
final class YourActivity: AppCompatActivity {
override class var documentLaunchMode: ActivityDocumentLaunchMode {
.intoExisting
}
/// ...
}
enabled
Whether or not the activity can be instantiated by the system – "true" if it can be, and "false" if not → docs
The default value is "true".
final class YourActivity: AppCompatActivity {
override class var enabled: Bool { false }
/// ...
}
excludeFromRecents
Whether or not the task initiated by this activity should be excluded from the list of recently used applications, the overview screen → docs
final class YourActivity: AppCompatActivity {
override class var excludeFromRecents: Bool { true }
/// ...
}
exported
Whether it can be launched by components of other applications → docs
If "true", the activity is accessible to any app, and is launchable by its exact class name.
If "false", the activity can be launched only by components of the same application, applications with the same user ID, or privileged system components.
"false" is the default value when there are no intent filters.
final class YourActivity: AppCompatActivity {
override class var exported: Bool { true }
/// ...
}
finishOnTaskLaunch
Whether or not an existing instance of the activity should be shut down (finished), except for the root activity, whenever the user again launches its task (chooses the task on the home screen) – "true" if it should be shut down, and "false" if not → docs
The default value is "false".
final class YourActivity: AppCompatActivity {
override class var finishOnTaskLaunch: Bool { true }
/// ...
}
hardwareAccelerated
Whether or not hardware-accelerated rendering should be enabled for this Activity – "true" if it should be enabled, and "false" if not → docs
The default value is "false".
final class YourActivity: AppCompatActivity {
override class var hardwareAccelerated: Bool { true }
/// ...
}
icon
An icon representing the activity → docs
final class YourActivity: AppCompatActivity {
override class var icon: String? { "@mipmap/ic_launcher" }
/// ...
}
roundIcon
A default round icon for all application components → docs
final class YourActivity: AppCompatActivity {
override class var roundIcon: String? { "@mipmap/ic_launcher_round" }
/// ...
}
immersive
Sets the immersive mode setting for the current activity → docs
final class YourActivity: AppCompatActivity {
override class var immersive: Bool { true }
/// ...
}
label
A user-readable label for the activity → docs
final class YourActivity: AppCompatActivity {
override class var label: String? { "My Activity" }
/// ...
}
launchMode
An instruction on how the activity should be launched → docs
final class YourActivity: AppCompatActivity {
override class var launchMode: LaunchMode { .singleTop }
/// ...
}
lockTaskMode
Specifies how this activity should behave when the device is in lock task mode → docs
final class YourActivity: AppCompatActivity {
override class var lockTaskMode: LockTaskMode { .ifWhitelisted }
/// ...
}
maxRecents
he maximum number of tasks rooted at this activity in the overview screen → docs
final class YourActivity: AppCompatActivity {
override class var maxRecents: Int { 3 }
/// ...
}
maxAspectRatio
The maximum aspect ratio the activity supports → docs
final class YourActivity: AppCompatActivity {
override class var maxAspectRatio: Float { 2.0 }
/// ...
}
multiprocess
Whether an instance of the activity can be launched into the process of the component that started it – "true" if it can be, and "false" if not → docs
The default value is "false".
final class YourActivity: AppCompatActivity {
override class var multiprocess: Bool { true }
/// ...
}
noHistory
Whether or not the activity should be removed from the activity stack and finished (its finish() method called) when the user navigates away from it and it's no longer visible on screen – "true" if it should be finished, and "false" if not → docs
The default value is "false".
final class YourActivity: AppCompatActivity {
override class var noHistory: Bool { true }
/// ...
}
parentActivityName
The class name of the logical parent of the activity → docs
final class YourActivity: AppCompatActivity {
override class var parentActivityName: AnyActivity.Type? {
ParentActivity.self
}
/// ...
}
persistableMode
Defines how an instance of an activity is preserved within a containing task across device restarts → docs
final class YourActivity: AppCompatActivity {
override class var persistableMode: PersistableMode { .acrossReboots }
/// ...
}
permission
The name of a permission that clients must have to launch the activity or otherwise get it to respond to an intent → docs
final class YourActivity: AppCompatActivity {
override class var permission: ManifestPermission? { .sendSms }
/// ...
}
process
The name of the process in which the activity should run → docs
final class YourActivity: AppCompatActivity {
override class var process: String? { ":remote" }
/// ...
}
relinquishTaskIdentity
Whether or not the activity relinquishes its task identifiers to an activity above it in the task stack → docs
final class YourActivity: AppCompatActivity {
override class var relinquishTaskIdentity: Bool { true }
/// ...
}
resizeableActivity
Specifies whether the app supports multi-window mode → docs
final class YourActivity: AppCompatActivity {
override class var resizeableActivity: Bool { true }
/// ...
}
screenOrientation
The orientation of the activity's display on the device → docs
final class YourActivity: AppCompatActivity {
override class var screenOrientation: ScreenOrientation {
.portrait
}
/// ...
}
The system ignores this attribute if the activity is running in multi-window mode
showForAllUsers
Whether or not the activity is shown when the device's current user is different than the user who launched the activity. You can set this attribute to a literal value – "true" or "false" – or you can set the attribute to a resource or theme attribute that contains a boolean value → docs
final class YourActivity: AppCompatActivity {
override class var showForAllUsers: Bool { true }
/// ...
}
stateNotNeeded
Whether or not the activity can be killed and successfully restarted without having saved its state — "true" if it can be restarted without reference to its previous state, and "false" if its previous state is required → docs
The default value is "false".
final class YourActivity: AppCompatActivity {
override class var stateNotNeeded: Bool { true }
/// ...
}
supportsPictureInPicture
Specifies whether the activity supports Picture-in-Picture display → docs
final class YourActivity: AppCompatActivity {
override class var supportsPictureInPicture: Bool { true }
/// ...
}
taskAffinity
The task that the activity has an affinity for → docs
final class YourActivity: AppCompatActivity {
override class var taskAffinity: String? { "com.example.task" }
/// ...
}
theme
The theme name applied to the activity as soon as it is created, before any views are instantiated. If not specified, the application-level theme defined in your app's manifest is used. If no application-level theme is set, the default system theme will be applied → docs
final class YourActivity: AppCompatActivity {
override class var theme: Style? { .material3DayNightNoActionBar }
/// ...
}
uiOptions
Extra options for an activity's UI → docs
final class YourActivity: AppCompatActivity {
override class var uiOptions: ApplicationUIOptions {
.splitActionBarWhenNarrow
}
/// ...
}
windowSoftInputMode
How the main window of the activity interacts with the window containing the on-screen soft keyboard → docs
final class YourActivity: AppCompatActivity {
override class var windowSoftInputMode: [WindowSoftInputMode] {
[.adjustResize, .stateHidden]
}
/// ...
}
intentFilter
Specifies the types of intents that an activity can respond to → docs
final class MainActivity: AppCompatActivity {
override class var intentFilters: [IntentFilter] {
[.action(.main).category(.launcher)]
}
/// ...
}
metaData
A name-value pair for an item of additional, arbitrary data that can be supplied to the parent component → docs
final class YourActivity: AppCompatActivity {
override class var metaData: [MetaData] {
[.name("key").value("value")]
}
/// ...
}