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.

8d9b8aa31d299a7bc2211f4a4a517215?s=128

Matt Logan

July 13, 2017
Tweet

Transcript

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

  2. Activities class HomeActivity : AppCompatActivity() {
 
 override fun onCreate(savedInstanceState:

    Bundle?) {
 super.onCreate(savedInstanceState)
 setContentView(R.layout.activity_home)
 }
 }
  3. Activities class HomeActivity : AppCompatActivity() {
 
 override fun onCreate(savedInstanceState:

    Bundle?) {
 super.onCreate(savedInstanceState)
 setContentView(R.layout.activity_home)
 }
 } Entry point
  4. Activities class HomeActivity : AppCompatActivity() {
 
 override fun onCreate(savedInstanceState:

    Bundle?) {
 super.onCreate(savedInstanceState)
 setContentView(R.layout.activity_home)
 }
 } UI
  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)
 }
 }

  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
  7. Activities • Entry point to your app • UI building

    block • Navigation
  8. How do activities handle navigation?

  9. Intents @OnClick(R.id.view_trip_button)
 fun onViewTripButtonClicked() {
 val tripActivity = Intent(this, TripActivity!::class.java)


    startActivity(tripActivity)
 }
  10. Intents @OnClick(R.id.view_trip_button)
 fun onViewTripButtonClicked() {
 val tripActivity = Intent(this, TripActivity!::class.java)


    startActivity(tripActivity)
 }
  11. Intents Intent(Context packageContext, Class<?> cls) Package context (where to look)

    Class of activity to start
  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)
 }
 }
  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)
 }
 }
  14. Intents Intent(String action) Action to fulfill, like capturing an image

  15. Intents • Navigation within your app • Navigation between apps

    • Can also pass data (we didn’t cover this)
  16. What if we want to navigate backward?

  17. Tasks Home Trip details Camera

  18. Tasks Home Trip details Camera

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

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

    app APK Camera app APK
  21. Tasks • Collection of activities • Associated with a “back

    stack” of UI history • Can span multiple processes & APKs • Customized with intent flags & manifest attributes
  22. Launch modes @OnClick(R.id.open_camera_button)
 fun onOpenCameraButtonClicked() {
 val cameraIntent = Intent(MediaStore.ACTION_IMAGE_CAPTURE)

    
 startActivity(cameraIntent)
 }
  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)
 }
  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
  25. Launch modes Home Trip details Camera Task A Task B

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

  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:
  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
  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
  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
  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
  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)
 }
  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!
  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
  35. Launch modes Home Trip details onNewIntent() FLAG_ACTIVITY_SINGLE_TOP

  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)
 }
  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
  38. Launch modes Home Trip details FLAG_ACTIVITY_CLEAR_TOP | FLAG_ACTIVITY_NEW_TASK Weather home

    Weather details Weather forecast
  39. Launch modes Home Trip details Weather home Weather details onNewIntent()

    FLAG_ACTIVITY_CLEAR_TOP | FLAG_ACTIVITY_NEW_TASK
  40. Launch modes <activity
 android:name=".TripActivity"
 android:launchMode="standard"
 !/>

  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
  42. Task affinity <activity
 android:name=".TripActivity"
 android:taskAffinity="tripTask"
 !/>

  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.”
  44. Task affinity af·fin·i·ty noun an indication of which task an

    activity prefers to belong to.
  45. Task affinity • Useful with FLAG_ACTIVITY_NEW_TASK • Activity gets launched

    into task of activities with the same affinity
  46. Task affinity Home Trip details taskAffinity="tripTask" Weather details taskAffinity="tripTask" Weather

    home
  47. Task affinity • Relevant when allowTaskReparenting="true" • Activity can be

    reassigned tasks as needed
  48. Task affinity Home Trip details Weather home Weather details allowTaskReparenting="true"

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

  50. Task affinity • One more use case • Two “apps”

    in one APK • Use affinities to separate activities into tasks
  51. Task affinity <activity
 android:name=".HomeActivity"!/> 
 <activity
 android:name=".TripDetailsActivity"
 android:taskAffinity="tripTask"!/> <activity
 android:name=".ExendedTripDetailsActivity"


    android:taskAffinity="tripTask"!/>
  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
  53. Clearing the back stack <activity
 android:name=".TripActivity"
 android:alwaysRetainTaskState="true"
 !/> Prevents clearing

    of activities from task
  54. Clearing the back stack <activity
 android:name=".TripActivity"
 android:clearTaskOnLaunch="true"
 !/> Removes all

    activities except root when user returns to task
  55. Clearing the back stack <activity
 android:name=".TripActivity"
 android:finishOnTaskLaunch="true"
 !/> Same as

    clearTaskOnLaunch but only operates on a single activity
  56. Task helpers • NavUtils has some navigation helper methods •

    navigateUpTo(Activity, Intent) • navigateUpFromSameTask(Activity) • getParentActivityIntent(Activity)
  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
  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”
  59. How does all of this work?

  60. How’s it work? • Intents are handled by ActivityManagerService •

    One of several system services • Orchestrates activities, including tasks and their respective back stacks
  61. How’s it work? val activityManager =
 getSystemService(ACTIVITY_SERVICE) as ActivityManager What

    about this activity manager?
  62. How’s it work? • ActivityManagerService is a system service •

    ActivityManager is for app developers
  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
  64. Why does Android work this way?

  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
  66. What if we want to handle navigation without involving the

    Android OS?
  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
  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!
  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
  70. Thanks! @_mattlogan Twitter mattlogan.me Blog & links github.com/mattlogan Open source