Android - The 4 Basic Android Components

4 Basic Android Components

  • Activity

    • Dictate the UI and handle the user interaction to the device’s screen
  • Service

    • Handle background processing associated with an app
  • Content Provider

    • Handle data and database management issues
  • Broadcast Receiver

    • Handle communication between Android OS and apps

All components must be fully controlled, understood by the Kernel

Activity Overview

An Activity is a functional unit of the application, which may be invoked by another activity or application. It has a user interface of some form. An application may incorporate a number of activities. One activity may be nominated as the default which means it may be directly executed by the user.

The main purpose of an activity is to handle visual user interface and to interact with user

Activity is like Controller in MVC model, while View is like View in MVC model

  • Typically an app has one or more activities
  • Each activity has its UI (XML) and program code (Java)
  • An existing activity can be replaced by new one with the same contract (intent)
  • All activities can be invoked by other applications (intent filter)

The Life Cycle of an Activity

The Activity base class defines a series of events that govern the life cycle of an activity.

From the moment an activity appears on the screen to the moment it is hidden, it goes through a number of stages

  • onCreate()

  • Called when the activity is first created

    • By default, the activity created for you contains the onCreate() event.
  • Use to create and instantiate the objects

  • onStart()

    • Called when the activity becomes visible to the user
  • onResume()

  • Called when the activity starts interacting with the user

    • Use to start any services or code that needs to run while your activity is in the foreground
  • onPause()

  • Called when the current activity is being paused and the previous activity is being resumed

    • Use to stop any services or code that does not need to run when your activity is not in the foreground
  • onStop()

    • Called when the activity is no longer visible to the user
  • onDestroy()

    • Called before the activity is destroyed by the system
    • Used to free up resources before your activity is destroyed
  • onRestart()

    • Called when the activity has been stopped and is restarting again

  • Running
    • the Activity is running, and it is being processed at the Foreground, the user can interact with the Activity
  • Pause
    • the Activity is no longer at the Foreground (but part of the UI could still be visible to the user)
    • cannot interact with the user. It can be re-activated by onResume() function
  • Stop
    • the Activity stands by in the background and cannot be seen any more on the screen.
    • Can be re-activated by the onRestart function

Create an Activity

To create an activity, you create a Java class that extends the Activity base class

1
2
public class MainActivity extends AppCompatActivity{ //In some API it is called Activity
}
  • Your activity class loads its user interface (UI) component using the XML file defined in your res/layout folder

  • Every activity you have in your application must be declared in your AndroidManifest.xml file

Understanding the Life Cycle of an Activity

When Activity is shown (The activity is first loaded to the screen and can interact with users):

  • onCreate(), onStart(), onResume() are called

When Activity is exited (The Back button of mobile is clicked):

  • onPause(), onStop(), onDestroy() are called

When the Activity is reopened back by selecting from the Overview:

  • onCreate(), onStart(), onResume() are called again

When the Home button is clicked and then another app is loaded (e.g. Phone app):

  • onPause(), onStop() are called

When the Phone app exits (Back) and then select the activity from Overview

  • onRestart(), onStart(), onResume() are called

The 3 Nested Lifetimes

Foreground (Active) Lifetime

  • During this time, the activity is in front of all other activities on screen and is interacting with the user.

Visible Lifetime

  • During this time, the user can see the activity on-screen, though it may not be in the foreground and interacting with the user.
  • onStart() and onStop() can be called multiple times, as the activity alternates between being visible and hidden to the user.

Activity - The Concepts of Intents

An intent is basically the “glue” that enables activities from different applications to work together seamlessly, ensuring that tasks can be performed as though they all belong to one single application

  • Navigation between activities is through an intent.
    • That is, When your app has more than one activity, you often need to navigate from one to another.
  • Intent is a facility for run-time binding between components (Activity, Service, Broadcast Receiver)
  • A message delivered between components (Inter- Process Communication)
  • No matter they are in the same or different applications
    • Do not need to know the location
  • Implemented by android.content.Intent

To Activate an Activity component:

  • startActivity

To activate a Service component:

  • startService or bindService

To activate a Broadcast Receiver component or register broadcast receiver in AndroidManifest.xml:

  • broadcastIntent

The 2 Types of Intent

  • Explicit Intent: Call by Name
    • Specify the name of the intent, the result is unique and predictable
    • Early Binding
    • High performance
  • Implicit Intent: Call by Behavior
    • Tell Intent what to do
    • Determined by the system that matches your requirement
    • Late Binding

Example Code of Intent MainActivity to MainActivity2 after clicking Button btnMove:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
@Override
protected void onCreate(Bundle savedInstanceState){
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);

//Locate the button
Button btnMove = findViewByID(R.id.btnMove);
btnMove.setOnClickListener(new View.OnClickListener(){
@Override
public void onClick(View v){
Intent intent = new Intent(MainActivity.this, MainActivity2.class);
startActivity(intent);
MainActivity.this.finish(); // or simply finish();
}
}
}

