Activity / Services Flashcards
How to manage tasks in Activity ?
Android manages tasks and the back stack by placing all activities started in succession in the same task, in a last in, first out stack. This works great for most apps, and you usually don’t have to worry about how your activities are associated with tasks or how they exist in the back stack.
However, you might decide that you want to interrupt the normal behavior. For example, you might want an activity in your app to begin a new task when it is started, instead of being placed within the current task. Or, when you start an activity, you might want to bring forward an existing instance of it, instead of creating a new instance on top of the back stack. Or you might want your back stack to be cleared of all activities except for the root activity when the user leaves the task.
You can do these things and more using attributes in the <activity> manifest element and flags in the intent that you pass to startActivity().</activity>
What is principle activity attribute for manage the tasks ?
These are the principal <activity> attributes that you can use to manage tasks:</activity>
- taskAffinity
- launchMode
- allowTaskReparenting
- clearTaskOnLaunch
- alwaysRetainTaskState
- finishOnTaskLaunch
How to handle affinities ?
An affinity indicates which task an activity “prefers” to belong to. By default, all the activities from the same app have an affinity for each other: they “prefer” to be in the same task.
However, you can modify the default affinity for an activity. Activities defined in different apps can share an affinity, and activities defined in the same app can be assigned different task affinities.
You can modify an activity’s affinity using the taskAffinity attribute of the <activity> element.</activity>
The taskAffinity attribute takes a string value that must be different than the default package name declared in the <manifest> element, because the system uses that name to identify the default task affinity for the app.</manifest>
The affinity comes into play in two circumstances:
- When the intent that launches an activity contains the FLAG_ACTIVITY_NEW_TASK flag.
- When an activity has its allowTaskReparenting attribute set to “true”.
How to clear the back stack ?
f the user leaves a task for a long time, the system clears the task of all activities except the root activity. When the user returns to the task, only the root activity is restored. The system behaves this way based on the assumption that after an extended amount of time users have abandoned what they were doing before and are returning to the task to begin something new.
There are some activity attributes that you can use to modify this behavior:
- alwaysRetainTaskState
When this attribute is set to “true” in the root activity of a task, the default behavior just described does not happen. The task retains all activities in its stack even after a long period. - clearTaskOnLaunch
When this attribute is set to “true” in the root activity of a task, the task is cleared down to the root activity whenever the user leaves the task and returns to it. In other words, it’s the opposite of alwaysRetainTaskState. The user always returns to the task in its initial state, even after leaving the task for only a moment. - finishOnTaskLaunch
This attribute is like clearTaskOnLaunch, but it operates on a single activity, not an entire task. It can also cause any activity to finish except for the root activity. When it’s set to “true”, the activity remains part of the task only for the current session. If the user leaves and then returns to the task, it is no longer present.
How to define Launch Modes ?
Launch modes let you define how a new instance of an activity is associated with the current task. You can define launch modes in two ways, described in the sections that follow:
- Using the manifest file
When you declare an activity in your manifest file, you can specify how the activity associates with tasks when it starts. - Using intent flags
When you call startActivity(), you can include a flag in the Intent that declares how (or whether) the new activity associates with the current task.
Define launch modes using the manifest file.
When declaring an activity in your manifest file, you can specify how the activity associates with a task using the <activity> element's launchMode attribute.</activity>
There are five launch modes you can assign to the launchMode attribute:
- “standard”
The default mode. The system creates a new instance of the activity in the task it was started from and routes the intent to it. The activity can be instantiated multiple times, each instance can belong to different tasks, and one task can have multiple instances. - “singleTop”
If an instance of the activity already exists at the top of the current task, the system routes the intent to that instance through a call to its onNewIntent() method, rather than creating a new instance of the activity. The activity is instantiated multiple times, each instance can belong to different tasks, and one task can have multiple instances (but only if the activity at the top of the back stack is not an existing instance of the activity). - “singleTask”
The system creates the activity at the root of a new task or locates the activity on an existing task with the same affinity. If an instance of the activity already exists, the system routes the intent to the existing instance through a call to its onNewIntent() method, rather than creating a new instance. Meanwhile all of the other activities on top of it are destroyed. - “singleInstance”.
The behavior is the same as for “singleTask”, except that the system doesn’t launch any other activities into the task holding the instance. The activity is always the single and only member of its task. Any activities started by this one open in a separate task. - “singleInstancePerTask”.
The activity can only run as the root activity of the task, the first activity that created the task, and therefore there can only be one instance of this activity in a task. In contrast to the singleTask launch mode, this activity can be started in multiple instances in different tasks if the FLAG_ACTIVITY_MULTIPLE_TASK or FLAG_ACTIVITY_NEW_DOCUMENT flag is set.
Note: “singleTask” and “singleInstancePerTask” remove all activities that are above the starting activity from the task. For example, suppose a task consists of root activity A with activities B and C. The task is A-B-C, with C on top. An intent arrives for an activity of type A. If A’s launch mode is “singleTask” or “singleInstancePerTask”, the existing instance of A receives the intent through onNewIntent(). B and C are finished, and the task is now A.
Define launch modes using Intent flags.
When starting an activity, you can modify the default association of an activity to its task by including flags in the intent that you deliver to startActivity(). The flags you can use to modify the default behavior are the following:
- FLAG_ACTIVITY_NEW_TASK
The system starts the activity in a new task. If a task is already running for the activity being started, that task is brought to the foreground with its last state restored, and the activity receives the new intent in onNewIntent().
This produces the same behavior as the “singleTask” launchMode value discussed in the preceding section.
- FLAG_ACTIVITY_SINGLE_TOP
If the activity being started is the current activity, at the top of the back stack, then the existing instance receives a call to onNewIntent() instead of creating a new instance of the activity.
This produces the same behavior as the “singleTop” launchMode value discussed in the preceding section.
- FLAG_ACTIVITY_CLEAR_TOP
If the activity being started is already running in the current task, then—instead of launching a new instance of that activity—the system destroys all the other activities on top of it. The intent is delivered to the resumed instance of the activity, now on top, through onNewIntent().
There is no value for the launchMode attribute that produces this behavior.
FLAG_ACTIVITY_CLEAR_TOP is most often used in conjunction with FLAG_ACTIVITY_NEW_TASK. When used together, these flags locate an existing activity in another task and put it in a position where it can respond to the intent.
What is Android Services?
A Service is an application component that can perform long-running operations in the background. It does not provide a user interface. Once started, a service might continue running for some time, even after the user switches to another application. Additionally, a component can bind to a service to interact with it and even perform interprocess communication (IPC). For example, a service can handle network transactions, play music, perform file I/O, or interact with a content provider, all from the background.
Caution: A service runs in the main thread of its hosting process; the service does not create its own thread and does not run in a separate process unless you specify otherwise. You should run any blocking operations on a separate thread within the service to avoid Application Not Responding (ANR) errors.
Explain the type of services.
Types of Services:
These are the three different types of services:
- Foreground
A foreground service performs some operation that is noticeable to the user. For example, an audio app would use a foreground service to play an audio track. Foreground services must display a Notification. Foreground services continue running even when the user isn’t interacting with the app.
When you use a foreground service, you must display a notification so that users are actively aware that the service is running. This notification cannot be dismissed unless the service is either stopped or removed from the foreground.
Note: The WorkManager API offers a flexible way of scheduling tasks, and is able to run these jobs as foreground services if needed. In many cases, using WorkManager is preferable to using foreground services directly.
- Background
A background service performs an operation that isn’t directly noticed by the user. For example, if an app used a service to compact its storage, that would usually be a background service.
Note: If your app targets API level 26 or higher, the system imposes restrictions on running background services when the app itself isn’t in the foreground. In most situations, for example, you shouldn’t access location information from the background. Instead, schedule tasks using WorkManager.
- Bound
A service is bound when an application component binds to it by calling bindService(). A bound service offers a client-server interface that allows components to interact with the service, send requests, receive results, and even do so across processes with interprocess communication (IPC). A bound service runs only as long as another application component is bound to it. Multiple components can bind to the service at once, but when all of them unbind, the service is destroyed.
How to Choose between a service and a thread.
A service is simply a component that can run in the background, even when the user is not interacting with your application, so you should create a service only if that is what you need.
If you must perform work outside of your main thread, but only while the user is interacting with your application, you should instead create a new thread in the context of another application component. For example, if you want to play some music, but only while your activity is running, you might create a thread in onCreate(), start running it in onStart(), and stop it in onStop(). Also consider using thread pools and executors from the java.util.concurrent package or Kotlin coroutines instead of the traditional Thread class.
Remember that if you do use a service, it still runs in your application’s main thread by default, so you should still create a new thread within the service if it performs intensive or blocking operations.
Declare a service in the manifest.
You must declare all services in your application’s manifest file, just as you do for activities and other components.
To declare your service, add a <service> element as a child of the <application> element. Here is an example:</application></service>
<manifest … >
…
<application … >
<service></service>
…
</application>
</manifest>
What is the callback methods of services?
To create a service, you must create a subclass of Service or use one of its existing subclasses. In your implementation, you must override some callback methods that handle key aspects of the service lifecycle and provide a mechanism that allows the components to bind to the service, if appropriate. These are the most important callback methods that you should override:
- onStartCommand()
The system invokes this method by calling startService() when another component (such as an activity) requests that the service be started. When this method executes, the service is started and can run in the background indefinitely. If you implement this, it is your responsibility to stop the service when its work is complete by calling stopSelf() or stopService(). If you only want to provide binding, you don’t need to implement this method. - onBind()
The system invokes this method by calling bindService() when another component wants to bind with the service (such as to perform RPC). In your implementation of this method, you must provide an interface that clients use to communicate with the service by returning an IBinder. You must always implement this method; however, if you don’t want to allow binding, you should return null. - onCreate()
The system invokes this method to perform one-time setup procedures when the service is initially created (before it calls either onStartCommand() or onBind()). If the service is already running, this method is not called. - onDestroy()
The system invokes this method when the service is no longer used and is being destroyed. Your service should implement this to clean up any resources such as threads, registered listeners, or receivers. This is the last call that the service receives.
If a component starts the service by calling startService() (which results in a call to onStartCommand()), the service continues to run until it stops itself with stopSelf() or another component stops it by calling stopService().
If a component calls bindService() to create the service and onStartCommand() is not called, the service runs only as long as the component is bound to it. After the service is unbound from all of its clients, the system destroys it.
What is return value constraints for onStartCommand() method?
The onStartCommand() method must return an integer. The integer is a value that describes how the system should continue the service in the event that the system kills it.
The return value from onStartCommand() must be one of the following constants:
- START_NOT_STICKY
If the system kills the service after onStartCommand() returns, do not recreate the service unless there are pending intents to deliver. This is the safest option to avoid running your service when not necessary and when your application can simply restart any unfinished jobs. - START_STICKY
If the system kills the service after onStartCommand() returns, recreate the service and call onStartCommand(), but do not redeliver the last intent. Instead, the system calls onStartCommand() with a null intent unless there are pending intents to start the service. In that case, those intents are delivered. This is suitable for media players (or similar services) that are not executing commands but are running indefinitely and waiting for a job. - START_REDELIVER_INTENT
If the system kills the service after onStartCommand() returns, recreate the service and call onStartCommand() with the last intent that was delivered to the service. Any pending intents are delivered in turn. This is suitable for services that are actively performing a job that should be immediately resumed, such as downloading a file.
How to manage the lifecycle of a service ?
The lifecycle of a service is much simpler than that of an activity. However, it’s even more important that you pay close attention to how your service is created and destroyed because a service can run in the background without the user being aware.
The service lifecycle—from when it’s created to when it’s destroyed—can follow either of these two paths:
- A started service
The service is created when another component calls startService(). The service then runs indefinitely and must stop itself by calling stopSelf(). Another component can also stop the service by calling stopService(). When the service is stopped, the system destroys it. - A bound service
The service is created when another component (a client) calls bindService(). The client then communicates with the service through an IBinder interface. The client can close the connection by calling unbindService(). Multiple clients can bind to the same service and when all of them unbind, the system destroys the service. The service does not need to stop itself.
These two paths aren’t entirely separate. You can bind to a service that is already started with startService(). For example, you can start a background music service by calling startService() with an Intent that identifies the music to play. Later, possibly when the user wants to exercise some control over the player or get information about the current song, an activity can bind to the service by calling bindService(). In cases such as this, stopService() or stopSelf() doesn’t actually stop the service until all of the clients unbind.
How to create bound services?
When creating a service that provides binding, you must provide an IBinder that provides the programming interface that clients can use to interact with the service. There are three ways you can define the interface:
- Extend the Binder class
If your service is private to your own application and runs in the same process as the client, which is common, create your interface by extending the Binder class and returning an instance of it from onBind(). The client receives the Binder and can use it to directly access public methods available in either the Binder implementation or the Service.
This is the preferred technique when your service is merely a background worker for your own application. The only use case when this is not the preferred way to create your interface is if your service is used by other applications or across separate processes.
- Use a Messenger
If you need your interface to work across different processes, you can create an interface for the service with a Messenger. In this manner, the service defines a Handler that responds to different types of Message objects.
This Handler is the basis for a Messenger that can then share an IBinder with the client, letting the client send commands to the service using Message objects. Additionally, the client can define a Messenger of its own, so the service can send messages back.
This is the simplest way to perform interprocess communication (IPC), because the Messenger queues all requests into a single thread so that you don’t have to design your service to be thread-safe.
- Use AIDL
Android Interface Definition Language (AIDL) decomposes objects into primitives that the operating system can understand and marshalls them across processes to perform IPC. The previous technique, using a Messenger, is actually based on AIDL as its underlying structure.
As mentioned in the preceding section, the Messenger creates a queue of all the client requests in a single thread, so the service receives requests one at a time. If, however, you want your service to handle multiple requests simultaneously, then you can use AIDL directly. In this case, your service must be thread-safe and capable of multithreading.
To use AIDL directly, create an .aidl file that defines the programming interface. The Android SDK tools use this file to generate an abstract class that implements the interface and handles IPC, which you can then extend within your service.