IEMS5722 Mobile Network Programming and Distributed
Server Architecture
Lecture 2
Lecturer: Ivan Ng
Department of Information Engineering
Topics
● Kotlin Concepts
● Activity
● Intent Filters
● Declare Permissions
● Managing the Activity Lifecycle
● Broadcast Receiver
Last lesson recap
Kotlin Concepts
[Link]
Basic Types
● In Kotlin, everything is an object in the sense that you can call member
functions and properties on any variable.
● While certain types have an optimized internal representation as primitive
values at runtime (such as numbers, characters, booleans and others), they
appear and behave like regular classes to you.
● Basic types used in Kotlin:
○ Numbers and their unsigned counterparts
○ Characters
○ Booleans
○ Strings
○ Arrays
Control Flow - If expression
● To use if in Kotlin, add the condition to check within parentheses () and the
action to take if the result is true within curly braces {}.
● You can use else and else if for additional branches and checks.
fun main(){
val heightAlice=100
val heightBob=120
var taller = heightAlice
if (heightAlice < heightBob)
taller = heightBob
// Uses an else branch
if (heightAlice > heightBob) {
taller = heightAlice
} else {
taller = heightBob
}
}
Control Flow - If expression
● You can also write if as an expression, which lets you assign its returned value
directly to a variable. In this form, an else branch is required.
fun main(){
val heightAlice=100
val heightBob=120
// Uses if as an expression
val taller = if (heightAlice > heightBob) heightAlice else heightBob
// Uses else if as an expression:
val heightLimit = 175
val heightOrLimit = if (heightLimit > heightAlice) heightLimit else if (heightAlice > heightBob) heightAlice else heightBob
println("Taller height is $taller")
// Taller height is 120
println("Height or limit is $heightOrLimit")
// Height or limit is 175
}
Control Flow - When expressions and statements
● when is a conditional expression that runs code based on multiple possible
values or conditions.
● Use when as an expression
fun main(){
val x = 1
// Returns a string assigned to the
// text variable
val text = when (x) {
1 -> "x == 1"
2 -> "x == 2"
else -> "x is neither 1 nor 2"
}
println(text)
}
Control Flow - When expressions and statements
● Use when as a statement
fun main(){
val x = 1
when (x) {
1 -> print("x == 1")
2 -> print("x == 2")
else -> print("x is neither 1 nor 2")
}
}
Control Flow - When expressions and statements
● If you use when as an expression, you must cover all possible cases.
● The value of the first matching branch becomes the value of the overall
expression.
● If you don't cover all cases, the compiler throws an error.
Control Flow - Other ways to use when
● Group multiple conditions into a single branch using commas:
when (ticketPriority) {
"Low", "Medium" -> print("Standard response time")
else -> print("High-priority handling")
}
Control Flow - Other ways to use when
● Use expressions that evaluate to true or false as branch conditions:
when (enteredPin) {
// Expression
[Link]() -> print("PIN is correct")
else -> print("Incorrect PIN")
}
Control Flow - Other ways to use when
● Check whether a value is or isn't contained in a range or collection using the in
or !in keywords:
when (x) {
in 1..10 -> print("x is in the range")
in validNumbers -> print("x is valid")
!in 10..20 -> print("x is outside the range")
else -> print("none of the above")
}
Returns and jumps
● Kotlin has three structural jump expressions:
○ return by default returns from the nearest enclosing function or anonymous function.
○ break terminates the nearest enclosing loop.
○ continue proceeds to the next step of the nearest enclosing loop.
● All of these expressions can be used as part of larger expressions:
val s = [Link] ?: return
Later you will know this:
The Elvis operator ?: checks whether the expression on its left ([Link]) is null.
If [Link] is not null, it proceeds with the value of [Link].
If [Link] is null, the expression after ?: is executed (in this case, return).
Break and continue labels
● Any expression in Kotlin may be marked with a label.
● Labels have the form of an identifier followed by the @ sign, such as abc@ or fooBar@.
● To label an expression, just add a label in front of it.
fun main() { fun main() { fun main() {
loop@ for (i in 1..100) { for (i in 1..100) { loop1@for (i in 1..100) {
println(i) println(i) loop2@for (j in 1..10) {
if (i==3) if (i==3) println(j)
break@loop break if (i==2)
} } break@loop1
} } if (j==3)
break@loop2
}
}
}
Return to labels
● In Kotlin, functions can be nested using function literals, local functions, and
object expressions.
● A qualified return allows you to return from an outer function.
fun main() {
listOf(1, 2, 3, 4, 5).forEach {
if (it == 3) return@forEach // local return to the caller of the lambda
print(it)
}
print(" done with implicit label")
}
Output:
1245 done with implicit label
Class
● Classes in Kotlin are declared using the keyword class:
class Person { /*...*/ }
Class - Constructors
● A class in Kotlin has a primary constructor and possibly one or more
secondary constructors.
● The primary constructor is declared in the class header, and it goes after the
class name and optional type parameters.
class Person constructor(firstName: String) { /*...*/ }
the constructor keyword can be omitted:
class Person(firstName: String) { /*...*/ }
Creating instances of a Class
● Example with an implicit constructor
// Define a simple class
class Person(val name: String, val age: Int) {
fun introduce() {
println("Hi, my name is $name and I am $age years old.")
}
}
// Function to call the class
fun main() {
// Create an instance of the Person class
val person = Person("Alice", 25)
// Call the 'introduce' function of the Person class
[Link]()
}
Creating instances of a Class
● Example with an explicit constructor
// Define a class with an explicit constructor
class Person constructor(val name: String, val age: Int) {
init {
// Initialization block that runs when the object is created
println("Person object created! Name: $name, Age: $age")
}
// Class function
fun introduce() {
println("Hi, my name is $name and I am $age years old.")
}
}
// Function to call the class
fun main() {
// Create an instance of the Person class
val person = Person("Alice", 25)
// Call the 'introduce' function of the Person class
[Link]()
}
Declaring properties
● Properties in Kotlin classes can be declared either as mutable, using the var
keyword, or as read-only, using the val keyword.
class Address {
var name: String = "Holmes, Sherlock"
var street: String = "Baker"
var city: String = "London"
var state: String? = null
var zip: String = "123456"
}
Remarks:
String? is a nullable type you will learn later
Declaring properties
● To use a property, simply refer to it by its name:
fun copyAddress(address: Address): Address {
val result = Address() // there's no 'new' keyword in Kotlin
[Link] = [Link] // accessors are called
[Link] = [Link]
// ...
return result
}
Member functions
● A member function is a function that is defined inside a class or object:
class Sample {
fun foo() { print("Foo") }
}
Member functions are called with dot notation:
Sample().foo() // creates instance of class Sample and calls foo
Lambda expressions
● Lambdas expression and anonymous function both are function literals
● These functions are not declared but passed immediately as an expression.
● A function without a name is called an anonymous function.
● For a lambda expression, we can say that it is an anonymous function.
val lambda_name : Data_type = { argument_List -> code_body }
Example
// with type annotation in lambda expression
val sum1 = { a: Int, b: Int -> a + b }
// without type annotation in lambda expression
val sum2:(Int,Int)-> Int = { a , b -> a + b}
Lambda expressions
● We must explicitly declare the type of our lambda expression.
● Pattern: (Input) -> Output
● Lambda examples with return type:
val lambda1: (Int) -> Int = {a -> a * a}
val lambda2: (String,String) -> String = { a , b -> a + b }
val lambda3: (Int)-> Unit = {print(Int)}
Null safety
● What is null?
● When you declare a variable, you need to assign it a value immediately.
● For example, when you declare a favoriteActor variable, you may assign it a
"Johnson Chan" string value immediately.
val favoriteActor = "Johnson Chan"
Null safety
● What if you don't have a favorite actor?
● You might want to assign the variable a "Nobody" or "None" value.
● You can use null to indicate that there's no value associated with the variable.
fun main() {
val favoriteActor = null
}
Null safety
● Print the value of the favoriteActor variable with the println() function and then
run this program:
fun main() {
val favoriteActor = null
println(favoriteActor)
}
What is the running result?
Non-nullable type
● You can reassign variables defined with the var keyword to different values of
the same type.
● You can reassign a name variable that's declared with one name to another
name as long as the new name is of String type.
fun main() {
var favoriteActor: String = "Johnson Chan"
favoriteActor = "Peter Lee"
println(favoriteActor)
}
Non-nullable type
● There are occasions after you declare a variable when you may want to assign
the variable to null.
fun main() {
var favoriteActor: String = "Johnson Chan"
favoriteActor = null
println(favoriteActor)
}
What is the running result? You will see an error.
Null cannot be a value of a non-null type 'String'.
Nullable and Non-nullable type
● In Kotlin, there's a distinction between nullable and non-nullable types.
● Nullable types are variables that can hold null.
● Non-nullable types are variables that can't hold null.
● A type is only nullable if you explicitly let it hold null.
Nullable type
● To declare nullable variables in Kotlin, you need to add a ? operator to the
end of the type.
● For example, a String? type can hold either a string or null, whereas a String
type can only hold a string.
fun main() {
var favoriteActor: String? = "Johnson Chan"
favoriteActor = null
}
Nullable type
● What is the result of the below function?
fun main() {
var favoriteActor: String? = "Johnson Chan"
println(favoriteActor)
favoriteActor = null
println(favoriteActor)
}
Do you think it can run? Try in Kotlin Playground
Access a property of a non-nullable variable
● You learned to use the . operator to access methods and properties of
non-nullable variables.
● Declare a favoriteActor variable of String type and assign it to the name of your
favorite actor:
fun main() {
var favoriteActor: String = "Johnson Chan"
println([Link])
}
Access a property of a nullable variable
● Change the favoriteActor variable type to a nullable type and then run this
program:
● This error is a compile error.
fun main() {
var favoriteActor: String? = "Johnson Chan"
println([Link])
}
What is the running result? Do you know why?
Access a property of a nullable variable
● A compile error happens when Kotlin isn't able to compile the code due to a
syntax error in your code.
● Kotlin intentionally applies syntactic rules so that it can achieve null safety,
which refers to a guarantee that no accidental calls are made on potentially
null variables.
fun main() {
var favoriteActor: String? = "Johnson Chan"
println([Link])
}
What is the running result? Do you know why?
Access a property of a nullable variable
● Use the ?. safe call operator
● You can use the ?. safe call operator to access methods or properties of
nullable variables.
fun main() {
var favoriteActor: String? = "Johnson Chan"
println(favoriteActor?.length)
}
What is the running result?
Access a property of a nullable variable
● How about this?
fun main() {
var favoriteActor: String? = null
println(favoriteActor?.length)
}
What is the running result?
Access a property of a nullable variable with assertion
● You can also use the !! not-null assertion operator to access methods or
properties of nullable variables.
Access a property of a nullable variable with assertion
● As the name suggests, if you use the !! not-null assertion, it means that you
assert that the value of the variable isn't null, regardless of whether it is or isn't.
● The use of a !! not-null assertion operator may result in a NullPointerException
error being thrown if the nullable variable is indeed null.
fun main() {
var favoriteActor: String? = null
println(favoriteActor!!.length)
}
What is the running result?
Access a property of a nullable variable with null checks
● You can use the if branch in the if/else conditionals to perform null checks.
fun main() {
var favoriteActor: String? = null
if(favoriteActor != null) {
println("The number of characters in your favorite actor's name is ${[Link]}.")
} else {
println("You didn't input a name.")
}
}
Access a property of a nullable variable with null checks
● The ?: Elvis operator is an operator that you can use together with the ?.
safe-call operator.
● With the ?: Elvis operator, you can add a default value when the ?. safe-call
operator returns null.
● It's similar to an if/else expression, but in a more idiomatic way.
fun main() {
var favoriteActor: String? = "Johnson Chan"
val lengthOfName = favoriteActor?.length ?: 0
println("The number of characters in your favorite actor's name is $lengthOfName.")
}
We will explain with more details on the next page
Access a property of a nullable variable with null checks
fun main() {
var favoriteActor: String? = "Johnson Chan"
val lengthOfName = favoriteActor?.length ?: 0
println("The number of characters in your favorite actor's name is $lengthOfName.")
}
If favoriteActor has a value (e.g., "Leonardo DiCaprio"):
● favoriteActor?.length evaluates to the length of "Leonardo DiCaprio" (17).
● The result (17) is assigned to lengthOfName.
If favoriteActor is null:
● The safe call (?.) returns null.
● The Elvis operator (?:) assigns the default value 0 to lengthOfName.
Activity
Activity
● The Activity class is essential to Android apps and their application model.
● Activities are launched and managed differently than traditional main()
methods.
● The Android system uses lifecycle callback methods to manage activity
stages.
Activity
● Mobile app experiences differ
from desktop apps—user
interactions don’t always start in
the same place.
● User journeys are often
non-deterministic.
● Example:
○ Opening an email app from the
home screen shows an email list.
○ Launching the email app via a social
media app may open the compose
screen directly.
Activity
● The Activity class is designed to facilitate this paradigm.
● When one app invokes another, the calling app invokes an activity in the
other app, rather than the app as an atomic whole.
● The activity serves as the entry point for an app's interaction with the user.
Activity
● An activity provides the window in which the app draws its UI.
● This window typically fills the screen, but may be smaller than the screen and
float on top of other windows.
● Most apps contain multiple screens, which means they comprise multiple
activities.
● Typically, one activity in an app is specified as the main activity, which is the
first screen to appear when the user launches the app.
● Each activity can then start another activity in order to perform different
actions.
Activity
● To use activities in your app, you must register information about them in the
app’s manifest, and you must manage activity lifecycles appropriately.
● To declare your activity, open your manifest file and add an <activity> element
as a child of the <application> element.
<manifest ... >
<application ... >
<activity android:name=".ExampleActivity" />
...
</application ... >
...
</manifest >
Intents and
Intent Filters
Intents
● An intent in Android is a messaging
object used to request an action from
another app component (e.g., starting
an activity, service, or broadcasting a
message).
● They provide the ability to launch an
activity based not only on an explicit
intent, but also an implicit intent.
Intents
● An explicit intent specifies the exact ● An implicit intent does not specify a
component (e.g., activity, service) to specific component but rather
start by naming it explicitly. describes an action to be performed.
● Example: Start the Send Email activity ● The Android system determines which
in the Gmail app app or component can handle the
intent.
Intents
How an implicit intent is delivered
through the system to start another
activity:
[1] Activity A creates an Intent with an
action description and passes it to
startActivity().
Activity A (the current activity) creates an instance of
Intent and specifies the action it wants performed (e.g.,
opening a webpage).
val intent = Intent(Intent.ACTION_VIEW)
[Link] = [Link]("[Link]
startActivity(intent)
Intents
How an implicit intent is delivered
through the system to start another
activity:
[2] The Android System searches all apps
for an intent filter that matches the intent.
The Android system checks all the apps installed on the device for components (like
activities) that have declared they can handle this type of intent using an intent filter in
their [Link].
<activity android:name=".WebViewActivity">
<intent-filter>
<action android:name="[Link]" />
<category android:name="[Link]" />
<data android:scheme="http" />
</intent-filter>
</activity>
Intents
How an implicit intent is delivered
through the system to start another
activity:
[3] When a match is found, the system
starts the matching activity (Activity B) by
invoking its onCreate() method and
passing it the Intent.
Intent Filters
● An intent filter in Android is a declaration in the app's [Link] file
that specifies the types of intents a component (such as an activity, service, or
broadcast receiver) can handle.
● It tells the Android system what actions, data types, or categories the
component is capable of responding to.
Intent Filters
● Define in the manifest file ([Link])
● For example, the following code snippet shows how to configure an activity
that sends text data, and receives requests from other activities to do so:
● In this example, the <action> element specifies that this activity sends data.
<activity android:name=".ExampleActivity" android:icon="@drawable/app_icon">
<intent-filter>
<action android:name="[Link]" />
<category android:name="[Link]" />
<data android:mimeType="text/plain" />
</intent-filter>
</activity>
Intent Filters
● If you intend for your app to be self-contained and not allow other apps to
activate its activities, you don't need any other intent filters.
● Activities that you don't want to make available to other applications should
have no intent filters.
Declare Permissions
Declare Permissions
● To control who can access your app's activity in Android, you can declare
permissions in your [Link] file.
● By requiring a specific permission, you restrict access to only those apps that
have been granted that permission.
Declare a Custom Permission
● You can define a custom permission in your app's [Link] if your
activity needs to be protected by a permission that is not already provided by
Android.
<manifest xmlns:android="[Link]
package="[Link]">
<!-- Declare a custom permission -->
<permission
android:name="[Link].MY_CUSTOM_PERMISSION"
android:protectionLevel="normal" />
...
</manifest>
Require the Permission for Your Activity
● Use the android:permission attribute in the <activity> tag to specify the
permission required to start your activity.
<manifest xmlns:android="[Link]
package="[Link]">
<application>
<!-- Declare an activity that requires a specific permission -->
<activity android:name=".MyProtectedActivity"
android:permission="[Link].MY_CUSTOM_PERMISSION">
<intent-filter>
<action android:name="[Link]" />
<category android:name="[Link]" />
</intent-filter>
</activity>
</application>
</manifest>
Grant the Permission (If Using a Custom Permission)
● If another app needs to call your activity, the app must declare the permission
in its [Link] like this:
<manifest xmlns:android="[Link]
package="[Link]">
<uses-permission android:name="[Link].MY_CUSTOM_PERMISSION" />
...
</manifest>
Managing the
Activity Lifecycle
Activity Lifecycle
● The activity lifecycle in Android refers to
the sequence of states an activity goes
through from when it is created to when
it is destroyed.
● Each state represents what the activity is
currently doing and how it interacts with
the user.
● Android invokes callbacks when the
activity moves from one state to another,
and you can override those methods in
your own activities to perform tasks in
response to those lifecycle state
changes.
[Link]
Activity Lifecycle
onCreate()
● You must implement this
callback, which fires when the
system creates your activity.
● Your implementation should
initialize the essential
components of your activity.
● Most importantly, this is where
you must call setContentView()
to define the layout for the
activity's user interface.
Activity Lifecycle
onStart()
● As onCreate() exits, the
activity enters the Started
state, and the activity
becomes visible to the user.
Activity Lifecycle
onResume()
● The system invokes this
callback just before the activity
starts interacting with the user.
● At this point, the activity is at the
top of the activity stack, and
captures all user input.
● Most of an app’s core
functionality is implemented in
the onResume() method.
Activity Lifecycle
onPause()
● The system calls onPause() when the
activity loses focus and enters a Paused
state.
● This state occurs when, for example, the
user taps the Back or Recents button.
● It technically means your activity is still
partially visible, but most often is an
indication that the user is leaving the
activity, and the activity will soon enter the
Stopped or Resumed state.
● Once onPause() finishes executing, the next
callback is either onStop() or onResume()
Activity Lifecycle
onStop()
● The system calls onStop() when
the activity is no longer visible
to the user.
● The next callback that the
system calls is either onRestart(),
if the activity is coming back to
interact with the user, or by
onDestroy() if this activity is
completely terminating.
Activity Lifecycle
onRestart()
● The system invokes this
callback when an activity in
the Stopped state is about to
restart.
Activity Lifecycle
onDestroy()
● The system invokes this
callback before an activity is
destroyed.
● This callback is the final one that
the activity receives.
● onDestroy() is usually
implemented to ensure that all
of an activity’s resources are
released when the activity, or
the process containing it, is
destroyed.
Activity Lifecycle
and Back Stack
Lifecycle of a task and its back stack
● The device Home screen is the starting place for most tasks.
● When a user touches the icon for an app or shortcut in the app launcher or on
the Home screen, that app's task comes to the foreground.
● If no task exists for the app, then a new task is created and the main activity
for that app opens as the root activity in the stack.
Lifecycle of a task and its back stack
● When the current activity starts another,
the new activity is pushed on the top of
the stack and takes focus.
● The previous activity remains in the
stack, but is stopped.
● When an activity is stopped, the system
retains the current state of its user
interface.
● When the user performs the back action,
the current activity is popped from the
top of the stack and destroyed.
● The previous activity resumes, and the
previous state of its UI is restored.
Lifecycle of a task and its back stack
● Activities in the stack are never rearranged, only pushed onto and popped
from the stack as they are started by the current activity and dismissed by the
user through the Back button or gesture.
● Therefore, the back stack operates as a last in, first out object structure.
Background and Foreground Tasks
● A task is a cohesive unit that can
move to the background when a user
begins a new task or goes to the
Home screen.
● While in the background, all the
activities in the task are stopped, but
the back stack for the task remains
intact—the task loses focus while
another task takes place
● A task can then return to the
foreground so users can pick up
where they left off.
Activity Lifecycle
The Back button indicates that the user
wants to exit the current activity or remove
● Back and Recent Button it from the navigation stack. When the
Back button is pressed:
What Happens:
● The current activity is removed from
the activity stack.
● The activity transitions through the
following lifecycle methods:
● onPause(): Called as the activity is
no longer in the foreground.
● onStop(): Called as the activity is no
longer visible.
● onDestroy(): Called as the activity is
destroyed and removed from
memory.
Activity Lifecycle
The Recent Apps button (also called the
Overview button) does not destroy the
● Back and Recent Apps Button current activity. Instead, it moves the
current app to the background and
displays a list of recently used apps for
multitasking.
What Happens:
● The activity is paused and then
stopped, but it is not destroyed. It
remains in memory, and the system
can bring it back to the foreground if
the user selects it again.
● The lifecycle methods triggered:
● onPause(): Called as the activity is
no longer in the foreground.
● onStop(): Called as the activity is no
longer visible.
Activity Lifecycle
Broadcast Receiver
(Optional)
Broadcast overview
● In Android, a broadcast is a system-wide message used to notify apps about
events (either system-generated or app-specific).
Connectivity Changes Broadcast: [Link].CONNECTIVITY_CHANGE
Use Case: Detect when the device connects/disconnects from a network (Wi-Fi, mobile
data, etc.).
Example:
- Apps like YouTube pause video playback when the internet disconnects.
- Apps like Gmail sync emails when connectivity is restored.
Device Boot Completed Broadcast: [Link].BOOT_COMPLETED
Use Case: Perform actions after the device reboots.
Example:
- Alarm or reminder apps reschedule alarms after a device reboot.
- Auto-start apps (e.g., email sync, fitness tracking) initialize themselves.
Broadcast overview
● In Android, a broadcast is a system-wide message used to notify apps about
events (either system-generated or app-specific).
Battery Status Broadcast: [Link].BATTERY_CHANGED
Use Case: Monitor battery status, level, or charging state.
Example:
- Battery saver apps notify users when the battery is low.
- Apps display battery percentage or charging status.
Airplane Mode Changes Broadcast: [Link].AIRPLANE_MODE
Use Case: Detect when the airplane mode is turned on or off.
Example:
- Messaging apps disable message sending when airplane mode is enabled.
Broadcast overview
● Android apps send and receive broadcast messages from the Android system
and other Android apps, similar to the publish-subscribe design pattern.
● The system and apps typically send broadcasts when certain events occur.
● Android system sends broadcasts when various system events occur, such as
system boot or device charging.
● Apps also send custom broadcasts, for example, to notify other apps of
something that might interest them (for example, new data download).
Broadcast overview
Get notifications when intents occur
Broadcast Receiver
Android System (within an app)
Register for intents to observer
Broadcast overview
● Broadcasts can be used as a messaging system across apps and outside of
the normal user flow.
● However, you must be careful not to abuse the opportunity to respond to
broadcasts and run jobs in the background that can contribute to a slow
system performance.
Receive broadcasts
● Apps can receive broadcasts in two ways: through context-registered
receivers and manifest-declared receivers.
● We introduce manifest-declared receivers here.
Receive broadcasts
● If you declare a broadcast receiver in your manifest, the system launches your
app when the broadcast is sent.
● If the app is not already running, the system launches the app.
● Specify the <receiver> element in your app's manifest.
<receiver android:name=".MyBroadcastReceiver" android:exported="false">
<intent-filter>
<action android:name="[Link].ACTION_UPDATE_DATA" />
</intent-filter>
</receiver>
Send broadcasts (Optional)
● Android provides two ways for apps to send broadcasts:
● The sendOrderedBroadcast(Intent, String) method sends broadcasts to one
receiver at a time.
● The sendBroadcast(Intent) method sends broadcasts to all receivers in an
undefined order. This is called a Normal Broadcast.
● The sendBroadcast(Intent) method is introduced here.
Send broadcasts (Optional)
● The following code snippet demonstrates how to send a broadcast by creating
an Intent and calling sendBroadcast(Intent).
val intent = Intent("[Link].ACTION_UPDATE_DATA").apply {
putExtra("[Link]", newData)
setPackage("[Link]")
}
[Link](intent)
Resources
Resources
● Getting Started with Kotlin
[Link]
● Introduction to activities
[Link]
Resources
● Android Lifecycle
[Link]
lifecycle
End of Lecture