Note If you check AndroidManifest.xml , it will contain all the activities automatically once you create the activity.

Activity Communication

Get the returning data from another activity:

  • Need to declare the request code for the SubActivities
  • Instead of using startActivity, use startActivityForResult
    • startActivityForResult(Intent intent, int requestCode)

Example:

MainActivity.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
public class MainActivity extends AppCompatActivity {

//Declare the request code for the two SubActivities
private static final int SUBACTIVITY1 = 1;
private static final int SUBACTIVITY2 = 2;

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);

Button btnA1 = findViewById(R.id.btnA1);
Button btnA2 = findViewById(R.id.btnA2);

btnA1.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Intent intent = new Intent(MainActivity.this, SubActivity1.class);
startActivityForResult(intent, SUBACTIVITY1);
}
});

btnA2.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Intent intent = new Intent(MainActivity.this, SubActivity2.class);
startActivityForResult(intent, SUBACTIVITY2);
}
});

}

//Add the override method to get the result
//Click `control + o` to generate this
@Override
protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {
super.onActivityResult(requestCode, resultCode, data);

TextView textMsg = findViewById(R.id.textMsg);

switch (requestCode){
case SUBACTIVITY1:
if (resultCode == RESULT_OK){
//URI (Uniform Resource Identifier)
//This class provides constructors for creating URI instances from their components or by parsing their string forms, methods for accessing the various components of an instance, and methods for normalizing, resolving, and relativizing URI instances.
//Instances of this URI class are immutable.
Uri uriData = data.getData();
textMsg.setText(uriData.toString());
}
break;
case SUBACTIVITY2:
//Do something
break;

}
}
}

SubActivity1.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
public class SubActivity1 extends AppCompatActivity {

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_sub1);

EditText editInput = findViewById(R.id.editInput);
Button btnOK = findViewById(R.id.btnOK);
Button btnCancel = findViewById(R.id.btnCancel);

btnOK.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
String uriString = editInput.getText().toString();
Uri data = Uri.parse(uriString);
Intent intent = new Intent(null, data);
setResult(RESULT_OK, intent);
finish();
}
});

btnCancel.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
setResult(RESULT_CANCELED, null);
finish();
}
});
}
}

SubActivity2 is not implemented. Only the class and onCreate method.

Activity Communication - Easier Way

Besides returning data from an activity, it is also common to pass data to an activity.

To exchange data with another activity:

Method 1:

Use the putExtra() method of an Intent object to add a name/value pair

  • Value can be String, char, Int, Float,…

Sender side

1
2
3
4
5
6
Intent intent = new Intent(MainActivity.this, MainActivity2.class);
intent.putExtra("msg","I love programming!");
intent.putExtra("num", 2);
startActivity(intent);
//Or
//startActivityForResult(intent, SUBACTIVITY2);

Reciever side

1
2
String msg = getIntent().getStringExtra("msg");
int num = getIntent().getIntExtra("num");

Method 2:

Use the Bundle Object to add new name/values pairs

  • Value can be String, char, Int, Float,…

Sender Side

1
2
3
4
5
6
7
8
Intent intent = new Intent(MainActivity.this, MainActivity2.class);

Bundle extras = new Bundle();
extras.putString("msg", "You will love programming!");
extras.putInt("age", 18);

//Attach the Bundle object to the Intent Object by putExtras
intent.putExtras(extras);

Reciever side

1
2
3
Bundle bundle = getIntent().getExtras();
String msg = bundle.getString("msg");
int age = bundle.getInt("age");

Intent Filter

  • Structured description of Intent values to be matched in AndroidManifest.xml
  • An Intent Filter can be matched against action, category and data (both URI and data type)
  • Include a “priority” value to order multiple matching filters

Example (Default Activity in AndroidManifest.xml)

1
2
3
4
5
6
7
<activity android:name=".MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />

<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>

Intent Attributes

Intent Structure: Action + Data + Other Attributes (e.g., Category, Type, Component, Extras, etc.)

  • Action
    • Verb, describe what you want to do
    • ACTION_VIEW, ACTION_EDIT, ACTION_DIAL, etc.
    • Set by using setAction
  • Data
    • String type, in URI format
    • content://contacts/people/1 (the person whose identifier is 1); tel:10086 (telephone number) etc
    • Set by using Intent.setData
  • Component
    • Explicit name of the intent. By specifying this attribute, all of the other Intent attributes become optional.
    • Used in explicit intent resolution
    • Set by using setClass / setClassName / setComponent
  • Type
    • Specifies an explicit type of the intent data.
    • Usually inferred by data, like: text/plain, image/gif
    • Set by using Intent.setType
  • Category
    • Gives additional information about the action to execute
    • CATEGORY_LAUNCHER, CATEGORY_DEFAULT etc
    • addCategory / hasCategory / removeCategory
  • Extra
    • Bundle of any additional data to provide extended information to the component
    • E.g. If we have an action to send an e-mail message, we could also include extra pieces of data here to supply a subject, body, etc.
    • putExtra / removeExtra

