Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Activities, Intents, Tasks, and the Back Stack (360|AnDev, July 2017)

Activities, Intents, Tasks, and the Back Stack (360|AnDev, July 2017)

The Android operating system is unique in the way that it manages the series of screens with which a user can interact. Generally, we know these “screens” as activities, though we can make exceptions for smaller components like fragments and views. As application developers, we create activities and move between them with intents. However, the way that Android uses intents to create, destroy, and maintain a “stack” of Activities is mostly hidden from us — for good reason! In this talk, we’ll work towards a solid understanding of activities, intents, tasks, and the back stack. We may also take a quick look into the mechanisms by which the Android operating system orchestrates all of these things internally.

Matt Logan

July 13, 2017
Tweet

More Decks by Matt Logan

Other Decks in Programming

Transcript

  1. Activities, Intents, Tasks,
    and the Back Stack
    Matt Logan

    View Slide

  2. Activities
    class HomeActivity : AppCompatActivity() {


    override fun onCreate(savedInstanceState: Bundle?) {

    super.onCreate(savedInstanceState)

    setContentView(R.layout.activity_home)

    }

    }

    View Slide

  3. Activities
    class HomeActivity : AppCompatActivity() {


    override fun onCreate(savedInstanceState: Bundle?) {

    super.onCreate(savedInstanceState)

    setContentView(R.layout.activity_home)

    }

    }
    Entry point

    View Slide

  4. Activities
    class HomeActivity : AppCompatActivity() {


    override fun onCreate(savedInstanceState: Bundle?) {

    super.onCreate(savedInstanceState)

    setContentView(R.layout.activity_home)

    }

    }
    UI

    View Slide

  5. Activities
    class HomeActivity : AppCompatActivity() {


    override fun onCreate(savedInstanceState: Bundle?) {

    super.onCreate(savedInstanceState)

    setContentView(R.layout.activity_main)

    }


    @OnClick(R.id.view_trip_button)
    fun onViewTripButtonClicked() {

    val tripActivity =
    Intent(this, TripActivity!::class.java)

    startActivity(tripActivity)

    }

    }


    View Slide

  6. Activities
    class HomeActivity : AppCompatActivity() {


    override fun onCreate(savedInstanceState: Bundle?) {

    super.onCreate(savedInstanceState)

    setContentView(R.layout.activity_main)

    }


    @OnClick(R.id.view_trip_button)
    fun onViewTripButtonClicked() {

    val tripActivity =
    Intent(this, TripActivity!::class.java)

    startActivity(tripActivity)

    }

    }

    Navigation

    View Slide

  7. Activities
    • Entry point to your app
    • UI building block
    • Navigation

    View Slide

  8. How do activities handle navigation?

    View Slide

  9. Intents
    @OnClick(R.id.view_trip_button)

    fun onViewTripButtonClicked() {

    val tripActivity =
    Intent(this, TripActivity!::class.java)

    startActivity(tripActivity)

    }

    View Slide

  10. Intents
    @OnClick(R.id.view_trip_button)

    fun onViewTripButtonClicked() {

    val tripActivity =
    Intent(this, TripActivity!::class.java)

    startActivity(tripActivity)

    }

    View Slide

  11. Intents
    Intent(Context packageContext, Class> cls)
    Package context
    (where to look)
    Class of activity to start

    View Slide

  12. Intents
    class TripActivity : AppCompatActivity() {


    override fun onCreate(savedInstanceState: Bundle?) {

    super.onCreate(savedInstanceState)

    setContentView(R.layout.activity_trip)

    }


    @OnClick(R.id.open_camera_button)

    fun onOpenCameraButtonClicked() {

    val cameraIntent =
    Intent(MediaStore.ACTION_IMAGE_CAPTURE)

    startActivity(cameraIntent)

    }

    }

    View Slide

  13. Intents
    class TripActivity : AppCompatActivity() {


    override fun onCreate(savedInstanceState: Bundle?) {

    super.onCreate(savedInstanceState)

    setContentView(R.layout.activity_trip)

    }


    @OnClick(R.id.open_camera_button)

    fun onOpenCameraButtonClicked() {

    val cameraIntent =
    Intent(MediaStore.ACTION_IMAGE_CAPTURE)

    startActivity(cameraIntent)

    }

    }

    View Slide

  14. Intents
    Intent(String action)
    Action to fulfill, like capturing an image

    View Slide

  15. Intents
    • Navigation within your app
    • Navigation between apps
    • Can also pass data (we didn’t cover this)

    View Slide

  16. What if we want to navigate backward?

    View Slide

  17. Tasks
    Home Trip details Camera

    View Slide

  18. Tasks
    Home
    Trip details
    Camera

    View Slide

  19. Tasks
    HomeActivity
    TripActivity
    WeatherService
    TripProvider
    CameraActivity
    UploadService
    DownloadService
    GalleryActivity
    Travel app APK Camera app APK

    View Slide

  20. Tasks
    HomeActivity
    TripActivity
    WeatherService
    TripProvider
    CameraActivity
    UploadService
    DownloadService
    GalleryActivity
    Travel app APK Camera app APK

    View Slide

  21. Tasks
    • Collection of activities
    • Associated with a “back stack” of UI history
    • Can span multiple processes & APKs
    • Customized with intent flags & manifest attributes

    View Slide

  22. Launch modes
    @OnClick(R.id.open_camera_button)

    fun onOpenCameraButtonClicked() {

    val cameraIntent =
    Intent(MediaStore.ACTION_IMAGE_CAPTURE)

    startActivity(cameraIntent)

    }

    View Slide

  23. Launch modes
    @OnClick(R.id.open_camera_button)

    fun onOpenCameraButtonClicked() {

    val cameraIntent =
    Intent(MediaStore.ACTION_IMAGE_CAPTURE)

    cameraIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)

    startActivity(cameraIntent)

    }

    View Slide

  24. Launch modes
    • FLAG_ACTIVITY_NEW_TASK
    • Starts activity in a new task
    • If activity is already running in a task, will
    reuse existing task
    • Unless paired with
    FLAG_ACTIVITY_MULTIPLE_TASK

    View Slide

  25. Launch modes
    Home Trip details
    Camera
    Task A
    Task B

    View Slide

  26. Launch modes
    Home
    Trip Details
    Camera
    Task A Task B

    View Slide

  27. Launch modes
    intent.addFlags(

    FLAG_ACTIVITY_NEW_TASK or FLAG_ACTIVITY_MULTIPLE_TASK)
    Kotlin:
    intent.addFlags(

    FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_MULTIPLE_TASK);
    Java:

    View Slide

  28. Launch modes
    intent.addFlags(

    FLAG_ACTIVITY_NEW_TASK or FLAG_ACTIVITY_MULTIPLE_TASK)
    Kotlin:
    intent.addFlags(

    FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_MULTIPLE_TASK);
    Java: Bitwise “or” operator

    View Slide

  29. Launch modes
    public static final int FLAG_ACTIVITY_NEW_TASK = 0x10000000;
    public static final int FLAG_ACTIVITY_MULTIPLE_TASK = 0x08000000;
    val flags = FLAG_ACTIVITY_NEW_TASK or FLAG_ACTIVITY_MULTIPLE_TASK

    View Slide

  30. Launch modes
    = 0000 1000 0000 0000 0000 0000 0000 0000
    = 0001 0000 0000 0000 0000 0000 0000 0000
    public static final int FLAG_ACTIVITY_NEW_TASK = 0x10000000;
    public static final int FLAG_ACTIVITY_MULTIPLE_TASK = 0x08000000;
    val flags = FLAG_ACTIVITY_NEW_TASK or FLAG_ACTIVITY_MULTIPLE_TASK

    View Slide

  31. Launch modes
    public static final int FLAG_ACTIVITY_NEW_TASK = 0x10000000;
    = 0000 1000 0000 0000 0000 0000 0000 0000
    = 0001 0000 0000 0000 0000 0000 0000 0000
    public static final int FLAG_ACTIVITY_MULTIPLE_TASK = 0x08000000;
    val flags = FLAG_ACTIVITY_NEW_TASK or FLAG_ACTIVITY_MULTIPLE_TASK
    = 0001 1000 0000 0000 0000 0000 0000 0000
    = 402,653,184
    Result
    Result

    View Slide

  32. Launch modes
    @OnClick(R.id.open_camera_button)

    fun onOpenCameraButtonClicked() {

    val cameraIntent =
    Intent(MediaStore.ACTION_IMAGE_CAPTURE)

    cameraIntent.addFlags(FLAG_ACTIVITY_NEW_TASK or
    FLAG_ACTIVITY_MULTIPLE_TASK)

    startActivity(cameraIntent)

    }

    View Slide

  33. Launch modes
    @OnClick(R.id.open_camera_button)

    fun onOpenCameraButtonClicked() {

    val cameraIntent =
    Intent(MediaStore.ACTION_IMAGE_CAPTURE)

    cameraIntent.addFlags(402_653_184)

    startActivity(cameraIntent)

    }
    Don't do this!

    View Slide

  34. Launch modes
    • FLAG_ACTIVITY_SINGLE_TOP
    • Activity won’t be launched if already at top
    • Intent will be delivered to existing activity
    via onNewIntent(Intent) callback

    View Slide

  35. Launch modes
    Home Trip details
    onNewIntent()
    FLAG_ACTIVITY_SINGLE_TOP

    View Slide

  36. Launch modes
    override fun onNewIntent(intent: Intent) {

    super.onNewIntent(intent)


    !// Get data from new intent 

    val newTripName =
    intent.extras.getString(EXTRA_TRIP_NAME)


    !// Clear existing UI and repopulate with new data

    refreshUi(newTripName)

    }

    View Slide

  37. Launch modes
    • FLAG_ACTIVITY_CLEAR_TOP
    • Destroys everything on top if it already exists
    • Can be used with FLAG_ACTIVITY_NEW_TASK
    • Locates activity in any task
    • Destroys all activities on top
    • Activity is brought to foreground

    View Slide

  38. Launch modes
    Home Trip details
    FLAG_ACTIVITY_CLEAR_TOP | FLAG_ACTIVITY_NEW_TASK
    Weather home Weather details Weather forecast

    View Slide

  39. Launch modes
    Home Trip details
    Weather home Weather details
    onNewIntent()
    FLAG_ACTIVITY_CLEAR_TOP | FLAG_ACTIVITY_NEW_TASK

    View Slide

  40. Launch modes
    android:name=".TripActivity"

    android:launchMode="standard"

    !/>

    View Slide

  41. Launch modes
    • "standard" — new instance in current task
    • "singleTop" — won’t be launched if already at top
    • "singleTask" — creates new task for activity
    • "singleInstance" — no other activities can be
    launched into same task
    • Can be overridden with intent flags

    View Slide

  42. Task affinity
    android:name=".TripActivity"

    android:taskAffinity="tripTask"

    !/>

    View Slide

  43. Task affinity
    af·fin·i·ty
    noun
    a spontaneous or natural liking or sympathy for
    someone or something.
    “Matt has an affinity for pizza.”

    View Slide

  44. Task affinity
    af·fin·i·ty
    noun
    an indication of which task an activity prefers to
    belong to.

    View Slide

  45. Task affinity
    • Useful with FLAG_ACTIVITY_NEW_TASK
    • Activity gets launched into task of activities
    with the same affinity

    View Slide

  46. Task affinity
    Home
    Trip details
    taskAffinity="tripTask"
    Weather details
    taskAffinity="tripTask"
    Weather home

    View Slide

  47. Task affinity
    • Relevant when
    allowTaskReparenting="true"
    • Activity can be reassigned tasks as needed

    View Slide

  48. Task affinity
    Home Trip details
    Weather home
    Weather details
    allowTaskReparenting="true"

    View Slide

  49. Task affinity
    Home Trip details
    Weather home Weather details
    allowTaskReparenting="true"

    View Slide

  50. Task affinity
    • One more use case
    • Two “apps” in one APK
    • Use affinities to separate activities into tasks

    View Slide

  51. Task affinity
    android:name=".HomeActivity"!/>

    android:name=".TripDetailsActivity"

    android:taskAffinity="tripTask"!/>
    android:name=".ExendedTripDetailsActivity"

    android:taskAffinity="tripTask"!/>

    View Slide

  52. Clearing the back stack
    • Default behavior:
    • After some amount of time, system clears all
    activities in task except the root
    • User has likely abandoned that task
    • Modified with more manifest attributes

    View Slide

  53. Clearing the back stack
    android:name=".TripActivity"

    android:alwaysRetainTaskState="true"

    !/>
    Prevents clearing of activities from task

    View Slide

  54. Clearing the back stack
    android:name=".TripActivity"

    android:clearTaskOnLaunch="true"

    !/>
    Removes all activities except root when
    user returns to task

    View Slide

  55. Clearing the back stack
    android:name=".TripActivity"

    android:finishOnTaskLaunch="true"

    !/>
    Same as clearTaskOnLaunch but only
    operates on a single activity

    View Slide

  56. Task helpers
    • NavUtils has some navigation helper methods
    • navigateUpTo(Activity, Intent)
    • navigateUpFromSameTask(Activity)
    • getParentActivityIntent(Activity)

    View Slide

  57. Task helpers
    • TaskStackBuilder lets you create a back
    stack manually
    • Useful when starting activity in a new task
    with no UI history
    • Lets build the typical UI history that would
    lead the user to that point

    View Slide

  58. Recents
    • Back button should never traverse tasks
    • User can switch tasks with “recents” system UI
    • Basically a view of all tasks
    • Special behavior added in Lollipop for
    “documents”

    View Slide

  59. How does all of this work?

    View Slide

  60. How’s it work?
    • Intents are handled by
    ActivityManagerService
    • One of several system services
    • Orchestrates activities, including tasks and
    their respective back stacks

    View Slide

  61. How’s it work?
    val activityManager =

    getSystemService(ACTIVITY_SERVICE) as ActivityManager
    What about this activity manager?

    View Slide

  62. How’s it work?
    • ActivityManagerService is a system service
    • ActivityManager is for app developers

    View Slide

  63. How’s it work?
    • ActivityManager
    • Some methods are broadly useful:
    • isLowRamDevice()
    • clearApplicationUserData()
    • Apps can interact with its task stack via:
    • ActivityManager.AppStack
    • ActivityManager.RecentTaskInfo

    View Slide

  64. Why does Android work this way?

    View Slide

  65. Design considerations
    • Android OS operates on modular components
    • Enforces separation of concerns at
    application level and at device level
    • Navigation is handled by the OS, not your app
    • Allows for navigation model involving
    activities in separate APKs

    View Slide

  66. What if we want to handle navigation
    without involving the Android OS?

    View Slide

  67. In-app navigation
    • Official, supported solution: fragments
    • FragmentManager & FragmentTransaction take
    the place of ActivityManagerService & intents
    • Lifecycle methods very similar to Activity
    • Fragment back stack similar to task back stack
    • Built in state restoration
    • Support for animated transitions

    View Slide

  68. In-app navigation
    • Non-fragment solutions
    • Can avoid some complexity of fragments
    • Manually add & remove views to & from a
    root view group
    • You have to maintain a navigation stack
    yourself!

    View Slide

  69. Resources
    Android “Activities — Tasks and Back Stack” developer guide
    https://developer.android.com/guide/components/activities/tasks-and-back-stack.html
    Dan Morrill’s 2008 Google I/O talk: Inside the Android Application Framework
    https://www.youtube.com/watch?v=TkPiXRNee7A
    Anatomy of Android: Activity Manager
    https://anatomyofandroid.com/2013/10/16/activity-manager/
    The Cheese Factory: Understand Android Activity’s launchMode
    https://inthecheesefactory.com/blog/understand-android-activity-launchmode/en
    Android Programming: The Big Nerd Ranch Guide
    https://www.bignerdranch.com/books/android-programming/
    Square Engineering Blog: Advocating Against Android Fragments
    https://medium.com/square-corner-blog/advocating-against-android-fragments-81fd0b462c97

    View Slide

  70. Thanks!
    @_mattlogan
    Twitter
    mattlogan.me
    Blog & links
    github.com/mattlogan
    Open source

    View Slide