Some Intent Examples:

1
2
3
//Search Google
Intent intent = new Intent();
intent.setAction(Intent.ACTION_WEB_SEARCH); intent.putExtra(SearchManager.QUERY,"searchString")
1
2
3
//Browse web page
Uri uri = Uri.parse("http://www.google.com");
Intent it = new Intent(Intent.ACTION_VIEW, uri);
1
2
3
//Display map
Uri uri = Uri.parse("geo:38.899533,-77.036476");
Intent it = new Intent(Intent.Action_VIEW, uri);
1
2
3
4
//Send SMS
Uri uri = Uri.parse("smsto:0800000123");
Intent it = new Intent(Intent.ACTION_SENDTO, uri);
it.putExtra("sms_body", "The SMS text");

Intent Resolution

  • The system collects all intent filters in all apps to form an intent filter list

  • The matching of an intent with an intent filter in the list is called Intent Resolution

  • Only three aspects of an Intent object are consulted when the object is tested against an intent filter:action, data (both URI and data type), category

  • For an intent filter to match an Intent:

    • The action and category must match
    • The data (both the data type and data scheme + authority + path if specified) must match.

Example:

img

AndroidManifest.xml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<activity android:name=".MainActivity2">
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<data android:scheme="scheme" android:host="polyu.edu.hk" />
</intent-filter>
</activity>
<activity android:name=".MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />

<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>

MainActivity.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
public class MainActivity extends AppCompatActivity {

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);

Button btnGo = findViewById(R.id.btnGo);
btnGo.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
//Matching with <action android:name="android.intent.action.VIEW" />
// and <data android:scheme="scheme" android:host="polyu.edu.hk" />
Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse("scheme://polyu.edu.hk/path"));
startActivity(intent);
}
});
}
}

MainActivity2.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public class MainActivity2 extends AppCompatActivity {

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main2);

Button btnBack = findViewById(R.id.btnBack);
btnBack.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
finish();
}
});
}
}

Showcase:

  • Clicking the Button in MainActivity would go to Main2Activity.

Service

A Service is similar to an activity, except it runs in the background without a UI. An example of a service might be a media player that plays music while the user performs other tasks.

Service is a component that can be running in the background. It does not need a UI.

  • Basically Activitiy without UI
  • Used For long-running background tasks
    • e.g. large data download services, media players

Without using Service, the Download will be terminated once user switch to another activity.

System Services include:

  • Core Services
    • Activity Manager
    • Package Manager
    • Window Manager
    • Resource Manager
    • Content Providers
    • View System
  • Hardware Services
    • Telephony Service
    • Location Service
    • Bluetooth Service
    • WiFi Service
    • USB Service
    • Sensor Service

Implementing Service

To implement a Service:

  • Inherit own class from android.app.Service
    • e.g. public class myService extends Service{}
  • Override methods in the super class (only common methods are mentioned)
    • onCreate()
    • onDestroy()
    • onStartCommand(Intent intent, int flags, int startId)
    • onBind(Intent intent)
    • onUnbind(Intent intent)
  • Add Service declaration under <application> in AndroidManifest.xml
    • e.g. <service android:name=".MyService" android:enabled="true" android:exported="true" />
    • android:enabled="true" - Whether or not the service can be instantiated by the system
    • android:exported="true" - Whether or not components of other applications can invoke the service or interact with it

There are 2 methods to start a Service:

  • startService
    • startService(new Intent(getBaseContext(), MyService.class));
    • Service won’t stop without calling Context.stopService
      • stopService(new Intent(getBaseContext(), MyService.class));
  • bindService
    • Bind service to an activity
    • The Lifecycle is same as activity
    • User can call Unbind to stop it manually

Service Example using startService/stopService

First Declare a Service class:

We can use File -> New -> Service -> Service to declare/create a service.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
public class myService extends Service{
@Override
public int onStartCommand(Intent intent, int flags, int startId){
//do something...
return super.onStartCommand(intent, flags, startId)
}

@Override
public void onDestroy(){
//do something...
super.onDestroy();
}

@Nullable
@Override
public IBinder onBind(Intent intent){
return null;
}
}

Then Declare the service in AndroidManifest.xml within <application>

1
2
3
<service android:name=".MyService" 
android:enabled="true"
android:exported="true" />

To start a Service:

1
startService(new Intent(getBaseContext(), MyService.class));

To stop a Service

1
stopService(new Intent(getBaseContext(), MyService.class));

Service Example using bindService/ServiceConnection

The ServiceConnection will receive the service object when it is created and be told if it dies and restarts

The service will be considered required by the system only for as long as the calling context exists

1
2
3
4
5
6
7
8
9
10
11
12
import android.content.ServiceConnection;

private ServiceConnection SvcConn = new ServiceConnection(){
public void onServiceConnected(ComponentName arg0, IBinder arg1) {
//do something...
}
public void onServiceDisconnected(ComponentName name) {
//do something...
}
};
Intent i = new Intent(MainHello.this, TestService.class);
bindService(i, SvcConn, Context.BIND_AUTO_CREATE);

Broadcast Receiver

Broadcast Receivers simply respond to broadcast messages from other applications or from the system. For example, it may be useful for the application to know when a picture has been taken. This is the kind of event that may result in a broadcast message.

Broadcast Receiver will let you know when system events occur.

E.g.

  • SMS is arriving
  • Battery is low
  • WiFi is connected
  • System is shutting down

Broadcast Receiver has No User Interface.

  • Only Inform the user via Activity or Notification
    • e.g., background flashlight, vibration, sound, notification icon on the status bar, etc.

Kinds of Broadcast

  • Normal Broadcast
    • Context.sendBroadcast
    • All receivers will receive the broadcast at the same time
  • Ordered Broadcast
    • Context.sendOrderedBroadcast
    • Can be aborted by previous receiver using broadcastReceiver.abortBroadcast()
    • Priority can be set in Intent

Broadcast Registration

Two ways to register Broadcast.

  • Static Broadcast receivers, which you register in the Android manifest file.
    • AndroidManifest.xml
    • Application/Receiver node
    • Life cycle ends when OnReceive return.
  • Dynamic Broadcast receivers, which you register using a context.
    • Context.registerReceiver(BroadcastReceiver,IntentFilter)
    • Must be unregistered manually.

Static Broadcast Registration Example

Example: SMSReceived using AndroidManifest.xml

  • Within the <application> tag:
1
2
3
4
5
<receiver android:name="SMSRecv">
<intent-filter>
<action android:name="android.provider.Telephony.SMS_RECEIVED"> </action>
</intent-filter>
</receiver>
  • Outside the <application> tag:
1
<uses-permission android:name="android.permission.RECEIVE_SMS"></uses-permission>

To use the broadcast in the onReceive method

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
private static final String SMS_RECEIVED = "android.provider.Telephony.SMS_RECEIVED";
public void onReceive(Context arg0, Intent intent){

String action = intent.getAction();

if(SMS_RECEIVED.equals(action)){
Bundle bundle = intent.getExtras();
Object[] pdus = (Object[]) bundle.get("pdus");
for(Object pdu: pdus){
SmsMessage message = SmsMessage.createFromPdu((byte[])pdu);
String sender = message.getOriginatingAddress();
String content = message.getMessageBody();
Log.v("HELLO", content);
}
}
}

Another Example:

  • Display a toast message “Airplane Mode Changed” when Airplane_Mode detected
  • and “Do Not Disturb Mode Changed” when RINGER_MODE detected

In AndroidManifest.xml Within the <application> tag:

1
2
3
4
5
6
<receiver android:name=".MyReceiver"> 
<intent-filter>
<action android:name="android.intent.action.AIRPLANE_MODE"/>
<action android:name="android.media.RINGER_MODE_CHANGED"/>
</intent-filter>
</receiver>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
public class MyReceiver extends BroadcastReceiver {
private static String AIRPLANE_MODE = "android.intent.action.AIRPLANE_MODE";
private static String DO_NOT_DISTURB_MODE ="android.media.RINGER_MODE_CHANGED";

@Override
public void onReceive(Context context, Intent intent) {
String msg = "";
if (intent.getAction().equals(AIRPLANE_MODE))
{
msg = "Airplane Mode Changed";
}
else if (intent.getAction().equals(DO_NOT_DISTURB_MODE))
{
msg = "Do Not Disturb mode Changed";
}
Toast.makeText(context, msg, Toast.LENGTH_SHORT).show();
}
}

Dynamic Broadcast Registration Example

Boot Completed

1
2
3
4
5
6
7
8
9
10
11
12
public class myReceiver extends BroadcastReceiver
{
static final String ACTION = "android.intent.action.BOOT_COMPLETED";

public void onReceive(Context context, Intent intent)
{
if(intent.getAction().equals(ACTION))
{
context.startService(new Intent(context, myService.class), null);
}
}
}

Content Provider

A Content Provider supplies data from one application to other applications on request. Such requests are handled by the methods of the ContentResolver class. The data may be stored in the file system, the database, or somewhere else entirely.

Vines: Will be Talked in the next article - Data Persistance.

https://vinesmsuic.github.io/androidlec3/#content-provider